OCI provisioning service

  • Done
  • quality assurance status badge
Details
4 participants
  • paul
  • Ludovic Courtès
  • Maxim Cournoyer
  • Maxim Cournoyer
Owner
unassigned
Submitted by
paul
Severity
normal

Debbugs page

P
(address . guix-patches@gnu.org)
2f43e635-508c-407a-8309-06e75d492d89@autistici.org
Hi Guix,

for a while now, we have been able to run Docker/OCI images as Shepherd
services with the oci-container-service-type. This was useful especially
to run software that is not packaged yet (or sometimes not packageable
at all, due to JS and other bootstrapping issues). It allows to declare
a list of oci-container-configuration records which would map to Docker
backed Shepherd services:

(service oci-container-service-type
(list
(oci-container-configuration
(image "prom/prometheus")
(network "host")
(ports
'(("9000" . "9000")
("9090" . "9090"))))
(oci-container-configuration
(image "grafana/grafana:10.0.1")
(network "host")
(volumes
'("/var/lib/grafana:/var/lib/grafana")))))

This allows us to have containerized but apparently native services
running on the Guix System. The downside is that unless some isolation
mechanism are disabled, or unless the running services are very simple
and don't have to interact with the world outside them, we lose some of
the nice virtualization features of containers. Above all is the need to
have all containers connected to the host network to be able to interact
with other containers. This is effectively the default behavior of most
if not all Guix native services but with the downside that usually OCI
images lack all the provenance information that is typical of Guix
packages, hence are less trustable than a native Guix package so in some
cases users do prefer to have them running in an isolated environment.

Another shortcoming of the oci-container-service-type is that it only
supports Docker as an OCI runtime which must have a running daemon with
full root privileges to be able to execute containers. Since some weeks
now, with the help of subids/subgids and unprivileged namespaces, we are
able to run completely rootless containers with the
rootless-podman-service-type.

This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated.  The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes:

(service iptables-service-type)
(service rootless-podman-service-type)
(service oci-service-type
(oci-configuration
(runtime 'podman)))
(simple-service 'oci-provisioning
oci-service-type
(oci-extension
(volumes
(list
(oci-network-configuration (name "grafana"))))
(networks
(list
(oci-network-configuration (name "monitoring"))))
(containers
(list
(oci-container-configuration
(image "docker.io/grafana/grafana:10.1.5")
(network "host")
(volumes
`(,( . "/opt/bitnami/grafana/conf/grafana.ini")
("grafana" . "/var/lib/grafana"))))))))

Please mind that this is only an example and it probably won't work on
your system without some more thought. The oci-service-type currently
only handles OCI objects creation, the user is supposed to handle state
once the objects are provsioned. It currently supports two different OCI
runtimes: Docker and rootless Podman.  Both runtimes are tested to make
sure provisioned containers can connect to each other through
provisioned networks and can read/write data with provisioned volumes.
Compared to the oci-container-service-type I added some utility Shepherd
actions that are supposed to help with debugging. The above
configuration would yield the following

$ sudo herd command-line podman-networks
/run/current-system/profile/bin/podman network create monitoring

$ sudo herd command-line podman-volumes
/run/current-system/profile/bin/podman volume create grafana

$ sudo herd doc podman-grafana list-actions

command-line:
Prints podman-grafana's OCI runtime command line invokation.

pull:
Pull podman-grafana's image (docker.io/grafana/grafana:10.1.5).

$ sudo herd command-line podman-grafana
/run/current-system/profile/bin/podman run --rm --name podman-grafana --network host -v grafana:/var/lib/grafana -v /gnu/store/yqfvhvf8j4008ykr52zh2dmc1d2mjxih-grafana.ini:/opt/bitnami/grafana/conf/grafana.ini docker.io/bitnami/grafana:10.1.5


At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future (you can find an untested version of
that at [0]). I tested my changes with:

guix shell -D guix -CPW -- make check-system TESTS="oci-service-docker oci-container oci-service-rootless-podman docker docker-system rootless-podman"


Please let me know your thoughts about this!

Thank you for all your work,

giacomo


[0]:
Attachment: file
G
G
Giacomo Leidi wrote on 5 Feb 14:02 -0800
[PATCH 1/4] services: rootless-podman: Use login shell.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
89778533f32ad1388f03414e884fff10f74ef379.1738792951.git.goodoldpaul@autistici.org
This commit allows for having PATH set when changing the owner of
/sys/fs/group.

* gnu/services/containers.scm (crgroups-fs-owner): Use login shell.

Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
---
gnu/services/containers.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Toggle diff (17 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 19d35ccbcb6..dc66ac4f967 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
(rootless-podman-configuration-group-name config))
(program-file "cgroups2-fs-owner-entrypoint"
#~(system*
- (string-append #+bash-minimal "/bin/bash") "-c"
+ (string-append #+bash-minimal "/bin/bash") "-l" "-c"
(string-append "echo Setting /sys/fs/cgroup "
"group ownership to " #$group " && chown -v "
"root:" #$group " /sys/fs/cgroup && "

base-commit: 5a897c5c95a81278b044c18d962d3bd83131ba06
--
2.48.1
G
G
Giacomo Leidi wrote on 5 Feb 14:02 -0800
[PATCH 4/4] tests: Use oci-image in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
d6b02a87a3827bec004de6a253c6f160a9f33578.1738792951.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/services/containers.scm: Export lower-oci-image.
* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/services/containers.scm | 2 +
gnu/tests/containers.scm | 80 ++++++++++++++----------------
gnu/tests/docker.scm | 98 ++++++++++++++++++++-----------------
3 files changed, 93 insertions(+), 87 deletions(-)

Toggle diff (272 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index c45f79c4ed1..e15dbc6a21c 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -74,6 +74,8 @@ (define-module (gnu services containers)
oci-image-system
oci-image-grafts?
+ lower-oci-image
+
oci-container-configuration
oci-container-configuration?
oci-container-configuration-fields
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 719647c298e..024000bca14 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -69,13 +69,47 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image
+ "guile-guest"
+ (oci-image
+ (repository "guile-guest")
+ (value
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain)))))
+ (pack-options
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))))))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %guile-oci-image
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..d969b28a68f 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -57,6 +58,40 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image
+ "guile-guest"
+ (oci-image
+ (repository "guile-guest")
+ (value
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain)))))
+ (pack-options
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))))))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +208,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +217,20 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image
+ "guix-system-guest"
+ (oci-image
+ (repository "guix-system-guest")
+ (value
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))))))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +347,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.48.1
G
G
Giacomo Leidi wrote on 5 Feb 14:02 -0800
[PATCH 3/4] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
c3c8730fa100fd8b577c4aa9ed642254421920da.1738792951.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/services/containers.scm (%oci-supported-runtimes): New variable;
(oci-runtime-cli): new variable;
(oci-runtime-name): new variable;
(oci-network-configuration): new variable;
(oci-volume-configuration): new variable;
(oci-configuration): new variable;
(oci-extension): new variable;
(oci-networks-shepherd-name): new variable;
(oci-service-type): new variable;
(oci-state->shepherd-services): new variable.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 295 +++++++---
gnu/services/containers.scm | 1038 +++++++++++++++++++++++++++++++----
gnu/services/docker.scm | 37 +-
gnu/tests/containers.scm | 956 +++++++++++++++++++++++++++++++-
4 files changed, 2130 insertions(+), 196 deletions(-)

Toggle diff (460 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index bb5f29277fb..ff3e77a1d00 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -41778,59 +41778,155 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@command{docker run} or @command{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources. When unset it will default to @code{docker-cli}
+package for the @code{'docker} runtime or to @code{podman} package for the
+@code{'podman} runtime.
+
+@item @code{user} (default: @code{"oci-container"}) (type: string)
+The user name under whose authority OCI commands will be run.
+
+@item @code{group} (default: @code{"docker"}) (type: string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -41850,16 +41946,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @command{docker run}
+or @command{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -41867,22 +41963,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -41910,7 +42008,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -41921,10 +42019,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -41935,25 +42034,95 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} or @command{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly
+passed to the runtime invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+passed to the runtime invokation.
@end table
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 50bf6c05549..c45f79c4ed1 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -41,6 +41,7 @@ (define-module (gnu services containers)
#:use-module ((guix scripts pack) #:prefix pack:)
#:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
@@ -96,8 +97,68 @@ (define-module (gnu services containers)
oci-container-configuration-workdir
oci-container-configuration-extra-arguments
+ list-of-oci-containers?
+ list-of-oci-networks?
+ list-of-oci-volumes?
+
+ %oci-supported-runtimes
+ oci-sanitize-runtime
+ oci-runtime-system-requirement
+ oci-runtime-cli
+ oci-runtime-system-cli
+ oci-runtime-name
+ oci-runtime-group
+
+ oci-network-configuration
+ oci-network-configuration?
+ oci-network-configuration-fields
+ oci-network-configuration-name
+ oci-network-configuration-driver
+ oci-network-configuration-gateway
+ oci-network-configuration-internal?
+ oci-network-configuration-ip-range
+ oci-network-configuration-ipam-driver
+ oci-network-configuration-ipv6?
+ oci-network-configuration-subnet
+ oci-network-configuration-labels
+ oci-network-configuration-extra-arguments
+
+ oci-volume-configuration
+ oci-volume-configuration?
+ oci-volume-configuration-fields
+ oci-volume-configuration-name
+ oci-volume-configuration-labels
+ oci-volume-configuration-extra-arguments
+
+ oci-configuration
+ oci-configuration?
+ oci-configuration-fields
+ oci-configuration-runtime
+ oci-configuration-runtime-cli
+ oci-configuration-user
+ oci-configuration-group
+ oci-configuration-containers
+ oci-configuration-networks
+ oci-configuration-volumes
+ oci-configuration-verbose?
+
+ oci-extension
+ oci-extension?
+ oci-extension-fields
+ oci-extension-containers
+ oci-extension-networks
+ oci-extension-volumes
+
+ oci-networks-shepherd-name
+ oci-volumes-shepherd-name
+
oci-container-shepherd-service
- %oci-container-accounts))
+ oci-service-type
+ oci-service-accounts
+ oci-service-profile
+ oci-service-subids
+ oci-state->shepherd-services
+ oci-configuration->shepherd-servi
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 5 Feb 14:02 -0800
[PATCH 2/4] services: oci-container-configuration: Move to (gnu services containers).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
92f19ba6c0842885735dfce653eef535360085f4.1738792951.git.goodoldpaul@autistici.org
This patch moves the oci-container-configuration and related
configuration records to (gnu services containers).
Public symbols are still exported for backwards
compatibility but since the oci-container-service-type will be
deprecated in favor of the more general oci-service-type, everything is
moved outside of the docker related module.

* gnu/services/docker.scm: Move everything related to oci-container-configuration
to...
* gnu/services/containers.scm: ...here.scm.
* gnu/tests/docker.scm: Simplify %test-oci-container test case.

Change-Id: Iae599dd5cc7442eb632f0c1b3b12f6b928397ae7
---
gnu/services/containers.scm | 549 +++++++++++++++++++++++++++++++++-
gnu/services/docker.scm | 577 +++---------------------------------
gnu/tests/docker.scm | 99 +++----
3 files changed, 625 insertions(+), 600 deletions(-)

Toggle diff (534 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index dc66ac4f967..50bf6c05549 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,19 +17,31 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services containers)
+ #:use-module (gnu image)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages containers)
+ #:use-module (gnu packages docker)
#:use-module (gnu packages file-systems)
#:use-module (gnu services)
#:use-module (gnu services base)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu system)
#:use-module (gnu system accounts)
+ #:use-module (gnu system image)
#:use-module (gnu system shadow)
#:use-module (gnu system pam)
+ #:use-module (guix diagnostics)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
+ #:use-module (guix monads)
#:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
rootless-podman-configuration-fields
@@ -48,7 +60,44 @@ (define-module (gnu services containers)
rootless-podman-shepherd-services
rootless-podman-service-etc
- rootless-podman-service-type))
+ rootless-podman-service-type
+
+ oci-image
+ oci-image?
+ oci-image-fields
+ oci-image-repository
+ oci-image-tag
+ oci-image-value
+ oci-image-pack-options
+ oci-image-target
+ oci-image-system
+ oci-image-grafts?
+
+ oci-container-configuration
+ oci-container-configuration?
+ oci-container-configuration-fields
+ oci-container-configuration-user
+ oci-container-configuration-group
+ oci-container-configuration-command
+ oci-container-configuration-entrypoint
+ oci-container-configuration-host-environment
+ oci-container-configuration-environment
+ oci-container-configuration-image
+ oci-container-configuration-provision
+ oci-container-configuration-requirement
+ oci-container-configuration-log-file
+ oci-container-configuration-auto-start?
+ oci-container-configuration-respawn?
+ oci-container-configuration-shepherd-actions
+ oci-container-configuration-network
+ oci-container-configuration-ports
+ oci-container-configuration-volumes
+ oci-container-configuration-container-user
+ oci-container-configuration-workdir
+ oci-container-configuration-extra-arguments
+
+ oci-container-shepherd-service
+ %oci-container-accounts))
(define (gexp-or-string? value)
(or (gexp? value)
@@ -188,7 +237,7 @@ (define (rootless-podman-cgroups-limits-service config)
rootless-podman-shared-root-fs))
(one-shot? #t)
(documentation
- "Allow setting cgroups limits: cpu, cpuset, memory and
+ "Allow setting cgroups limits: cpu, cpuset, io, memory and
pids.")
(start
#~(make-forkexec-constructor
@@ -242,3 +291,497 @@ (define rootless-podman-service-type
(default-value (rootless-podman-configuration))
(description
"This service configures rootless @code{podman} on the Guix System.")))
+
+
+;;;
+;;; OCI container.
+;;;
+
+(define (oci-sanitize-pair pair delimiter)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (match pair
+ (((? valid? key) . (? valid? value))
+ #~(string-append #$key #$delimiter #$value))
+ (_
+ (raise
+ (formatted-message
+ (G_ "pair members must contain only strings, gexps or file-like objects
+but ~a was found")
+ pair)))))
+
+(define (oci-sanitize-mixed-list name value delimiter)
+ (map
+ (lambda (el)
+ (cond ((string? el) el)
+ ((pair? el) (oci-sanitize-pair el delimiter))
+ (else
+ (raise
+ (formatted-message
+ (G_ "~a members must be either a string or a pair but ~a was
+found!")
+ name el)))))
+ value))
+
+(define (oci-sanitize-host-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "host-environment" value "="))
+
+(define (oci-sanitize-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "environment" value "="))
+
+(define (oci-sanitize-ports value)
+ ;; Expected spec format:
+ ;; '(("8088" . "80") "2022:22")
+ (oci-sanitize-mixed-list "ports" value ":"))
+
+(define (oci-sanitize-volumes value)
+ ;; Expected spec format:
+ ;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
+ (oci-sanitize-mixed-list "volumes" value ":"))
+
+(define (oci-sanitize-shepherd-actions value)
+ (map
+ (lambda (el)
+ (if (shepherd-action? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "shepherd-actions may only be shepherd-action records
+but ~a was found") el))))
+ value))
+
+(define (oci-sanitize-extra-arguments value)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (map
+ (lambda (el)
+ (if (valid? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "extra arguments may only be strings, gexps or file-like objects
+but ~a was found") el))))
+ value))
+
+(define (oci-image-reference image)
+ (if (string? image)
+ image
+ (string-append (oci-image-repository image)
+ ":" (oci-image-tag image))))
+
+(define (oci-lowerable-image? image)
+ (or (manifest? image)
+ (operating-system? image)
+ (gexp? image)
+ (file-like? image)))
+
+(define (string-or-oci-image? image)
+ (or (string? image)
+ (oci-image? image)))
+
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe/no-serialization string)
+
+(define-configuration/no-serialization oci-image
+ (repository
+ (string)
+ "A string like @code{myregistry.local:5000/testing/test-image} that names
+the OCI image.")
+ (tag
+ (string "latest")
+ "A string representing the OCI image tag. Defaults to @code{latest}.")
+ (value
+ (oci-lowerable-image)
+ "A @code{manifest} or @code{operating-system} record that will be lowered
+into an OCI compatible tarball. Otherwise this field's value can be a gexp
+or a file-like object that evaluates to an OCI compatible tarball.")
+ (pack-options
+ (list '())
+ "An optional set of keyword arguments that will be passed to the
+@code{docker-image} procedure from @code{guix scripts pack}. They can be used
+to replicate @command{guix pack} behavior:
+
+@lisp
+(oci-image
+ (repository \"guile\")
+ (tag \"3\")
+ (manifest (specifications->manifest '(\"guile\")))
+ (pack-options
+ '(#:symlinks ((\"/bin/guile\" -> \"bin/guile\"))
+ #:max-layers 2)))
+@end lisp
+
+If the @code{value} field is an @code{operating-system} record, this field's
+value will be ignored.")
+ (system
+ (maybe-string)
+ "Attempt to build for a given system, e.g. \"i686-linux\"")
+ (target
+ (maybe-string)
+ "Attempt to cross-build for a given triple, e.g. \"aarch64-linux-gnu\"")
+ (grafts?
+ (boolean #f)
+ "Whether to allow grafting or not in the pack build."))
+
+(define-configuration/no-serialization oci-container-configuration
+ (user
+ (string "oci-container")
+ "The user under whose authority docker commands will be run.")
+ (group
+ (string "docker")
+ "The group under whose authority docker commands will be run.")
+ (command
+ (list-of-strings '())
+ "Overwrite the default command (@code{CMD}) of the image.")
+ (entrypoint
+ (maybe-string)
+ "Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
+ (host-environment
+ (list '())
+ "Set environment variables in the host environment where @command{docker run}
+is invoked. This is especially useful to pass secrets from the host to the
+container without having them on the @command{docker run}'s command line: by
+setting the @code{MYSQL_PASSWORD} on the host and by passing
+@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
+possible to securely set values in the container environment. This field's
+value can be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to @code{make-forkexec-constructor}."
+ (sanitizer oci-sanitize-host-environment))
+ (environment
+ (list '())
+ "Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-environment))
+ (image
+ (string-or-oci-image)
+ "The image used to build the container. It can be a string or an
+@code{oci-image} record. Strings are resolved by the Docker
+Engine, and follow the usual format
+@code{myregistry.local:5000/testing/test-image:tag}.")
+ (provision
+ (maybe-string)
+ "Set the name of the provisioned Shepherd service.")
+ (requirement
+ (list-of-symbols '())
+ "Set additional Shepherd services dependencies to the provisioned Shepherd
+service.")
+ (log-file
+ (maybe-string)
+ "When @code{log-file} is set, it names the file to which the service’s
+standard output and standard error are redirected. @code{log-file} is created
+if it does not exist, otherwise it is appended to.")
+ (auto-start?
+ (boolean #t)
+ "Whether this service should be started automatically by the Shepherd. If it
+is @code{#f} the service has to be started manually with @command{herd start}.")
+ (respawn?
+ (boolean #f)
+ "Whether to restart the service when it stops, for instance when the
+underlying process dies.")
+ (shepherd-actions
+ (list '())
+ "This is a list of @code{shepherd-action} records defining actions supported
+by the service."
+ (sanitizer oci-sanitize-shepherd-actions))
+ (network
+ (maybe-string)
+ "Set a Docker network for the spawned container.")
+ (ports
+ (list '())
+ "Set the port or port ranges to expose from the spawned container. This can
+be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"8080\" . \"80\")
+ \"10443:443\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-ports))
+ (volumes
+ (list '())
+ "Set volume mappings for the spawned container. This can be a
+list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
+ \"/gnu/store:/gnu/store\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-volumes))
+ (container-user
+ (maybe-string)
+ "Set the current user inside the spawned container. You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#user,upstream}
+documentation for semantics.")
+ (workdir
+ (maybe-string)
+ "Set the current working for the spawned Shepherd service.
+You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
+documentation for semantics.")
+ (extra-arguments
+ (list '())
+ "A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} invokation."
+ (sanitizer oci-sanitize-extra-arguments)))
+
+(define oci-container-configuration->options
+ (lambda (config)
+ (let ((entrypoint
+ (oci-container-configuration-entrypoint config))
+ (network
+ (oci-container-configuration-network config))
+ (user
+ (oci-container-configuration-container-user config))
+ (workdir
+ (oci-container-configuration-workdir config)))
+ (apply append
+ (filter (compose not unspecified?)
+ `(,(if (maybe-value-set? entrypoint)
+ `("--entrypoint" ,entrypoint)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "--env" spec))
+ (oci-container-configuration-environment config))
+ ,(if (maybe-value-set? network)
+ `("--network" ,network)
+ '())
+ ,(if (maybe-value-set? user)
+ `("--user" ,user)
+ '())
+ ,(if (maybe-value-set? workdir)
+ `("--workdir" ,workdir)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "-p" spec))
+ (oci-container-configuration-ports config))
+ ,(append-map
+ (lambda (spec)
+ (list "-v" spec))
+ (oci-container-configuration-volumes config))))))))
+
+(define* (get-keyword-value args keyword #:key (default #f))
+ (let ((kv (memq keyword args)))
+ (if (and kv (>= (length kv) 2))
+ (cadr kv)
+ default)))
+
+(define (lower-operating-system os target system)
+ (mlet* %store-monad
+ ((tarball
+ (lower-object
+ (system-image (os->image os #:type docker-image-type))
+ system
+ #:target target)))
+ (return tarball)))
+
+(define (lower-manifest name image target system)
+ (define value (oci-image-value image))
+ (define options (oci-image-pack-options image))
+ (define image-reference
+ (oci-image-reference image))
+ (define image-tag
+ (let* ((extra-options
+ (get-keyword-value options #:extra-options))
+ (image-tag-option
+ (and extra-options
+ (get-keyword-value extra-options #:image-tag))))
+ (if image-tag-option
+ '()
+ `(#:extra-options (#:image-tag ,image-reference)))))
+
+ (mlet* %store-monad
+ ((_ (set-grafting
+ (oci-image-grafts? image)))
+ (guile (set-guile-for-build (default-guile)))
+ (profile
+ (profile-derivation value
+ #:target target
+ #:system system
+ #:hooks '()
+ #:locales? #f))
+ (tarball (apply pack:docker-image
+ `(,name ,profile
+ ,@options
+ ,@image-tag
+ #:localstatedir? #t))))
+ (return tarball)))
+
+(define (lower-oci-image name image)
+ (define value (oci-image-value image))
+ (define image-target (oci-image-target image))
+ (define image-system (oci-image-system image))
+ (define target
+ (if (maybe-value-set? image-target)
+ image-target
+ (%current-target-system)))
+ (define system
+ (if (maybe-value-set? image-system)
+ image-system
+ (%current-system)))
+ (with-store store
+ (run-with-store store
+ (match value
+ ((? manifest? value)
+ (lower-manifest name image target system))
+ ((? operating-system? value)
+ (lower-operating-system value target system))
+ ((or (? gexp? value)
+ (? file-like? value))
+ value)
+ (_
+ (raise
+ (formatted-message
+ (G_ "oci-image value must contain only manifest,
+operating-system, gexp or file-like records but ~a was found")
+ value))))
+ #:target target
+ #:system system)))
+
+(define (%oci-image-loader name image tag)
+ (let ((docker (file-append docker-cli "/bin/docker"))
+ (tarball (lower-oci-image name image)))
+ (with-imported-modules '((guix build utils))
+ (program-file (format #f "~a-image-loader" name)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+
+ (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
+ (define line
+ (read-line
+ (open-input-pipe
+ (string-append #$docker " load -i " #$tarball))))
+
+ (unless (or (eof-object? line)
+ (string-null? line))
+ (format #t "~a~%" line)
+ (let ((repository&tag
+ (string-drop line
+ (string-length
+ "Loaded image: "))))
+
+ (invoke #$docker "tag" repository&tag #$tag)
+ (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
+
+(define (oci-container-shepherd-service config)
+ (define (guess-name name image)
+ (if (maybe-value-set? name)
+ name
+ (string-append "docker-"
+ (basename
+ (if (string? image)
+ (first (string-split image #\:))
+ (oci-image-repository image))))))
+
+ (let* ((docker (file-append docker-cli "/bin/docker"))
+ (actions (oci-container-configuration-shepherd-actions config))
+ (auto-start?
+ (oci-container-configuration-auto-start? config))
+ (user (oci-container-configuration-user config))
+ (group (oci-container-configuration-group config))
+ (host-environment
+ (oci-container-configuration-host-environment config))
+ (command (oci-container-configuration-command config))
+ (log-file (oci-container-configuration-log-file config))
+ (provision (oci-container-configuration-provi
This message was truncated. Download the full message here.
P
Re: OCI provisioning service
(address . 76081@debbugs.gnu.org)
c685875a-0b74-4ada-9a04-c387b8db646e@autistici.org
Hi,

I'm about to send a v2. v2 compared to the first revision features:


- it actually compiles all the times :) (rev 1 referenced oci-image too
early for it to be working and generated a compile time error, if you
recompiled it sometimes went away so I thought it was a problem of my
setup. CI caught this)
- it allows more values to be overridden by eventual users of the Scheme API
- it allows passing extra arguments directly after each podman or docker
invokation, allowing for example for overriding podman --root and
similar options.

All of these tests should pass:

guix shell -D guix -CPW -- make check-system TESTS="oci-container
oci-service-rootless-podman docker docker-system rootless-podman
oci-service-docker"


Thank you for your work,

giacomo
G
G
Giacomo Leidi wrote on 9 Feb 11:15 -0800
[PATCH v2 1/4] services: rootless-podman: Use login shell.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
89778533f32ad1388f03414e884fff10f74ef379.1739128504.git.goodoldpaul@autistici.org
This commit allows for having PATH set when changing the owner of
/sys/fs/group.

* gnu/services/containers.scm (crgroups-fs-owner): Use login shell.

Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
---
gnu/services/containers.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Toggle diff (17 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 19d35ccbcb6..dc66ac4f967 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
(rootless-podman-configuration-group-name config))
(program-file "cgroups2-fs-owner-entrypoint"
#~(system*
- (string-append #+bash-minimal "/bin/bash") "-c"
+ (string-append #+bash-minimal "/bin/bash") "-l" "-c"
(string-append "echo Setting /sys/fs/cgroup "
"group ownership to " #$group " && chown -v "
"root:" #$group " /sys/fs/cgroup && "

base-commit: 5a897c5c95a81278b044c18d962d3bd83131ba06
--
2.48.1
G
G
Giacomo Leidi wrote on 9 Feb 11:15 -0800
[PATCH v2 3/4] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
c94fe584d2ba0071f64e456e708c1447ab094cd7.1739128504.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/services/containers.scm (%oci-supported-runtimes): New variable;
(oci-runtime-cli): new variable;
(oci-runtime-name): new variable;
(oci-network-configuration): new variable;
(oci-volume-configuration): new variable;
(oci-configuration): new variable;
(oci-extension): new variable;
(oci-networks-shepherd-name): new variable;
(oci-service-type): new variable;
(oci-state->shepherd-services): new variable.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 299 ++++++++--
gnu/services/containers.scm | 1086 +++++++++++++++++++++++++++++++----
gnu/services/docker.scm | 37 +-
gnu/tests/containers.scm | 999 +++++++++++++++++++++++++++++++-
4 files changed, 2225 insertions(+), 196 deletions(-)

Toggle diff (456 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index bb5f29277fb..8f66b35297b 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -41778,59 +41778,159 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@command{docker run} or @command{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources. When unset it will default to @code{docker-cli}
+package for the @code{'docker} runtime or to @code{podman} package for the
+@code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (default: @code{"oci-container"}) (type: string)
+The user name under whose authority OCI commands will be run.
+
+@item @code{group} (default: @code{"docker"}) (type: string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -41850,16 +41950,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @command{docker run}
+or @command{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -41867,22 +41967,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -41910,7 +42012,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -41921,10 +42023,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -41935,25 +42038,95 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} or @command{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly
+passed to the runtime invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+passed to the runtime invokation.
@end table
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 50bf6c05549..5c50c99eaf6 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -41,6 +41,7 @@ (define-module (gnu services containers)
#:use-module ((guix scripts pack) #:prefix pack:)
#:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
@@ -96,8 +97,72 @@ (define-module (gnu services containers)
oci-container-configuration-workdir
oci-container-configuration-extra-arguments
+ list-of-oci-containers?
+ list-of-oci-networks?
+ list-of-oci-volumes?
+
+ %oci-supported-runtimes
+ oci-sanitize-runtime
+ oci-runtime-system-environment
+ oci-runtime-system-extra-arguments
+ oci-runtime-system-group
+ oci-runtime-system-requirement
+ oci-runtime-cli
+ oci-runtime-system-cli
+ oci-runtime-name
+ oci-runtime-group
+
+ oci-network-configuration
+ oci-network-configuration?
+ oci-network-configuration-fields
+ oci-network-configuration-name
+ oci-network-configuration-driver
+ oci-network-configuration-gateway
+ oci-network-configuration-internal?
+ oci-network-configuration-ip-range
+ oci-network-configuration-ipam-driver
+ oci-network-configuration-ipv6?
+ oci-network-configuration-subnet
+ oci-network-configuration-labels
+ oci-network-configuration-extra-arguments
+
+ oci-volume-configuration
+ oci-volume-configuration?
+ oci-volume-configuration-fields
+ oci-volume-configuration-name
+ oci-volume-configuration-labels
+ oci-volume-configuration-extra-arguments
+
+ oci-configuration
+ oci-configuration?
+ oci-configuration-fields
+ oci-configuration-runtime
+ oci-configuration-runtime-cli
+ oci-configuration-runtime-extra-arguments
+ oci-configuration-user
+ oci-configuration-group
+ oci-configuration-containers
+ oci-configuration-networks
+ oci-configuration-volumes
+ oci-configuration-verbose?
+
+ oci-extension
+ oci-extension?
+ oci-extension-fields
+ oci-extension-containers
+ oci-extension-networks
+ oci-extension-vol
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 9 Feb 11:15 -0800
[PATCH v2 4/4] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
10254ceb4ef758730d46c5c30798833f9f592833.1739128504.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/services/containers.scm: Export lower-oci-image-state.
* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/services/containers.scm | 35 ++++++++-----
gnu/tests/containers.scm | 80 ++++++++++++++---------------
gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
3 files changed, 114 insertions(+), 101 deletions(-)

Toggle diff (342 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 5c50c99eaf6..2808afe7f08 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -74,6 +74,8 @@ (define-module (gnu services containers)
oci-image-system
oci-image-grafts?
+ lower-oci-image-state
+
oci-container-configuration
oci-container-configuration?
oci-container-configuration-fields
@@ -996,12 +998,9 @@ (define (lower-operating-system os target system)
#:target target)))
(return tarball)))
-(define (lower-manifest name image target system)
- "Lower IMAGE, a manifest record, into a tarball containing an OCI image."
- (define value (oci-image-value image))
- (define options (oci-image-pack-options image))
- (define image-reference
- (oci-image-reference image))
+(define (lower-manifest name value options image-reference
+ target system grafts?)
+ "Lower VALUE, a manifest record, into a tarball containing an OCI image."
(define image-tag
(let* ((extra-options
(get-keyword-value options #:extra-options))
@@ -1013,8 +1012,7 @@ (define (lower-manifest name image target system)
`(#:extra-options (#:image-tag ,image-reference)))))
(mlet* %store-monad
- ((_ (set-grafting
- (oci-image-grafts? image)))
+ ((_ (set-grafting grafts?))
(guile (set-guile-for-build (default-guile)))
(profile
(profile-derivation value
@@ -1029,11 +1027,8 @@ (define (lower-manifest name image target system)
#:localstatedir? #t))))
(return tarball)))
-(define (lower-oci-image name image)
- "Lower IMAGE, a oci-image record, into a tarball containing an OCI image."
- (define value (oci-image-value image))
- (define image-target (oci-image-target image))
- (define image-system (oci-image-system image))
+(define (lower-oci-image-state name value options reference
+ image-target image-system grafts?)
(define target
(if (maybe-value-set? image-target)
image-target
@@ -1046,7 +1041,8 @@ (define (lower-oci-image name image)
(run-with-store store
(match value
((? manifest? value)
- (lower-manifest name image target system))
+ (lower-manifest name value options reference
+ target system grafts?))
((? operating-system? value)
(lower-operating-system value target system))
((or (? gexp? value)
@@ -1061,6 +1057,17 @@ (define (lower-oci-image name image)
#:target target
#:system system)))
+(define (lower-oci-image name image)
+ "Lower IMAGE, a oci-image record, into a tarball containing an OCI image."
+ (lower-oci-image-state
+ name
+ (oci-image-value image)
+ (oci-image-pack-options image)
+ (oci-image-reference image)
+ (oci-image-target image)
+ (oci-image-system image)
+ (oci-image-grafts? image)))
+
(define* (oci-image-loader runtime-cli name image tag #:key (verbose? #f))
"Return a file-like object that, once lowered, will evaluate to a program able
to load IMAGE through RUNTIME-CLI and to tag it with TAG afterwards."
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index bac1f47bd34..1fcf6be7ace 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -69,13 +69,47 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-rootless-podman-os
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..07edd9d5341 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -57,6 +58,40 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +208,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +217,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +349,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.48.1
G
G
Giacomo Leidi wrote on 9 Feb 11:15 -0800
[PATCH v2 2/4] services: oci-container-configuration: Move to (gnu services containers).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
92f19ba6c0842885735dfce653eef535360085f4.1739128504.git.goodoldpaul@autistici.org
This patch moves the oci-container-configuration and related
configuration records to (gnu services containers).
Public symbols are still exported for backwards
compatibility but since the oci-container-service-type will be
deprecated in favor of the more general oci-service-type, everything is
moved outside of the docker related module.

* gnu/services/docker.scm: Move everything related to oci-container-configuration
to...
* gnu/services/containers.scm: ...here.scm.
* gnu/tests/docker.scm: Simplify %test-oci-container test case.

Change-Id: Iae599dd5cc7442eb632f0c1b3b12f6b928397ae7
---
gnu/services/containers.scm | 549 +++++++++++++++++++++++++++++++++-
gnu/services/docker.scm | 577 +++---------------------------------
gnu/tests/docker.scm | 99 +++----
3 files changed, 625 insertions(+), 600 deletions(-)

Toggle diff (534 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index dc66ac4f967..50bf6c05549 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,19 +17,31 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services containers)
+ #:use-module (gnu image)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages containers)
+ #:use-module (gnu packages docker)
#:use-module (gnu packages file-systems)
#:use-module (gnu services)
#:use-module (gnu services base)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu system)
#:use-module (gnu system accounts)
+ #:use-module (gnu system image)
#:use-module (gnu system shadow)
#:use-module (gnu system pam)
+ #:use-module (guix diagnostics)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
+ #:use-module (guix monads)
#:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
rootless-podman-configuration-fields
@@ -48,7 +60,44 @@ (define-module (gnu services containers)
rootless-podman-shepherd-services
rootless-podman-service-etc
- rootless-podman-service-type))
+ rootless-podman-service-type
+
+ oci-image
+ oci-image?
+ oci-image-fields
+ oci-image-repository
+ oci-image-tag
+ oci-image-value
+ oci-image-pack-options
+ oci-image-target
+ oci-image-system
+ oci-image-grafts?
+
+ oci-container-configuration
+ oci-container-configuration?
+ oci-container-configuration-fields
+ oci-container-configuration-user
+ oci-container-configuration-group
+ oci-container-configuration-command
+ oci-container-configuration-entrypoint
+ oci-container-configuration-host-environment
+ oci-container-configuration-environment
+ oci-container-configuration-image
+ oci-container-configuration-provision
+ oci-container-configuration-requirement
+ oci-container-configuration-log-file
+ oci-container-configuration-auto-start?
+ oci-container-configuration-respawn?
+ oci-container-configuration-shepherd-actions
+ oci-container-configuration-network
+ oci-container-configuration-ports
+ oci-container-configuration-volumes
+ oci-container-configuration-container-user
+ oci-container-configuration-workdir
+ oci-container-configuration-extra-arguments
+
+ oci-container-shepherd-service
+ %oci-container-accounts))
(define (gexp-or-string? value)
(or (gexp? value)
@@ -188,7 +237,7 @@ (define (rootless-podman-cgroups-limits-service config)
rootless-podman-shared-root-fs))
(one-shot? #t)
(documentation
- "Allow setting cgroups limits: cpu, cpuset, memory and
+ "Allow setting cgroups limits: cpu, cpuset, io, memory and
pids.")
(start
#~(make-forkexec-constructor
@@ -242,3 +291,497 @@ (define rootless-podman-service-type
(default-value (rootless-podman-configuration))
(description
"This service configures rootless @code{podman} on the Guix System.")))
+
+
+;;;
+;;; OCI container.
+;;;
+
+(define (oci-sanitize-pair pair delimiter)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (match pair
+ (((? valid? key) . (? valid? value))
+ #~(string-append #$key #$delimiter #$value))
+ (_
+ (raise
+ (formatted-message
+ (G_ "pair members must contain only strings, gexps or file-like objects
+but ~a was found")
+ pair)))))
+
+(define (oci-sanitize-mixed-list name value delimiter)
+ (map
+ (lambda (el)
+ (cond ((string? el) el)
+ ((pair? el) (oci-sanitize-pair el delimiter))
+ (else
+ (raise
+ (formatted-message
+ (G_ "~a members must be either a string or a pair but ~a was
+found!")
+ name el)))))
+ value))
+
+(define (oci-sanitize-host-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "host-environment" value "="))
+
+(define (oci-sanitize-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "environment" value "="))
+
+(define (oci-sanitize-ports value)
+ ;; Expected spec format:
+ ;; '(("8088" . "80") "2022:22")
+ (oci-sanitize-mixed-list "ports" value ":"))
+
+(define (oci-sanitize-volumes value)
+ ;; Expected spec format:
+ ;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
+ (oci-sanitize-mixed-list "volumes" value ":"))
+
+(define (oci-sanitize-shepherd-actions value)
+ (map
+ (lambda (el)
+ (if (shepherd-action? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "shepherd-actions may only be shepherd-action records
+but ~a was found") el))))
+ value))
+
+(define (oci-sanitize-extra-arguments value)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (map
+ (lambda (el)
+ (if (valid? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "extra arguments may only be strings, gexps or file-like objects
+but ~a was found") el))))
+ value))
+
+(define (oci-image-reference image)
+ (if (string? image)
+ image
+ (string-append (oci-image-repository image)
+ ":" (oci-image-tag image))))
+
+(define (oci-lowerable-image? image)
+ (or (manifest? image)
+ (operating-system? image)
+ (gexp? image)
+ (file-like? image)))
+
+(define (string-or-oci-image? image)
+ (or (string? image)
+ (oci-image? image)))
+
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe/no-serialization string)
+
+(define-configuration/no-serialization oci-image
+ (repository
+ (string)
+ "A string like @code{myregistry.local:5000/testing/test-image} that names
+the OCI image.")
+ (tag
+ (string "latest")
+ "A string representing the OCI image tag. Defaults to @code{latest}.")
+ (value
+ (oci-lowerable-image)
+ "A @code{manifest} or @code{operating-system} record that will be lowered
+into an OCI compatible tarball. Otherwise this field's value can be a gexp
+or a file-like object that evaluates to an OCI compatible tarball.")
+ (pack-options
+ (list '())
+ "An optional set of keyword arguments that will be passed to the
+@code{docker-image} procedure from @code{guix scripts pack}. They can be used
+to replicate @command{guix pack} behavior:
+
+@lisp
+(oci-image
+ (repository \"guile\")
+ (tag \"3\")
+ (manifest (specifications->manifest '(\"guile\")))
+ (pack-options
+ '(#:symlinks ((\"/bin/guile\" -> \"bin/guile\"))
+ #:max-layers 2)))
+@end lisp
+
+If the @code{value} field is an @code{operating-system} record, this field's
+value will be ignored.")
+ (system
+ (maybe-string)
+ "Attempt to build for a given system, e.g. \"i686-linux\"")
+ (target
+ (maybe-string)
+ "Attempt to cross-build for a given triple, e.g. \"aarch64-linux-gnu\"")
+ (grafts?
+ (boolean #f)
+ "Whether to allow grafting or not in the pack build."))
+
+(define-configuration/no-serialization oci-container-configuration
+ (user
+ (string "oci-container")
+ "The user under whose authority docker commands will be run.")
+ (group
+ (string "docker")
+ "The group under whose authority docker commands will be run.")
+ (command
+ (list-of-strings '())
+ "Overwrite the default command (@code{CMD}) of the image.")
+ (entrypoint
+ (maybe-string)
+ "Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
+ (host-environment
+ (list '())
+ "Set environment variables in the host environment where @command{docker run}
+is invoked. This is especially useful to pass secrets from the host to the
+container without having them on the @command{docker run}'s command line: by
+setting the @code{MYSQL_PASSWORD} on the host and by passing
+@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
+possible to securely set values in the container environment. This field's
+value can be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to @code{make-forkexec-constructor}."
+ (sanitizer oci-sanitize-host-environment))
+ (environment
+ (list '())
+ "Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-environment))
+ (image
+ (string-or-oci-image)
+ "The image used to build the container. It can be a string or an
+@code{oci-image} record. Strings are resolved by the Docker
+Engine, and follow the usual format
+@code{myregistry.local:5000/testing/test-image:tag}.")
+ (provision
+ (maybe-string)
+ "Set the name of the provisioned Shepherd service.")
+ (requirement
+ (list-of-symbols '())
+ "Set additional Shepherd services dependencies to the provisioned Shepherd
+service.")
+ (log-file
+ (maybe-string)
+ "When @code{log-file} is set, it names the file to which the service’s
+standard output and standard error are redirected. @code{log-file} is created
+if it does not exist, otherwise it is appended to.")
+ (auto-start?
+ (boolean #t)
+ "Whether this service should be started automatically by the Shepherd. If it
+is @code{#f} the service has to be started manually with @command{herd start}.")
+ (respawn?
+ (boolean #f)
+ "Whether to restart the service when it stops, for instance when the
+underlying process dies.")
+ (shepherd-actions
+ (list '())
+ "This is a list of @code{shepherd-action} records defining actions supported
+by the service."
+ (sanitizer oci-sanitize-shepherd-actions))
+ (network
+ (maybe-string)
+ "Set a Docker network for the spawned container.")
+ (ports
+ (list '())
+ "Set the port or port ranges to expose from the spawned container. This can
+be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"8080\" . \"80\")
+ \"10443:443\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-ports))
+ (volumes
+ (list '())
+ "Set volume mappings for the spawned container. This can be a
+list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
+ \"/gnu/store:/gnu/store\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-volumes))
+ (container-user
+ (maybe-string)
+ "Set the current user inside the spawned container. You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#user,upstream}
+documentation for semantics.")
+ (workdir
+ (maybe-string)
+ "Set the current working for the spawned Shepherd service.
+You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
+documentation for semantics.")
+ (extra-arguments
+ (list '())
+ "A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} invokation."
+ (sanitizer oci-sanitize-extra-arguments)))
+
+(define oci-container-configuration->options
+ (lambda (config)
+ (let ((entrypoint
+ (oci-container-configuration-entrypoint config))
+ (network
+ (oci-container-configuration-network config))
+ (user
+ (oci-container-configuration-container-user config))
+ (workdir
+ (oci-container-configuration-workdir config)))
+ (apply append
+ (filter (compose not unspecified?)
+ `(,(if (maybe-value-set? entrypoint)
+ `("--entrypoint" ,entrypoint)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "--env" spec))
+ (oci-container-configuration-environment config))
+ ,(if (maybe-value-set? network)
+ `("--network" ,network)
+ '())
+ ,(if (maybe-value-set? user)
+ `("--user" ,user)
+ '())
+ ,(if (maybe-value-set? workdir)
+ `("--workdir" ,workdir)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "-p" spec))
+ (oci-container-configuration-ports config))
+ ,(append-map
+ (lambda (spec)
+ (list "-v" spec))
+ (oci-container-configuration-volumes config))))))))
+
+(define* (get-keyword-value args keyword #:key (default #f))
+ (let ((kv (memq keyword args)))
+ (if (and kv (>= (length kv) 2))
+ (cadr kv)
+ default)))
+
+(define (lower-operating-system os target system)
+ (mlet* %store-monad
+ ((tarball
+ (lower-object
+ (system-image (os->image os #:type docker-image-type))
+ system
+ #:target target)))
+ (return tarball)))
+
+(define (lower-manifest name image target system)
+ (define value (oci-image-value image))
+ (define options (oci-image-pack-options image))
+ (define image-reference
+ (oci-image-reference image))
+ (define image-tag
+ (let* ((extra-options
+ (get-keyword-value options #:extra-options))
+ (image-tag-option
+ (and extra-options
+ (get-keyword-value extra-options #:image-tag))))
+ (if image-tag-option
+ '()
+ `(#:extra-options (#:image-tag ,image-reference)))))
+
+ (mlet* %store-monad
+ ((_ (set-grafting
+ (oci-image-grafts? image)))
+ (guile (set-guile-for-build (default-guile)))
+ (profile
+ (profile-derivation value
+ #:target target
+ #:system system
+ #:hooks '()
+ #:locales? #f))
+ (tarball (apply pack:docker-image
+ `(,name ,profile
+ ,@options
+ ,@image-tag
+ #:localstatedir? #t))))
+ (return tarball)))
+
+(define (lower-oci-image name image)
+ (define value (oci-image-value image))
+ (define image-target (oci-image-target image))
+ (define image-system (oci-image-system image))
+ (define target
+ (if (maybe-value-set? image-target)
+ image-target
+ (%current-target-system)))
+ (define system
+ (if (maybe-value-set? image-system)
+ image-system
+ (%current-system)))
+ (with-store store
+ (run-with-store store
+ (match value
+ ((? manifest? value)
+ (lower-manifest name image target system))
+ ((? operating-system? value)
+ (lower-operating-system value target system))
+ ((or (? gexp? value)
+ (? file-like? value))
+ value)
+ (_
+ (raise
+ (formatted-message
+ (G_ "oci-image value must contain only manifest,
+operating-system, gexp or file-like records but ~a was found")
+ value))))
+ #:target target
+ #:system system)))
+
+(define (%oci-image-loader name image tag)
+ (let ((docker (file-append docker-cli "/bin/docker"))
+ (tarball (lower-oci-image name image)))
+ (with-imported-modules '((guix build utils))
+ (program-file (format #f "~a-image-loader" name)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+
+ (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
+ (define line
+ (read-line
+ (open-input-pipe
+ (string-append #$docker " load -i " #$tarball))))
+
+ (unless (or (eof-object? line)
+ (string-null? line))
+ (format #t "~a~%" line)
+ (let ((repository&tag
+ (string-drop line
+ (string-length
+ "Loaded image: "))))
+
+ (invoke #$docker "tag" repository&tag #$tag)
+ (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
+
+(define (oci-container-shepherd-service config)
+ (define (guess-name name image)
+ (if (maybe-value-set? name)
+ name
+ (string-append "docker-"
+ (basename
+ (if (string? image)
+ (first (string-split image #\:))
+ (oci-image-repository image))))))
+
+ (let* ((docker (file-append docker-cli "/bin/docker"))
+ (actions (oci-container-configuration-shepherd-actions config))
+ (auto-start?
+ (oci-container-configuration-auto-start? config))
+ (user (oci-container-configuration-user config))
+ (group (oci-container-configuration-group config))
+ (host-environment
+ (oci-container-configuration-host-environment config))
+ (command (oci-container-configuration-command config))
+ (log-file (oci-container-configuration-log-file config))
+ (provision (oci-container-configuration-provi
This message was truncated. Download the full message here.
P
Re: OCI provisioning service
(address . 76081@debbugs.gnu.org)
e467b1c6-a54b-400b-97a6-1a7b4082686b@autistici.org
I'm sending a v3 fixing a bug in the merge algorithm for volumes and
networks.

On 2/9/25 20:14, paul wrote:
Toggle quote (26 lines)
> Hi,
>
> I'm about to send a v2. v2 compared to the first revision features:
>
>
> - it actually compiles all the times :) (rev 1 referenced oci-image
> too early for it to be working and generated a compile time error, if
> you recompiled it sometimes went away so I thought it was a problem of
> my setup. CI caught this)
> - it allows more values to be overridden by eventual users of the
> Scheme API
> - it allows passing extra arguments directly after each podman or
> docker invokation, allowing for example for overriding podman --root
> and similar options.
>
> All of these tests should pass:
>
> guix shell -D guix -CPW -- make check-system TESTS="oci-container
> oci-service-rootless-podman docker docker-system rootless-podman
> oci-service-docker"
>
>
> Thank you for your work,
>
> giacomo
>
G
G
Giacomo Leidi wrote on 9 Feb 12:39 -0800
[PATCH v3 1/4] services: rootless-podman: Use login shell.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
89778533f32ad1388f03414e884fff10f74ef379.1739133548.git.goodoldpaul@autistici.org
This commit allows for having PATH set when changing the owner of
/sys/fs/group.

* gnu/services/containers.scm (crgroups-fs-owner): Use login shell.

Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
---
gnu/services/containers.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Toggle diff (17 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 19d35ccbcb6..dc66ac4f967 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
(rootless-podman-configuration-group-name config))
(program-file "cgroups2-fs-owner-entrypoint"
#~(system*
- (string-append #+bash-minimal "/bin/bash") "-c"
+ (string-append #+bash-minimal "/bin/bash") "-l" "-c"
(string-append "echo Setting /sys/fs/cgroup "
"group ownership to " #$group " && chown -v "
"root:" #$group " /sys/fs/cgroup && "

base-commit: 5a897c5c95a81278b044c18d962d3bd83131ba06
--
2.48.1
G
G
Giacomo Leidi wrote on 9 Feb 12:39 -0800
[PATCH v3 3/4] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
5fa06184f4690fd44c553972dad855feaf7491f5.1739133548.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/services/containers.scm (%oci-supported-runtimes): New variable;
(oci-runtime-cli): new variable;
(oci-runtime-name): new variable;
(oci-network-configuration): new variable;
(oci-volume-configuration): new variable;
(oci-configuration): new variable;
(oci-extension): new variable;
(oci-networks-shepherd-name): new variable;
(oci-service-type): new variable;
(oci-state->shepherd-services): new variable.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 299 ++++++++--
gnu/services/containers.scm | 1086 +++++++++++++++++++++++++++++++----
gnu/services/docker.scm | 37 +-
gnu/tests/containers.scm | 999 +++++++++++++++++++++++++++++++-
4 files changed, 2225 insertions(+), 196 deletions(-)

Toggle diff (456 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index bb5f29277fb..8f66b35297b 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -41778,59 +41778,159 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@command{docker run} or @command{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources. When unset it will default to @code{docker-cli}
+package for the @code{'docker} runtime or to @code{podman} package for the
+@code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (default: @code{"oci-container"}) (type: string)
+The user name under whose authority OCI commands will be run.
+
+@item @code{group} (default: @code{"docker"}) (type: string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -41850,16 +41950,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @command{docker run}
+or @command{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -41867,22 +41967,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -41910,7 +42012,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -41921,10 +42023,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -41935,25 +42038,95 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} or @command{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly
+passed to the runtime invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+passed to the runtime invokation.
@end table
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 50bf6c05549..12c509c07ab 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -41,6 +41,7 @@ (define-module (gnu services containers)
#:use-module ((guix scripts pack) #:prefix pack:)
#:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
@@ -96,8 +97,72 @@ (define-module (gnu services containers)
oci-container-configuration-workdir
oci-container-configuration-extra-arguments
+ list-of-oci-containers?
+ list-of-oci-networks?
+ list-of-oci-volumes?
+
+ %oci-supported-runtimes
+ oci-sanitize-runtime
+ oci-runtime-system-environment
+ oci-runtime-system-extra-arguments
+ oci-runtime-system-group
+ oci-runtime-system-requirement
+ oci-runtime-cli
+ oci-runtime-system-cli
+ oci-runtime-name
+ oci-runtime-group
+
+ oci-network-configuration
+ oci-network-configuration?
+ oci-network-configuration-fields
+ oci-network-configuration-name
+ oci-network-configuration-driver
+ oci-network-configuration-gateway
+ oci-network-configuration-internal?
+ oci-network-configuration-ip-range
+ oci-network-configuration-ipam-driver
+ oci-network-configuration-ipv6?
+ oci-network-configuration-subnet
+ oci-network-configuration-labels
+ oci-network-configuration-extra-arguments
+
+ oci-volume-configuration
+ oci-volume-configuration?
+ oci-volume-configuration-fields
+ oci-volume-configuration-name
+ oci-volume-configuration-labels
+ oci-volume-configuration-extra-arguments
+
+ oci-configuration
+ oci-configuration?
+ oci-configuration-fields
+ oci-configuration-runtime
+ oci-configuration-runtime-cli
+ oci-configuration-runtime-extra-arguments
+ oci-configuration-user
+ oci-configuration-group
+ oci-configuration-containers
+ oci-configuration-networks
+ oci-configuration-volumes
+ oci-configuration-verbose?
+
+ oci-extension
+ oci-extension?
+ oci-extension-fields
+ oci-extension-containers
+ oci-extension-networks
+ oci-extension-vol
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 9 Feb 12:39 -0800
[PATCH v3 4/4] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
de9951c72836295e9eae310c2e19b372982915b3.1739133548.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/services/containers.scm: Export lower-oci-image-state.
* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/services/containers.scm | 35 ++++++++-----
gnu/tests/containers.scm | 80 ++++++++++++++---------------
gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
3 files changed, 114 insertions(+), 101 deletions(-)

Toggle diff (342 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 12c509c07ab..5979b26e6c9 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -74,6 +74,8 @@ (define-module (gnu services containers)
oci-image-system
oci-image-grafts?
+ lower-oci-image-state
+
oci-container-configuration
oci-container-configuration?
oci-container-configuration-fields
@@ -996,12 +998,9 @@ (define (lower-operating-system os target system)
#:target target)))
(return tarball)))
-(define (lower-manifest name image target system)
- "Lower IMAGE, a manifest record, into a tarball containing an OCI image."
- (define value (oci-image-value image))
- (define options (oci-image-pack-options image))
- (define image-reference
- (oci-image-reference image))
+(define (lower-manifest name value options image-reference
+ target system grafts?)
+ "Lower VALUE, a manifest record, into a tarball containing an OCI image."
(define image-tag
(let* ((extra-options
(get-keyword-value options #:extra-options))
@@ -1013,8 +1012,7 @@ (define (lower-manifest name image target system)
`(#:extra-options (#:image-tag ,image-reference)))))
(mlet* %store-monad
- ((_ (set-grafting
- (oci-image-grafts? image)))
+ ((_ (set-grafting grafts?))
(guile (set-guile-for-build (default-guile)))
(profile
(profile-derivation value
@@ -1029,11 +1027,8 @@ (define (lower-manifest name image target system)
#:localstatedir? #t))))
(return tarball)))
-(define (lower-oci-image name image)
- "Lower IMAGE, a oci-image record, into a tarball containing an OCI image."
- (define value (oci-image-value image))
- (define image-target (oci-image-target image))
- (define image-system (oci-image-system image))
+(define (lower-oci-image-state name value options reference
+ image-target image-system grafts?)
(define target
(if (maybe-value-set? image-target)
image-target
@@ -1046,7 +1041,8 @@ (define (lower-oci-image name image)
(run-with-store store
(match value
((? manifest? value)
- (lower-manifest name image target system))
+ (lower-manifest name value options reference
+ target system grafts?))
((? operating-system? value)
(lower-operating-system value target system))
((or (? gexp? value)
@@ -1061,6 +1057,17 @@ (define (lower-oci-image name image)
#:target target
#:system system)))
+(define (lower-oci-image name image)
+ "Lower IMAGE, a oci-image record, into a tarball containing an OCI image."
+ (lower-oci-image-state
+ name
+ (oci-image-value image)
+ (oci-image-pack-options image)
+ (oci-image-reference image)
+ (oci-image-target image)
+ (oci-image-system image)
+ (oci-image-grafts? image)))
+
(define* (oci-image-loader runtime-cli name image tag #:key (verbose? #f))
"Return a file-like object that, once lowered, will evaluate to a program able
to load IMAGE through RUNTIME-CLI and to tag it with TAG afterwards."
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index bac1f47bd34..1fcf6be7ace 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -69,13 +69,47 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-rootless-podman-os
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..07edd9d5341 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -57,6 +58,40 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +208,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +217,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +349,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.48.1
G
G
Giacomo Leidi wrote on 9 Feb 12:39 -0800
[PATCH v3 2/4] services: oci-container-configuration: Move to (gnu services containers).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
92f19ba6c0842885735dfce653eef535360085f4.1739133548.git.goodoldpaul@autistici.org
This patch moves the oci-container-configuration and related
configuration records to (gnu services containers).
Public symbols are still exported for backwards
compatibility but since the oci-container-service-type will be
deprecated in favor of the more general oci-service-type, everything is
moved outside of the docker related module.

* gnu/services/docker.scm: Move everything related to oci-container-configuration
to...
* gnu/services/containers.scm: ...here.scm.
* gnu/tests/docker.scm: Simplify %test-oci-container test case.

Change-Id: Iae599dd5cc7442eb632f0c1b3b12f6b928397ae7
---
gnu/services/containers.scm | 549 +++++++++++++++++++++++++++++++++-
gnu/services/docker.scm | 577 +++---------------------------------
gnu/tests/docker.scm | 99 +++----
3 files changed, 625 insertions(+), 600 deletions(-)

Toggle diff (534 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index dc66ac4f967..50bf6c05549 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,19 +17,31 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services containers)
+ #:use-module (gnu image)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages containers)
+ #:use-module (gnu packages docker)
#:use-module (gnu packages file-systems)
#:use-module (gnu services)
#:use-module (gnu services base)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu system)
#:use-module (gnu system accounts)
+ #:use-module (gnu system image)
#:use-module (gnu system shadow)
#:use-module (gnu system pam)
+ #:use-module (guix diagnostics)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
+ #:use-module (guix monads)
#:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
rootless-podman-configuration-fields
@@ -48,7 +60,44 @@ (define-module (gnu services containers)
rootless-podman-shepherd-services
rootless-podman-service-etc
- rootless-podman-service-type))
+ rootless-podman-service-type
+
+ oci-image
+ oci-image?
+ oci-image-fields
+ oci-image-repository
+ oci-image-tag
+ oci-image-value
+ oci-image-pack-options
+ oci-image-target
+ oci-image-system
+ oci-image-grafts?
+
+ oci-container-configuration
+ oci-container-configuration?
+ oci-container-configuration-fields
+ oci-container-configuration-user
+ oci-container-configuration-group
+ oci-container-configuration-command
+ oci-container-configuration-entrypoint
+ oci-container-configuration-host-environment
+ oci-container-configuration-environment
+ oci-container-configuration-image
+ oci-container-configuration-provision
+ oci-container-configuration-requirement
+ oci-container-configuration-log-file
+ oci-container-configuration-auto-start?
+ oci-container-configuration-respawn?
+ oci-container-configuration-shepherd-actions
+ oci-container-configuration-network
+ oci-container-configuration-ports
+ oci-container-configuration-volumes
+ oci-container-configuration-container-user
+ oci-container-configuration-workdir
+ oci-container-configuration-extra-arguments
+
+ oci-container-shepherd-service
+ %oci-container-accounts))
(define (gexp-or-string? value)
(or (gexp? value)
@@ -188,7 +237,7 @@ (define (rootless-podman-cgroups-limits-service config)
rootless-podman-shared-root-fs))
(one-shot? #t)
(documentation
- "Allow setting cgroups limits: cpu, cpuset, memory and
+ "Allow setting cgroups limits: cpu, cpuset, io, memory and
pids.")
(start
#~(make-forkexec-constructor
@@ -242,3 +291,497 @@ (define rootless-podman-service-type
(default-value (rootless-podman-configuration))
(description
"This service configures rootless @code{podman} on the Guix System.")))
+
+
+;;;
+;;; OCI container.
+;;;
+
+(define (oci-sanitize-pair pair delimiter)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (match pair
+ (((? valid? key) . (? valid? value))
+ #~(string-append #$key #$delimiter #$value))
+ (_
+ (raise
+ (formatted-message
+ (G_ "pair members must contain only strings, gexps or file-like objects
+but ~a was found")
+ pair)))))
+
+(define (oci-sanitize-mixed-list name value delimiter)
+ (map
+ (lambda (el)
+ (cond ((string? el) el)
+ ((pair? el) (oci-sanitize-pair el delimiter))
+ (else
+ (raise
+ (formatted-message
+ (G_ "~a members must be either a string or a pair but ~a was
+found!")
+ name el)))))
+ value))
+
+(define (oci-sanitize-host-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "host-environment" value "="))
+
+(define (oci-sanitize-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "environment" value "="))
+
+(define (oci-sanitize-ports value)
+ ;; Expected spec format:
+ ;; '(("8088" . "80") "2022:22")
+ (oci-sanitize-mixed-list "ports" value ":"))
+
+(define (oci-sanitize-volumes value)
+ ;; Expected spec format:
+ ;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
+ (oci-sanitize-mixed-list "volumes" value ":"))
+
+(define (oci-sanitize-shepherd-actions value)
+ (map
+ (lambda (el)
+ (if (shepherd-action? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "shepherd-actions may only be shepherd-action records
+but ~a was found") el))))
+ value))
+
+(define (oci-sanitize-extra-arguments value)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (map
+ (lambda (el)
+ (if (valid? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "extra arguments may only be strings, gexps or file-like objects
+but ~a was found") el))))
+ value))
+
+(define (oci-image-reference image)
+ (if (string? image)
+ image
+ (string-append (oci-image-repository image)
+ ":" (oci-image-tag image))))
+
+(define (oci-lowerable-image? image)
+ (or (manifest? image)
+ (operating-system? image)
+ (gexp? image)
+ (file-like? image)))
+
+(define (string-or-oci-image? image)
+ (or (string? image)
+ (oci-image? image)))
+
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe/no-serialization string)
+
+(define-configuration/no-serialization oci-image
+ (repository
+ (string)
+ "A string like @code{myregistry.local:5000/testing/test-image} that names
+the OCI image.")
+ (tag
+ (string "latest")
+ "A string representing the OCI image tag. Defaults to @code{latest}.")
+ (value
+ (oci-lowerable-image)
+ "A @code{manifest} or @code{operating-system} record that will be lowered
+into an OCI compatible tarball. Otherwise this field's value can be a gexp
+or a file-like object that evaluates to an OCI compatible tarball.")
+ (pack-options
+ (list '())
+ "An optional set of keyword arguments that will be passed to the
+@code{docker-image} procedure from @code{guix scripts pack}. They can be used
+to replicate @command{guix pack} behavior:
+
+@lisp
+(oci-image
+ (repository \"guile\")
+ (tag \"3\")
+ (manifest (specifications->manifest '(\"guile\")))
+ (pack-options
+ '(#:symlinks ((\"/bin/guile\" -> \"bin/guile\"))
+ #:max-layers 2)))
+@end lisp
+
+If the @code{value} field is an @code{operating-system} record, this field's
+value will be ignored.")
+ (system
+ (maybe-string)
+ "Attempt to build for a given system, e.g. \"i686-linux\"")
+ (target
+ (maybe-string)
+ "Attempt to cross-build for a given triple, e.g. \"aarch64-linux-gnu\"")
+ (grafts?
+ (boolean #f)
+ "Whether to allow grafting or not in the pack build."))
+
+(define-configuration/no-serialization oci-container-configuration
+ (user
+ (string "oci-container")
+ "The user under whose authority docker commands will be run.")
+ (group
+ (string "docker")
+ "The group under whose authority docker commands will be run.")
+ (command
+ (list-of-strings '())
+ "Overwrite the default command (@code{CMD}) of the image.")
+ (entrypoint
+ (maybe-string)
+ "Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
+ (host-environment
+ (list '())
+ "Set environment variables in the host environment where @command{docker run}
+is invoked. This is especially useful to pass secrets from the host to the
+container without having them on the @command{docker run}'s command line: by
+setting the @code{MYSQL_PASSWORD} on the host and by passing
+@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
+possible to securely set values in the container environment. This field's
+value can be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to @code{make-forkexec-constructor}."
+ (sanitizer oci-sanitize-host-environment))
+ (environment
+ (list '())
+ "Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-environment))
+ (image
+ (string-or-oci-image)
+ "The image used to build the container. It can be a string or an
+@code{oci-image} record. Strings are resolved by the Docker
+Engine, and follow the usual format
+@code{myregistry.local:5000/testing/test-image:tag}.")
+ (provision
+ (maybe-string)
+ "Set the name of the provisioned Shepherd service.")
+ (requirement
+ (list-of-symbols '())
+ "Set additional Shepherd services dependencies to the provisioned Shepherd
+service.")
+ (log-file
+ (maybe-string)
+ "When @code{log-file} is set, it names the file to which the service’s
+standard output and standard error are redirected. @code{log-file} is created
+if it does not exist, otherwise it is appended to.")
+ (auto-start?
+ (boolean #t)
+ "Whether this service should be started automatically by the Shepherd. If it
+is @code{#f} the service has to be started manually with @command{herd start}.")
+ (respawn?
+ (boolean #f)
+ "Whether to restart the service when it stops, for instance when the
+underlying process dies.")
+ (shepherd-actions
+ (list '())
+ "This is a list of @code{shepherd-action} records defining actions supported
+by the service."
+ (sanitizer oci-sanitize-shepherd-actions))
+ (network
+ (maybe-string)
+ "Set a Docker network for the spawned container.")
+ (ports
+ (list '())
+ "Set the port or port ranges to expose from the spawned container. This can
+be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"8080\" . \"80\")
+ \"10443:443\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-ports))
+ (volumes
+ (list '())
+ "Set volume mappings for the spawned container. This can be a
+list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
+ \"/gnu/store:/gnu/store\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-volumes))
+ (container-user
+ (maybe-string)
+ "Set the current user inside the spawned container. You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#user,upstream}
+documentation for semantics.")
+ (workdir
+ (maybe-string)
+ "Set the current working for the spawned Shepherd service.
+You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
+documentation for semantics.")
+ (extra-arguments
+ (list '())
+ "A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} invokation."
+ (sanitizer oci-sanitize-extra-arguments)))
+
+(define oci-container-configuration->options
+ (lambda (config)
+ (let ((entrypoint
+ (oci-container-configuration-entrypoint config))
+ (network
+ (oci-container-configuration-network config))
+ (user
+ (oci-container-configuration-container-user config))
+ (workdir
+ (oci-container-configuration-workdir config)))
+ (apply append
+ (filter (compose not unspecified?)
+ `(,(if (maybe-value-set? entrypoint)
+ `("--entrypoint" ,entrypoint)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "--env" spec))
+ (oci-container-configuration-environment config))
+ ,(if (maybe-value-set? network)
+ `("--network" ,network)
+ '())
+ ,(if (maybe-value-set? user)
+ `("--user" ,user)
+ '())
+ ,(if (maybe-value-set? workdir)
+ `("--workdir" ,workdir)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "-p" spec))
+ (oci-container-configuration-ports config))
+ ,(append-map
+ (lambda (spec)
+ (list "-v" spec))
+ (oci-container-configuration-volumes config))))))))
+
+(define* (get-keyword-value args keyword #:key (default #f))
+ (let ((kv (memq keyword args)))
+ (if (and kv (>= (length kv) 2))
+ (cadr kv)
+ default)))
+
+(define (lower-operating-system os target system)
+ (mlet* %store-monad
+ ((tarball
+ (lower-object
+ (system-image (os->image os #:type docker-image-type))
+ system
+ #:target target)))
+ (return tarball)))
+
+(define (lower-manifest name image target system)
+ (define value (oci-image-value image))
+ (define options (oci-image-pack-options image))
+ (define image-reference
+ (oci-image-reference image))
+ (define image-tag
+ (let* ((extra-options
+ (get-keyword-value options #:extra-options))
+ (image-tag-option
+ (and extra-options
+ (get-keyword-value extra-options #:image-tag))))
+ (if image-tag-option
+ '()
+ `(#:extra-options (#:image-tag ,image-reference)))))
+
+ (mlet* %store-monad
+ ((_ (set-grafting
+ (oci-image-grafts? image)))
+ (guile (set-guile-for-build (default-guile)))
+ (profile
+ (profile-derivation value
+ #:target target
+ #:system system
+ #:hooks '()
+ #:locales? #f))
+ (tarball (apply pack:docker-image
+ `(,name ,profile
+ ,@options
+ ,@image-tag
+ #:localstatedir? #t))))
+ (return tarball)))
+
+(define (lower-oci-image name image)
+ (define value (oci-image-value image))
+ (define image-target (oci-image-target image))
+ (define image-system (oci-image-system image))
+ (define target
+ (if (maybe-value-set? image-target)
+ image-target
+ (%current-target-system)))
+ (define system
+ (if (maybe-value-set? image-system)
+ image-system
+ (%current-system)))
+ (with-store store
+ (run-with-store store
+ (match value
+ ((? manifest? value)
+ (lower-manifest name image target system))
+ ((? operating-system? value)
+ (lower-operating-system value target system))
+ ((or (? gexp? value)
+ (? file-like? value))
+ value)
+ (_
+ (raise
+ (formatted-message
+ (G_ "oci-image value must contain only manifest,
+operating-system, gexp or file-like records but ~a was found")
+ value))))
+ #:target target
+ #:system system)))
+
+(define (%oci-image-loader name image tag)
+ (let ((docker (file-append docker-cli "/bin/docker"))
+ (tarball (lower-oci-image name image)))
+ (with-imported-modules '((guix build utils))
+ (program-file (format #f "~a-image-loader" name)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+
+ (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
+ (define line
+ (read-line
+ (open-input-pipe
+ (string-append #$docker " load -i " #$tarball))))
+
+ (unless (or (eof-object? line)
+ (string-null? line))
+ (format #t "~a~%" line)
+ (let ((repository&tag
+ (string-drop line
+ (string-length
+ "Loaded image: "))))
+
+ (invoke #$docker "tag" repository&tag #$tag)
+ (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
+
+(define (oci-container-shepherd-service config)
+ (define (guess-name name image)
+ (if (maybe-value-set? name)
+ name
+ (string-append "docker-"
+ (basename
+ (if (string? image)
+ (first (string-split image #\:))
+ (oci-image-repository image))))))
+
+ (let* ((docker (file-append docker-cli "/bin/docker"))
+ (actions (oci-container-configuration-shepherd-actions config))
+ (auto-start?
+ (oci-container-configuration-auto-start? config))
+ (user (oci-container-configuration-user config))
+ (group (oci-container-configuration-group config))
+ (host-environment
+ (oci-container-configuration-host-environment config))
+ (command (oci-container-configuration-command config))
+ (log-file (oci-container-configuration-log-file config))
+ (provision (oci-container-configuration-provi
This message was truncated. Download the full message here.
P
Re: OCI provisioning service
(address . 76081@debbugs.gnu.org)
274b98ea-1af9-4f0f-af58-8fa755feb73b@autistici.org
Hi guix,

On 2/9/25 21:38, paul wrote:
Toggle quote (25 lines)
> I'm sending a v3 fixing a bug in the merge algorithm for volumes and
> networks.
>
> On 2/9/25 20:14, paul wrote:
>> Hi,
>>
>> I'm about to send a v2. v2 compared to the first revision features:
>>
>>
>> - it actually compiles all the times :) (rev 1 referenced oci-image
>> too early for it to be working and generated a compile time error, if
>> you recompiled it sometimes went away so I thought it was a problem
>> of my setup. CI caught this)
>> - it allows more values to be overridden by eventual users of the
>> Scheme API
>> - it allows passing extra arguments directly after each podman or
>> docker invokation, allowing for example for overriding podman --root
>> and similar options.
>>
>> All of these tests should pass:
>>
>> guix shell -D guix -CPW -- make check-system TESTS="oci-container
>> oci-service-rootless-podman docker docker-system rootless-podman
>> oci-service-docker"
>>
I'm sending a v4 changing slightly the image loader, the same tests as
before are supposed to pass. Now the Home service [0] is working for me
with rootless podman. I'll try it on different distros if I manage to.


Thank you for your work,

giacomo


[0]:
G
G
Giacomo Leidi wrote on 11 Feb 17:10 -0800
[PATCH v4 3/4] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
30398d43f3b262022c3511187fe92296b00667ce.1739322637.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/services/containers.scm (%oci-supported-runtimes): New variable;
(oci-runtime-cli): new variable;
(oci-runtime-name): new variable;
(oci-network-configuration): new variable;
(oci-volume-configuration): new variable;
(oci-configuration): new variable;
(oci-extension): new variable;
(oci-networks-shepherd-name): new variable;
(oci-service-type): new variable;
(oci-state->shepherd-services): new variable.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 305 +++++++--
gnu/services/containers.scm | 1217 ++++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 37 +-
gnu/tests/containers.scm | 999 +++++++++++++++++++++++++++-
4 files changed, 2313 insertions(+), 245 deletions(-)

Toggle diff (452 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index ce780682ed0..500cf9f839c 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -41872,59 +41872,161 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@command{docker run} or @command{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources. When unset it will default to @code{docker-cli}
+package for the @code{'docker} runtime or to @code{podman} package for the
+@code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -41944,16 +42046,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @command{docker run}
+or @command{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -41961,22 +42063,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -42004,7 +42108,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -42015,10 +42119,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -42029,25 +42134,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} or @command{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker network create} or @command{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker volume create} or @command{podman volume create}
+invokation.
@end table
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 50bf6c05549..018dfa60f48 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -41,6 +41,7 @@ (define-module (gnu services containers)
#:use-module ((guix scripts pack) #:prefix pack:)
#:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
@@ -96,8 +97,73 @@ (define-module (gnu services containers)
oci-container-configuration-workdir
oci-container-configuration-extra-arguments
+ list-of-oci-containers?
+ list-of-oci-networks?
+ list-of-oci-volumes?
+
+ %oci-supported-runtimes
+ oci-sanitize-runtime
+ oci-runtime-system-environment
+ oci-runtime-system-extra-arguments
+ oci-runtime-system-group
+ oci-runtime-system-requirement
+ oci-runtime-cli
+ oci-runtime-system-cli
+ oci-runtime-name
+ oci-runtime-group
+
+ oci-network-configuration
+ oci-network-configuration?
+ oci-network-configuration-fields
+ oci-network-configuration-name
+ oci-network-configuration-driver
+ oci-network-configuration-gateway
+ oci-network-configuration-internal?
+ oci-network-configuration-ip-range
+ oci-network-configuration-ipam-driver
+ oci-network-configuration-ipv6?
+ oci-network-configuration-subnet
+ oci-network-configuration-labels
+ oci-network-configuration-extra-arguments
+
+ oci-volume-configuration
+ oci-volume-configuration?
+ oci-volume-configuration-fields
+ oci-volume-configuration-name
+ oci-volume-configuration-labels
+ oci-volume-configuration-extra-arguments
+
+ oci-configuration
+ oci-configuration?
+ oci-configuration-fields
+ oci-configuration-runtime
+ oci-configuration-runtime-cli
+ oci-configuration-runtime-extra-arguments
+ oci-configuration-user
+ oci-configuration-group
+ oci-configuration-containers
+ oci-configur
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 11 Feb 17:10 -0800
[PATCH v4 4/4] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
ca35d6b7caceb4e2dd3db863b9b7c1601f3ee0cf.1739322637.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/services/containers.scm: Export lower-oci-image-state.
* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/services/containers.scm | 2 +
gnu/tests/containers.scm | 80 ++++++++++++++---------------
gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
3 files changed, 95 insertions(+), 87 deletions(-)

Toggle diff (274 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 018dfa60f48..63e9cb37b53 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -74,6 +74,8 @@ (define-module (gnu services containers)
oci-image-system
oci-image-grafts?
+ lower-oci-image-state
+
oci-container-configuration
oci-container-configuration?
oci-container-configuration-fields
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index bac1f47bd34..1fcf6be7ace 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -69,13 +69,47 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-rootless-podman-os
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..07edd9d5341 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -57,6 +58,40 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +208,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +217,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +349,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.48.1
G
G
Giacomo Leidi wrote on 11 Feb 17:10 -0800
[PATCH v4 1/4] services: rootless-podman: Use login shell.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
3e199fa3b6b15d853ea1887ad0c518c7cb4f4b25.1739322637.git.goodoldpaul@autistici.org
This commit allows for having PATH set when changing the owner of
/sys/fs/group.

* gnu/services/containers.scm (crgroups-fs-owner): Use login shell.

Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
---
gnu/services/containers.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Toggle diff (17 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 19d35ccbcb6..dc66ac4f967 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
(rootless-podman-configuration-group-name config))
(program-file "cgroups2-fs-owner-entrypoint"
#~(system*
- (string-append #+bash-minimal "/bin/bash") "-c"
+ (string-append #+bash-minimal "/bin/bash") "-l" "-c"
(string-append "echo Setting /sys/fs/cgroup "
"group ownership to " #$group " && chown -v "
"root:" #$group " /sys/fs/cgroup && "

base-commit: f650fc3e15e0ba04b3998daf13db8666fcfd9627
--
2.48.1
G
G
Giacomo Leidi wrote on 11 Feb 17:10 -0800
[PATCH v4 2/4] services: oci-container-configuration: Move to (gnu services containers).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
77229c8777b0b074a44bd848b60140a49b621ac6.1739322637.git.goodoldpaul@autistici.org
This patch moves the oci-container-configuration and related
configuration records to (gnu services containers).
Public symbols are still exported for backwards
compatibility but since the oci-container-service-type will be
deprecated in favor of the more general oci-service-type, everything is
moved outside of the docker related module.

* gnu/services/docker.scm: Move everything related to oci-container-configuration
to...
* gnu/services/containers.scm: ...here.scm.
* gnu/tests/docker.scm: Simplify %test-oci-container test case.

Change-Id: Iae599dd5cc7442eb632f0c1b3b12f6b928397ae7
---
gnu/services/containers.scm | 549 +++++++++++++++++++++++++++++++++-
gnu/services/docker.scm | 577 +++---------------------------------
gnu/tests/docker.scm | 99 +++----
3 files changed, 625 insertions(+), 600 deletions(-)

Toggle diff (534 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index dc66ac4f967..50bf6c05549 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,19 +17,31 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services containers)
+ #:use-module (gnu image)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages containers)
+ #:use-module (gnu packages docker)
#:use-module (gnu packages file-systems)
#:use-module (gnu services)
#:use-module (gnu services base)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu system)
#:use-module (gnu system accounts)
+ #:use-module (gnu system image)
#:use-module (gnu system shadow)
#:use-module (gnu system pam)
+ #:use-module (guix diagnostics)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
+ #:use-module (guix monads)
#:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
rootless-podman-configuration-fields
@@ -48,7 +60,44 @@ (define-module (gnu services containers)
rootless-podman-shepherd-services
rootless-podman-service-etc
- rootless-podman-service-type))
+ rootless-podman-service-type
+
+ oci-image
+ oci-image?
+ oci-image-fields
+ oci-image-repository
+ oci-image-tag
+ oci-image-value
+ oci-image-pack-options
+ oci-image-target
+ oci-image-system
+ oci-image-grafts?
+
+ oci-container-configuration
+ oci-container-configuration?
+ oci-container-configuration-fields
+ oci-container-configuration-user
+ oci-container-configuration-group
+ oci-container-configuration-command
+ oci-container-configuration-entrypoint
+ oci-container-configuration-host-environment
+ oci-container-configuration-environment
+ oci-container-configuration-image
+ oci-container-configuration-provision
+ oci-container-configuration-requirement
+ oci-container-configuration-log-file
+ oci-container-configuration-auto-start?
+ oci-container-configuration-respawn?
+ oci-container-configuration-shepherd-actions
+ oci-container-configuration-network
+ oci-container-configuration-ports
+ oci-container-configuration-volumes
+ oci-container-configuration-container-user
+ oci-container-configuration-workdir
+ oci-container-configuration-extra-arguments
+
+ oci-container-shepherd-service
+ %oci-container-accounts))
(define (gexp-or-string? value)
(or (gexp? value)
@@ -188,7 +237,7 @@ (define (rootless-podman-cgroups-limits-service config)
rootless-podman-shared-root-fs))
(one-shot? #t)
(documentation
- "Allow setting cgroups limits: cpu, cpuset, memory and
+ "Allow setting cgroups limits: cpu, cpuset, io, memory and
pids.")
(start
#~(make-forkexec-constructor
@@ -242,3 +291,497 @@ (define rootless-podman-service-type
(default-value (rootless-podman-configuration))
(description
"This service configures rootless @code{podman} on the Guix System.")))
+
+
+;;;
+;;; OCI container.
+;;;
+
+(define (oci-sanitize-pair pair delimiter)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (match pair
+ (((? valid? key) . (? valid? value))
+ #~(string-append #$key #$delimiter #$value))
+ (_
+ (raise
+ (formatted-message
+ (G_ "pair members must contain only strings, gexps or file-like objects
+but ~a was found")
+ pair)))))
+
+(define (oci-sanitize-mixed-list name value delimiter)
+ (map
+ (lambda (el)
+ (cond ((string? el) el)
+ ((pair? el) (oci-sanitize-pair el delimiter))
+ (else
+ (raise
+ (formatted-message
+ (G_ "~a members must be either a string or a pair but ~a was
+found!")
+ name el)))))
+ value))
+
+(define (oci-sanitize-host-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "host-environment" value "="))
+
+(define (oci-sanitize-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "environment" value "="))
+
+(define (oci-sanitize-ports value)
+ ;; Expected spec format:
+ ;; '(("8088" . "80") "2022:22")
+ (oci-sanitize-mixed-list "ports" value ":"))
+
+(define (oci-sanitize-volumes value)
+ ;; Expected spec format:
+ ;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
+ (oci-sanitize-mixed-list "volumes" value ":"))
+
+(define (oci-sanitize-shepherd-actions value)
+ (map
+ (lambda (el)
+ (if (shepherd-action? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "shepherd-actions may only be shepherd-action records
+but ~a was found") el))))
+ value))
+
+(define (oci-sanitize-extra-arguments value)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (map
+ (lambda (el)
+ (if (valid? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "extra arguments may only be strings, gexps or file-like objects
+but ~a was found") el))))
+ value))
+
+(define (oci-image-reference image)
+ (if (string? image)
+ image
+ (string-append (oci-image-repository image)
+ ":" (oci-image-tag image))))
+
+(define (oci-lowerable-image? image)
+ (or (manifest? image)
+ (operating-system? image)
+ (gexp? image)
+ (file-like? image)))
+
+(define (string-or-oci-image? image)
+ (or (string? image)
+ (oci-image? image)))
+
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe/no-serialization string)
+
+(define-configuration/no-serialization oci-image
+ (repository
+ (string)
+ "A string like @code{myregistry.local:5000/testing/test-image} that names
+the OCI image.")
+ (tag
+ (string "latest")
+ "A string representing the OCI image tag. Defaults to @code{latest}.")
+ (value
+ (oci-lowerable-image)
+ "A @code{manifest} or @code{operating-system} record that will be lowered
+into an OCI compatible tarball. Otherwise this field's value can be a gexp
+or a file-like object that evaluates to an OCI compatible tarball.")
+ (pack-options
+ (list '())
+ "An optional set of keyword arguments that will be passed to the
+@code{docker-image} procedure from @code{guix scripts pack}. They can be used
+to replicate @command{guix pack} behavior:
+
+@lisp
+(oci-image
+ (repository \"guile\")
+ (tag \"3\")
+ (manifest (specifications->manifest '(\"guile\")))
+ (pack-options
+ '(#:symlinks ((\"/bin/guile\" -> \"bin/guile\"))
+ #:max-layers 2)))
+@end lisp
+
+If the @code{value} field is an @code{operating-system} record, this field's
+value will be ignored.")
+ (system
+ (maybe-string)
+ "Attempt to build for a given system, e.g. \"i686-linux\"")
+ (target
+ (maybe-string)
+ "Attempt to cross-build for a given triple, e.g. \"aarch64-linux-gnu\"")
+ (grafts?
+ (boolean #f)
+ "Whether to allow grafting or not in the pack build."))
+
+(define-configuration/no-serialization oci-container-configuration
+ (user
+ (string "oci-container")
+ "The user under whose authority docker commands will be run.")
+ (group
+ (string "docker")
+ "The group under whose authority docker commands will be run.")
+ (command
+ (list-of-strings '())
+ "Overwrite the default command (@code{CMD}) of the image.")
+ (entrypoint
+ (maybe-string)
+ "Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
+ (host-environment
+ (list '())
+ "Set environment variables in the host environment where @command{docker run}
+is invoked. This is especially useful to pass secrets from the host to the
+container without having them on the @command{docker run}'s command line: by
+setting the @code{MYSQL_PASSWORD} on the host and by passing
+@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
+possible to securely set values in the container environment. This field's
+value can be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to @code{make-forkexec-constructor}."
+ (sanitizer oci-sanitize-host-environment))
+ (environment
+ (list '())
+ "Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-environment))
+ (image
+ (string-or-oci-image)
+ "The image used to build the container. It can be a string or an
+@code{oci-image} record. Strings are resolved by the Docker
+Engine, and follow the usual format
+@code{myregistry.local:5000/testing/test-image:tag}.")
+ (provision
+ (maybe-string)
+ "Set the name of the provisioned Shepherd service.")
+ (requirement
+ (list-of-symbols '())
+ "Set additional Shepherd services dependencies to the provisioned Shepherd
+service.")
+ (log-file
+ (maybe-string)
+ "When @code{log-file} is set, it names the file to which the service’s
+standard output and standard error are redirected. @code{log-file} is created
+if it does not exist, otherwise it is appended to.")
+ (auto-start?
+ (boolean #t)
+ "Whether this service should be started automatically by the Shepherd. If it
+is @code{#f} the service has to be started manually with @command{herd start}.")
+ (respawn?
+ (boolean #f)
+ "Whether to restart the service when it stops, for instance when the
+underlying process dies.")
+ (shepherd-actions
+ (list '())
+ "This is a list of @code{shepherd-action} records defining actions supported
+by the service."
+ (sanitizer oci-sanitize-shepherd-actions))
+ (network
+ (maybe-string)
+ "Set a Docker network for the spawned container.")
+ (ports
+ (list '())
+ "Set the port or port ranges to expose from the spawned container. This can
+be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"8080\" . \"80\")
+ \"10443:443\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-ports))
+ (volumes
+ (list '())
+ "Set volume mappings for the spawned container. This can be a
+list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
+ \"/gnu/store:/gnu/store\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-volumes))
+ (container-user
+ (maybe-string)
+ "Set the current user inside the spawned container. You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#user,upstream}
+documentation for semantics.")
+ (workdir
+ (maybe-string)
+ "Set the current working for the spawned Shepherd service.
+You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
+documentation for semantics.")
+ (extra-arguments
+ (list '())
+ "A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} invokation."
+ (sanitizer oci-sanitize-extra-arguments)))
+
+(define oci-container-configuration->options
+ (lambda (config)
+ (let ((entrypoint
+ (oci-container-configuration-entrypoint config))
+ (network
+ (oci-container-configuration-network config))
+ (user
+ (oci-container-configuration-container-user config))
+ (workdir
+ (oci-container-configuration-workdir config)))
+ (apply append
+ (filter (compose not unspecified?)
+ `(,(if (maybe-value-set? entrypoint)
+ `("--entrypoint" ,entrypoint)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "--env" spec))
+ (oci-container-configuration-environment config))
+ ,(if (maybe-value-set? network)
+ `("--network" ,network)
+ '())
+ ,(if (maybe-value-set? user)
+ `("--user" ,user)
+ '())
+ ,(if (maybe-value-set? workdir)
+ `("--workdir" ,workdir)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "-p" spec))
+ (oci-container-configuration-ports config))
+ ,(append-map
+ (lambda (spec)
+ (list "-v" spec))
+ (oci-container-configuration-volumes config))))))))
+
+(define* (get-keyword-value args keyword #:key (default #f))
+ (let ((kv (memq keyword args)))
+ (if (and kv (>= (length kv) 2))
+ (cadr kv)
+ default)))
+
+(define (lower-operating-system os target system)
+ (mlet* %store-monad
+ ((tarball
+ (lower-object
+ (system-image (os->image os #:type docker-image-type))
+ system
+ #:target target)))
+ (return tarball)))
+
+(define (lower-manifest name image target system)
+ (define value (oci-image-value image))
+ (define options (oci-image-pack-options image))
+ (define image-reference
+ (oci-image-reference image))
+ (define image-tag
+ (let* ((extra-options
+ (get-keyword-value options #:extra-options))
+ (image-tag-option
+ (and extra-options
+ (get-keyword-value extra-options #:image-tag))))
+ (if image-tag-option
+ '()
+ `(#:extra-options (#:image-tag ,image-reference)))))
+
+ (mlet* %store-monad
+ ((_ (set-grafting
+ (oci-image-grafts? image)))
+ (guile (set-guile-for-build (default-guile)))
+ (profile
+ (profile-derivation value
+ #:target target
+ #:system system
+ #:hooks '()
+ #:locales? #f))
+ (tarball (apply pack:docker-image
+ `(,name ,profile
+ ,@options
+ ,@image-tag
+ #:localstatedir? #t))))
+ (return tarball)))
+
+(define (lower-oci-image name image)
+ (define value (oci-image-value image))
+ (define image-target (oci-image-target image))
+ (define image-system (oci-image-system image))
+ (define target
+ (if (maybe-value-set? image-target)
+ image-target
+ (%current-target-system)))
+ (define system
+ (if (maybe-value-set? image-system)
+ image-system
+ (%current-system)))
+ (with-store store
+ (run-with-store store
+ (match value
+ ((? manifest? value)
+ (lower-manifest name image target system))
+ ((? operating-system? value)
+ (lower-operating-system value target system))
+ ((or (? gexp? value)
+ (? file-like? value))
+ value)
+ (_
+ (raise
+ (formatted-message
+ (G_ "oci-image value must contain only manifest,
+operating-system, gexp or file-like records but ~a was found")
+ value))))
+ #:target target
+ #:system system)))
+
+(define (%oci-image-loader name image tag)
+ (let ((docker (file-append docker-cli "/bin/docker"))
+ (tarball (lower-oci-image name image)))
+ (with-imported-modules '((guix build utils))
+ (program-file (format #f "~a-image-loader" name)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+
+ (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
+ (define line
+ (read-line
+ (open-input-pipe
+ (string-append #$docker " load -i " #$tarball))))
+
+ (unless (or (eof-object? line)
+ (string-null? line))
+ (format #t "~a~%" line)
+ (let ((repository&tag
+ (string-drop line
+ (string-length
+ "Loaded image: "))))
+
+ (invoke #$docker "tag" repository&tag #$tag)
+ (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
+
+(define (oci-container-shepherd-service config)
+ (define (guess-name name image)
+ (if (maybe-value-set? name)
+ name
+ (string-append "docker-"
+ (basename
+ (if (string? image)
+ (first (string-split image #\:))
+ (oci-image-repository image))))))
+
+ (let* ((docker (file-append docker-cli "/bin/docker"))
+ (actions (oci-container-configuration-shepherd-actions config))
+ (auto-start?
+ (oci-container-configuration-auto-start? config))
+ (user (oci-container-configuration-user config))
+ (group (oci-container-configuration-group config))
+ (host-environment
+ (oci-container-configuration-host-environment config))
+ (command (oci-container-configuration-command config))
+ (log-file (oci-container-configuration-log-file config))
+ (provision (oci-container-configuration-provi
This message was truncated. Download the full message here.
P
Re: OCI provisioning service
(address . 76081@debbugs.gnu.org)
da407350-a2e1-482b-a034-ddead598fa6b@autistici.org
Hi,

On 2/12/25 02:09, paul wrote:
Toggle quote (32 lines)
> Hi guix,
>
> On 2/9/25 21:38, paul wrote:
>> I'm sending a v3 fixing a bug in the merge algorithm for volumes and
>> networks.
>>
>> On 2/9/25 20:14, paul wrote:
>>> Hi,
>>>
>>> I'm about to send a v2. v2 compared to the first revision features:
>>>
>>>
>>> - it actually compiles all the times :) (rev 1 referenced oci-image
>>> too early for it to be working and generated a compile time error,
>>> if you recompiled it sometimes went away so I thought it was a
>>> problem of my setup. CI caught this)
>>> - it allows more values to be overridden by eventual users of the
>>> Scheme API
>>> - it allows passing extra arguments directly after each podman or
>>> docker invokation, allowing for example for overriding podman --root
>>> and similar options.
>>>
>>> All of these tests should pass:
>>>
>>> guix shell -D guix -CPW -- make check-system TESTS="oci-container
>>> oci-service-rootless-podman docker docker-system rootless-podman
>>> oci-service-docker"
>>>
> I'm sending a v4 changing slightly the image loader, the same tests as
> before are supposed to pass. Now the Home service [0] is working for
> me with rootless podman. I'll try it on different distros if I manage to.

I'm sending a v5 implementing a Home service. The changes compared to v4
are pretty trivial as the plumbing was already there, the only downside
is that I'm not able to use for-home? in define-configuration, so I had
to reimplement oci-configuration with (guix records) and had to
reimplement some validation (gnu services configuration) would figure
out magically.

Thank you for your work and time,

cheers
giacomo
G
G
Giacomo Leidi wrote on 18 Feb 16:09 -0800
[PATCH v5 3/5] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
a12ad3347114d37c91ebba6820654b46da31e21b.1739923762.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/services/containers.scm (%oci-supported-runtimes): New variable;
(oci-runtime-cli): new variable;
(oci-runtime-name): new variable;
(oci-network-configuration): new variable;
(oci-volume-configuration): new variable;
(oci-configuration): new variable;
(oci-extension): new variable;
(oci-networks-shepherd-name): new variable;
(oci-service-type): new variable;
(oci-state->shepherd-services): new variable.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 306 +++++++--
gnu/services/containers.scm | 1283 ++++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 37 +-
gnu/tests/containers.scm | 999 ++++++++++++++++++++++++++-
4 files changed, 2380 insertions(+), 245 deletions(-)

Toggle diff (451 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 54a736c518c..0dfec66a52b 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -42282,59 +42282,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@command{docker run} or @command{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources. When unset it will default to @code{docker-cli}
+package for the @code{'docker} runtime or to @code{podman} package for the
+@code{'podman} runtime. When a string is passed it will be interpreted as the
+absolute file-system path of the selected OCI runtime command.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -42354,16 +42457,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @command{docker run}
+or @command{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -42371,22 +42474,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -42414,7 +42519,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -42425,10 +42530,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -42439,25 +42545,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} or @command{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker network create} or @command{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker volume create} or @command{podman volume create}
+invokation.
@end table
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 50bf6c05549..bd1b6f14356 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -39,8 +39,10 @@ (define-module (gnu services containers)
#:use-module (guix packages)
#:use-module (guix profiles)
#:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix records)
#:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
@@ -96,8 +98,80 @@ (define-module (gnu services containers)
oci-container-configuration-workdir
oci-container-configuration-extra-arguments
+ list-of-oci-containers?
+ list-of-oci-networks?
+ list-of-oci-volumes?
+
+ %oci-supported-runtimes
+ oci-sanitize-runtime
+ oci-runtime-system-environment
+ oci-runtime-system-extra-arguments
+ oci-runtime-system-group
+ oci-runtime-system-requirement
+ oci-runtime-cli
+ oci-runtime-system-cli
+ oci-runtime-home-cli
+ oci-runtime-name
+ oci-runtime-group
+
+ oci-network-configuration
+ oci-network-configuration?
+ oci-network-configuration-fields
+ oci-network-configuration-name
+ oci-network-configuration-driver
+ oci-network-configuration-gateway
+ oci-network-configuration-internal?
+ oci-network-configuration-ip-range
+ oci-network-configuration-ipam-driver
+ oci-network-configuration-ipv6?
+ oci-network-configuration-subnet
+ oci-network-configuration-labels
+ oci-network-configuration-extra-arguments
+
+ oci-volume-configuration
+ oci-volume-configuration?
+ oci-volume-configuration-fields
+ oci-volume-configuration-name
+ oci-volume-configuration-labels
+ oci-volume-configuration-extra-arguments
+
+ oci-configuration
+ oci-configuration?
+ oci-configuration-runtime
+ oci-c
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 18 Feb 16:09 -0800
[PATCH v5 1/5] services: rootless-podman: Use login shell.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
6d774afae43df08bf52f7ecb8900d35f501280d8.1739923762.git.goodoldpaul@autistici.org
This commit allows for having PATH set when changing the owner of
/sys/fs/group.

* gnu/services/containers.scm (crgroups-fs-owner): Use login shell.

Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
---
gnu/services/containers.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Toggle diff (17 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 19d35ccbcb6..dc66ac4f967 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
(rootless-podman-configuration-group-name config))
(program-file "cgroups2-fs-owner-entrypoint"
#~(system*
- (string-append #+bash-minimal "/bin/bash") "-c"
+ (string-append #+bash-minimal "/bin/bash") "-l" "-c"
(string-append "echo Setting /sys/fs/cgroup "
"group ownership to " #$group " && chown -v "
"root:" #$group " /sys/fs/cgroup && "

base-commit: 3dc8026d58f9480547a595450a6483e0f13c1ba4
--
2.48.1
G
G
Giacomo Leidi wrote on 18 Feb 16:09 -0800
[PATCH v5 4/5] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
3fa0aa07079ab537bdd5b214803d56e08c426092.1739923762.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/services/containers.scm: Export lower-oci-image-state.
* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/services/containers.scm | 2 +
gnu/tests/containers.scm | 80 ++++++++++++++---------------
gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
3 files changed, 95 insertions(+), 87 deletions(-)

Toggle diff (274 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index bd1b6f14356..8b57ac57798 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -75,6 +75,8 @@ (define-module (gnu services containers)
oci-image-system
oci-image-grafts?
+ lower-oci-image-state
+
oci-container-configuration
oci-container-configuration?
oci-container-configuration-fields
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 5e6f39387e7..8cdd86e7ae3 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -69,13 +69,47 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-rootless-podman-os
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..07edd9d5341 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -57,6 +58,40 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +208,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +217,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +349,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.48.1
G
G
Giacomo Leidi wrote on 18 Feb 16:09 -0800
[PATCH v5 5/5] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
ec9bac788ce132ac4e306e3e47cb9bd2849346f7.1739923762.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 50 ++++++++++++++
gnu/local.mk | 1 +
3 files changed, 165 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (195 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 0dfec66a52b..eacca409473 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -49826,6 +49826,120 @@ Miscellaneous Home Services
(dicod-configuration @dots{})))
@end lisp
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 00000000000..938dde2f37a
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,50 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (oci-service-extension-wrap-validate
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli)))))
+ (service-extension home-shepherd-service-type
+ (oci-service-extension-wrap-validate
+ oci-configuration->shepherd-services))))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 01926bb1b8b..37ab59a3ba1 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -102,6 +102,7 @@ GNU_SYSTEM_MODULES = \
%D%/home.scm \
%D%/home/services.scm \
%D%/home/services/admin.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.48.1
G
G
Giacomo Leidi wrote on 18 Feb 16:09 -0800
[PATCH v5 2/5] services: oci-container-configuration: Move to (gnu services containers).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
cbdcfa5c8fcfbbbb4b843fc50b624171a5da5f6a.1739923762.git.goodoldpaul@autistici.org
This patch moves the oci-container-configuration and related
configuration records to (gnu services containers).
Public symbols are still exported for backwards
compatibility but since the oci-container-service-type will be
deprecated in favor of the more general oci-service-type, everything is
moved outside of the docker related module.

* gnu/services/docker.scm: Move everything related to oci-container-configuration
to...
* gnu/services/containers.scm: ...here.scm.
* gnu/tests/docker.scm: Simplify %test-oci-container test case.

Change-Id: Iae599dd5cc7442eb632f0c1b3b12f6b928397ae7
---
gnu/services/containers.scm | 549 +++++++++++++++++++++++++++++++++-
gnu/services/docker.scm | 577 +++---------------------------------
gnu/tests/docker.scm | 99 +++----
3 files changed, 625 insertions(+), 600 deletions(-)

Toggle diff (534 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index dc66ac4f967..50bf6c05549 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,19 +17,31 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services containers)
+ #:use-module (gnu image)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages containers)
+ #:use-module (gnu packages docker)
#:use-module (gnu packages file-systems)
#:use-module (gnu services)
#:use-module (gnu services base)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu system)
#:use-module (gnu system accounts)
+ #:use-module (gnu system image)
#:use-module (gnu system shadow)
#:use-module (gnu system pam)
+ #:use-module (guix diagnostics)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
+ #:use-module (guix monads)
#:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
rootless-podman-configuration-fields
@@ -48,7 +60,44 @@ (define-module (gnu services containers)
rootless-podman-shepherd-services
rootless-podman-service-etc
- rootless-podman-service-type))
+ rootless-podman-service-type
+
+ oci-image
+ oci-image?
+ oci-image-fields
+ oci-image-repository
+ oci-image-tag
+ oci-image-value
+ oci-image-pack-options
+ oci-image-target
+ oci-image-system
+ oci-image-grafts?
+
+ oci-container-configuration
+ oci-container-configuration?
+ oci-container-configuration-fields
+ oci-container-configuration-user
+ oci-container-configuration-group
+ oci-container-configuration-command
+ oci-container-configuration-entrypoint
+ oci-container-configuration-host-environment
+ oci-container-configuration-environment
+ oci-container-configuration-image
+ oci-container-configuration-provision
+ oci-container-configuration-requirement
+ oci-container-configuration-log-file
+ oci-container-configuration-auto-start?
+ oci-container-configuration-respawn?
+ oci-container-configuration-shepherd-actions
+ oci-container-configuration-network
+ oci-container-configuration-ports
+ oci-container-configuration-volumes
+ oci-container-configuration-container-user
+ oci-container-configuration-workdir
+ oci-container-configuration-extra-arguments
+
+ oci-container-shepherd-service
+ %oci-container-accounts))
(define (gexp-or-string? value)
(or (gexp? value)
@@ -188,7 +237,7 @@ (define (rootless-podman-cgroups-limits-service config)
rootless-podman-shared-root-fs))
(one-shot? #t)
(documentation
- "Allow setting cgroups limits: cpu, cpuset, memory and
+ "Allow setting cgroups limits: cpu, cpuset, io, memory and
pids.")
(start
#~(make-forkexec-constructor
@@ -242,3 +291,497 @@ (define rootless-podman-service-type
(default-value (rootless-podman-configuration))
(description
"This service configures rootless @code{podman} on the Guix System.")))
+
+
+;;;
+;;; OCI container.
+;;;
+
+(define (oci-sanitize-pair pair delimiter)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (match pair
+ (((? valid? key) . (? valid? value))
+ #~(string-append #$key #$delimiter #$value))
+ (_
+ (raise
+ (formatted-message
+ (G_ "pair members must contain only strings, gexps or file-like objects
+but ~a was found")
+ pair)))))
+
+(define (oci-sanitize-mixed-list name value delimiter)
+ (map
+ (lambda (el)
+ (cond ((string? el) el)
+ ((pair? el) (oci-sanitize-pair el delimiter))
+ (else
+ (raise
+ (formatted-message
+ (G_ "~a members must be either a string or a pair but ~a was
+found!")
+ name el)))))
+ value))
+
+(define (oci-sanitize-host-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "host-environment" value "="))
+
+(define (oci-sanitize-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "environment" value "="))
+
+(define (oci-sanitize-ports value)
+ ;; Expected spec format:
+ ;; '(("8088" . "80") "2022:22")
+ (oci-sanitize-mixed-list "ports" value ":"))
+
+(define (oci-sanitize-volumes value)
+ ;; Expected spec format:
+ ;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
+ (oci-sanitize-mixed-list "volumes" value ":"))
+
+(define (oci-sanitize-shepherd-actions value)
+ (map
+ (lambda (el)
+ (if (shepherd-action? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "shepherd-actions may only be shepherd-action records
+but ~a was found") el))))
+ value))
+
+(define (oci-sanitize-extra-arguments value)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (map
+ (lambda (el)
+ (if (valid? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "extra arguments may only be strings, gexps or file-like objects
+but ~a was found") el))))
+ value))
+
+(define (oci-image-reference image)
+ (if (string? image)
+ image
+ (string-append (oci-image-repository image)
+ ":" (oci-image-tag image))))
+
+(define (oci-lowerable-image? image)
+ (or (manifest? image)
+ (operating-system? image)
+ (gexp? image)
+ (file-like? image)))
+
+(define (string-or-oci-image? image)
+ (or (string? image)
+ (oci-image? image)))
+
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe/no-serialization string)
+
+(define-configuration/no-serialization oci-image
+ (repository
+ (string)
+ "A string like @code{myregistry.local:5000/testing/test-image} that names
+the OCI image.")
+ (tag
+ (string "latest")
+ "A string representing the OCI image tag. Defaults to @code{latest}.")
+ (value
+ (oci-lowerable-image)
+ "A @code{manifest} or @code{operating-system} record that will be lowered
+into an OCI compatible tarball. Otherwise this field's value can be a gexp
+or a file-like object that evaluates to an OCI compatible tarball.")
+ (pack-options
+ (list '())
+ "An optional set of keyword arguments that will be passed to the
+@code{docker-image} procedure from @code{guix scripts pack}. They can be used
+to replicate @command{guix pack} behavior:
+
+@lisp
+(oci-image
+ (repository \"guile\")
+ (tag \"3\")
+ (manifest (specifications->manifest '(\"guile\")))
+ (pack-options
+ '(#:symlinks ((\"/bin/guile\" -> \"bin/guile\"))
+ #:max-layers 2)))
+@end lisp
+
+If the @code{value} field is an @code{operating-system} record, this field's
+value will be ignored.")
+ (system
+ (maybe-string)
+ "Attempt to build for a given system, e.g. \"i686-linux\"")
+ (target
+ (maybe-string)
+ "Attempt to cross-build for a given triple, e.g. \"aarch64-linux-gnu\"")
+ (grafts?
+ (boolean #f)
+ "Whether to allow grafting or not in the pack build."))
+
+(define-configuration/no-serialization oci-container-configuration
+ (user
+ (string "oci-container")
+ "The user under whose authority docker commands will be run.")
+ (group
+ (string "docker")
+ "The group under whose authority docker commands will be run.")
+ (command
+ (list-of-strings '())
+ "Overwrite the default command (@code{CMD}) of the image.")
+ (entrypoint
+ (maybe-string)
+ "Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
+ (host-environment
+ (list '())
+ "Set environment variables in the host environment where @command{docker run}
+is invoked. This is especially useful to pass secrets from the host to the
+container without having them on the @command{docker run}'s command line: by
+setting the @code{MYSQL_PASSWORD} on the host and by passing
+@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
+possible to securely set values in the container environment. This field's
+value can be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to @code{make-forkexec-constructor}."
+ (sanitizer oci-sanitize-host-environment))
+ (environment
+ (list '())
+ "Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-environment))
+ (image
+ (string-or-oci-image)
+ "The image used to build the container. It can be a string or an
+@code{oci-image} record. Strings are resolved by the Docker
+Engine, and follow the usual format
+@code{myregistry.local:5000/testing/test-image:tag}.")
+ (provision
+ (maybe-string)
+ "Set the name of the provisioned Shepherd service.")
+ (requirement
+ (list-of-symbols '())
+ "Set additional Shepherd services dependencies to the provisioned Shepherd
+service.")
+ (log-file
+ (maybe-string)
+ "When @code{log-file} is set, it names the file to which the service’s
+standard output and standard error are redirected. @code{log-file} is created
+if it does not exist, otherwise it is appended to.")
+ (auto-start?
+ (boolean #t)
+ "Whether this service should be started automatically by the Shepherd. If it
+is @code{#f} the service has to be started manually with @command{herd start}.")
+ (respawn?
+ (boolean #f)
+ "Whether to restart the service when it stops, for instance when the
+underlying process dies.")
+ (shepherd-actions
+ (list '())
+ "This is a list of @code{shepherd-action} records defining actions supported
+by the service."
+ (sanitizer oci-sanitize-shepherd-actions))
+ (network
+ (maybe-string)
+ "Set a Docker network for the spawned container.")
+ (ports
+ (list '())
+ "Set the port or port ranges to expose from the spawned container. This can
+be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"8080\" . \"80\")
+ \"10443:443\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-ports))
+ (volumes
+ (list '())
+ "Set volume mappings for the spawned container. This can be a
+list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
+ \"/gnu/store:/gnu/store\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-volumes))
+ (container-user
+ (maybe-string)
+ "Set the current user inside the spawned container. You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#user,upstream}
+documentation for semantics.")
+ (workdir
+ (maybe-string)
+ "Set the current working for the spawned Shepherd service.
+You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
+documentation for semantics.")
+ (extra-arguments
+ (list '())
+ "A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} invokation."
+ (sanitizer oci-sanitize-extra-arguments)))
+
+(define oci-container-configuration->options
+ (lambda (config)
+ (let ((entrypoint
+ (oci-container-configuration-entrypoint config))
+ (network
+ (oci-container-configuration-network config))
+ (user
+ (oci-container-configuration-container-user config))
+ (workdir
+ (oci-container-configuration-workdir config)))
+ (apply append
+ (filter (compose not unspecified?)
+ `(,(if (maybe-value-set? entrypoint)
+ `("--entrypoint" ,entrypoint)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "--env" spec))
+ (oci-container-configuration-environment config))
+ ,(if (maybe-value-set? network)
+ `("--network" ,network)
+ '())
+ ,(if (maybe-value-set? user)
+ `("--user" ,user)
+ '())
+ ,(if (maybe-value-set? workdir)
+ `("--workdir" ,workdir)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "-p" spec))
+ (oci-container-configuration-ports config))
+ ,(append-map
+ (lambda (spec)
+ (list "-v" spec))
+ (oci-container-configuration-volumes config))))))))
+
+(define* (get-keyword-value args keyword #:key (default #f))
+ (let ((kv (memq keyword args)))
+ (if (and kv (>= (length kv) 2))
+ (cadr kv)
+ default)))
+
+(define (lower-operating-system os target system)
+ (mlet* %store-monad
+ ((tarball
+ (lower-object
+ (system-image (os->image os #:type docker-image-type))
+ system
+ #:target target)))
+ (return tarball)))
+
+(define (lower-manifest name image target system)
+ (define value (oci-image-value image))
+ (define options (oci-image-pack-options image))
+ (define image-reference
+ (oci-image-reference image))
+ (define image-tag
+ (let* ((extra-options
+ (get-keyword-value options #:extra-options))
+ (image-tag-option
+ (and extra-options
+ (get-keyword-value extra-options #:image-tag))))
+ (if image-tag-option
+ '()
+ `(#:extra-options (#:image-tag ,image-reference)))))
+
+ (mlet* %store-monad
+ ((_ (set-grafting
+ (oci-image-grafts? image)))
+ (guile (set-guile-for-build (default-guile)))
+ (profile
+ (profile-derivation value
+ #:target target
+ #:system system
+ #:hooks '()
+ #:locales? #f))
+ (tarball (apply pack:docker-image
+ `(,name ,profile
+ ,@options
+ ,@image-tag
+ #:localstatedir? #t))))
+ (return tarball)))
+
+(define (lower-oci-image name image)
+ (define value (oci-image-value image))
+ (define image-target (oci-image-target image))
+ (define image-system (oci-image-system image))
+ (define target
+ (if (maybe-value-set? image-target)
+ image-target
+ (%current-target-system)))
+ (define system
+ (if (maybe-value-set? image-system)
+ image-system
+ (%current-system)))
+ (with-store store
+ (run-with-store store
+ (match value
+ ((? manifest? value)
+ (lower-manifest name image target system))
+ ((? operating-system? value)
+ (lower-operating-system value target system))
+ ((or (? gexp? value)
+ (? file-like? value))
+ value)
+ (_
+ (raise
+ (formatted-message
+ (G_ "oci-image value must contain only manifest,
+operating-system, gexp or file-like records but ~a was found")
+ value))))
+ #:target target
+ #:system system)))
+
+(define (%oci-image-loader name image tag)
+ (let ((docker (file-append docker-cli "/bin/docker"))
+ (tarball (lower-oci-image name image)))
+ (with-imported-modules '((guix build utils))
+ (program-file (format #f "~a-image-loader" name)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+
+ (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
+ (define line
+ (read-line
+ (open-input-pipe
+ (string-append #$docker " load -i " #$tarball))))
+
+ (unless (or (eof-object? line)
+ (string-null? line))
+ (format #t "~a~%" line)
+ (let ((repository&tag
+ (string-drop line
+ (string-length
+ "Loaded image: "))))
+
+ (invoke #$docker "tag" repository&tag #$tag)
+ (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
+
+(define (oci-container-shepherd-service config)
+ (define (guess-name name image)
+ (if (maybe-value-set? name)
+ name
+ (string-append "docker-"
+ (basename
+ (if (string? image)
+ (first (string-split image #\:))
+ (oci-image-repository image))))))
+
+ (let* ((docker (file-append docker-cli "/bin/docker"))
+ (actions (oci-container-configuration-shepherd-actions config))
+ (auto-start?
+ (oci-container-configuration-auto-start? config))
+ (user (oci-container-configuration-user config))
+ (group (oci-container-configuration-group config))
+ (host-environment
+ (oci-container-configuration-host-environment config))
+ (command (oci-container-configuration-command config))
+ (log-file (oci-container-configuration-log-file config))
+ (provision (oci-container-configuration-provi
This message was truncated. Download the full message here.
P
Re: OCI provisioning service
(address . 76081@debbugs.gnu.org)
397afc1f-b638-430a-b9e7-7de4721bca45@autistici.org
Hi guix,

On 2/18/25 02:20, paul wrote:
Toggle quote (43 lines)
> Hi,
>
> On 2/12/25 02:09, paul wrote:
>> Hi guix,
>>
>> On 2/9/25 21:38, paul wrote:
>>> I'm sending a v3 fixing a bug in the merge algorithm for volumes and
>>> networks.
>>>
>>> On 2/9/25 20:14, paul wrote:
>>>> Hi,
>>>>
>>>> I'm about to send a v2. v2 compared to the first revision features:
>>>>
>>>>
>>>> - it actually compiles all the times :) (rev 1 referenced oci-image
>>>> too early for it to be working and generated a compile time error,
>>>> if you recompiled it sometimes went away so I thought it was a
>>>> problem of my setup. CI caught this)
>>>> - it allows more values to be overridden by eventual users of the
>>>> Scheme API
>>>> - it allows passing extra arguments directly after each podman or
>>>> docker invokation, allowing for example for overriding podman
>>>> --root and similar options.
>>>>
>>>> All of these tests should pass:
>>>>
>>>> guix shell -D guix -CPW -- make check-system TESTS="oci-container
>>>> oci-service-rootless-podman docker docker-system rootless-podman
>>>> oci-service-docker"
>>>>
>> I'm sending a v4 changing slightly the image loader, the same tests
>> as before are supposed to pass. Now the Home service [0] is working
>> for me with rootless podman. I'll try it on different distros if I
>> manage to.
>
> I'm sending a v5 implementing a Home service. The changes compared to
> v4 are pretty trivial as the plumbing was already there, the only
> downside is that I'm not able to use for-home? in
> define-configuration, so I had to reimplement oci-configuration with
> (guix records) and had to reimplement some validation (gnu services
> configuration) would figure out magically.

I'm sending  a v6. Compared to v5 it resolves the conflicts with master
and it should fix the stop action for rootless podman backed services.

Thank you for all your work,

giacomo
G
G
Giacomo Leidi wrote on 26 Feb 15:08 -0800
[PATCH v6 1/5] services: rootless-podman: Use login shell.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
0ba285c5241f6817250b4af71776072faf248a14.1740611290.git.goodoldpaul@autistici.org
This commit allows for having PATH set when changing the owner of
/sys/fs/group.

* gnu/services/containers.scm (crgroups-fs-owner): Use login shell.

Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
---
gnu/services/containers.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Toggle diff (17 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index b3cd109ce6c..d5a211765a6 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
(rootless-podman-configuration-group-name config))
(program-file "cgroups2-fs-owner-entrypoint"
#~(system*
- (string-append #+bash-minimal "/bin/bash") "-c"
+ (string-append #+bash-minimal "/bin/bash") "-l" "-c"
(string-append "echo Setting /sys/fs/cgroup "
"group ownership to " #$group " && chown -v "
"root:" #$group " /sys/fs/cgroup && "

base-commit: 4f220482de742c9c03cf6378ab147026a330edd0
--
2.48.1
G
G
Giacomo Leidi wrote on 26 Feb 15:08 -0800
[PATCH v6 3/5] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
584a7fb04ad1be743e3935ea59aaec8883dbf0f4.1740611290.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/services/containers.scm (%oci-supported-runtimes): New variable;
(oci-runtime-cli): new variable;
(oci-runtime-name): new variable;
(oci-network-configuration): new variable;
(oci-volume-configuration): new variable;
(oci-configuration): new variable;
(oci-extension): new variable;
(oci-networks-shepherd-name): new variable;
(oci-service-type): new variable;
(oci-state->shepherd-services): new variable.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 306 ++++++--
gnu/services/containers.scm | 1314 ++++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 37 +-
gnu/tests/containers.scm | 999 +++++++++++++++++++++++++-
4 files changed, 2404 insertions(+), 252 deletions(-)

Toggle diff (451 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index a036c85c31a..fd08261af3b 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -42325,59 +42325,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@command{docker run} or @command{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources. When unset it will default to @code{docker-cli}
+package for the @code{'docker} runtime or to @code{podman} package for the
+@code{'podman} runtime. When a string is passed it will be interpreted as the
+absolute file-system path of the selected OCI runtime command.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -42397,16 +42500,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @command{docker run}
+or @command{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -42414,22 +42517,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -42457,7 +42562,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -42468,10 +42573,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -42482,25 +42588,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} or @command{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker network create} or @command{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker volume create} or @command{podman volume create}
+invokation.
@end table
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 24f31c756b8..603eec25e2b 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -39,8 +39,10 @@ (define-module (gnu services containers)
#:use-module (guix packages)
#:use-module (guix profiles)
#:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix records)
#:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
@@ -96,8 +98,80 @@ (define-module (gnu services containers)
oci-container-configuration-workdir
oci-container-configuration-extra-arguments
+ list-of-oci-containers?
+ list-of-oci-networks?
+ list-of-oci-volumes?
+
+ %oci-supported-runtimes
+ oci-sanitize-runtime
+ oci-runtime-system-environment
+ oci-runtime-system-extra-arguments
+ oci-runtime-system-group
+ oci-runtime-system-requirement
+ oci-runtime-cli
+ oci-runtime-system-cli
+ oci-runtime-home-cli
+ oci-runtime-name
+ oci-runtime-group
+
+ oci-network-configuration
+ oci-network-configuration?
+ oci-network-configuration-fields
+ oci-network-configuration-name
+ oci-network-configuration-driver
+ oci-network-configuration-gateway
+ oci-network-configuration-internal?
+ oci-network-configuration-ip-range
+ oci-network-configuration-ipam-driver
+ oci-network-configuration-ipv6?
+ oci-network-configuration-subnet
+ oci-network-configuration-labels
+ oci-network-configuration-extra-arguments
+
+ oci-volume-configuration
+ oci-volume-configuration?
+ oci-volume-configuration-fields
+ oci-volume-configuration-name
+ oci-volume-configuration-labels
+ oci-volume-configuration-extra-arguments
+
+ oci-configuration
+ oci-configuration?
+ oci-configuration-runtime
+ oci-con
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 26 Feb 15:08 -0800
[PATCH v6 4/5] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
7d3f6521d783c726f555d906881624eed5a523c2.1740611290.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/services/containers.scm: Export lower-oci-image-state.
* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/services/containers.scm | 2 +
gnu/tests/containers.scm | 80 ++++++++++++++---------------
gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
3 files changed, 95 insertions(+), 87 deletions(-)

Toggle diff (274 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 603eec25e2b..139d5e4ec50 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -75,6 +75,8 @@ (define-module (gnu services containers)
oci-image-system
oci-image-grafts?
+ lower-oci-image-state
+
oci-container-configuration
oci-container-configuration?
oci-container-configuration-fields
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 5e6f39387e7..8cdd86e7ae3 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -69,13 +69,47 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-rootless-podman-os
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..07edd9d5341 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -57,6 +58,40 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +208,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +217,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +349,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.48.1
G
G
Giacomo Leidi wrote on 26 Feb 15:08 -0800
[PATCH v6 5/5] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
bd9961d8d23b364ea1cc14cb1869f0dacfdc39a8.1740611290.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 50 ++++++++++++++
gnu/local.mk | 1 +
3 files changed, 165 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (195 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index fd08261af3b..5513eb89048 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -49876,6 +49876,120 @@ Miscellaneous Home Services
(dicod-configuration @dots{})))
@end lisp
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 00000000000..938dde2f37a
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,50 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (oci-service-extension-wrap-validate
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli)))))
+ (service-extension home-shepherd-service-type
+ (oci-service-extension-wrap-validate
+ oci-configuration->shepherd-services))))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index c8a29bf98b5..9b86e59c37f 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -102,6 +102,7 @@ GNU_SYSTEM_MODULES = \
%D%/home.scm \
%D%/home/services.scm \
%D%/home/services/admin.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.48.1
G
G
Giacomo Leidi wrote on 26 Feb 15:08 -0800
[PATCH v6 2/5] services: oci-container-configuration: Move to (gnu services containers).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
4b79084a63c98ff9b15c925af93455ef1f1c1fd1.1740611290.git.goodoldpaul@autistici.org
This patch moves the oci-container-configuration and related
configuration records to (gnu services containers).
Public symbols are still exported for backwards
compatibility but since the oci-container-service-type will be
deprecated in favor of the more general oci-service-type, everything is
moved outside of the docker related module.

* gnu/services/docker.scm: Move everything related to oci-container-configuration
to...
* gnu/services/containers.scm: ...here.scm.
* gnu/tests/docker.scm: Simplify %test-oci-container test case.

Change-Id: Iae599dd5cc7442eb632f0c1b3b12f6b928397ae7
---
gnu/services/containers.scm | 549 +++++++++++++++++++++++++++++++++-
gnu/services/docker.scm | 577 +++---------------------------------
gnu/tests/docker.scm | 99 +++----
3 files changed, 625 insertions(+), 600 deletions(-)

Toggle diff (534 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index d5a211765a6..24f31c756b8 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,19 +17,31 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services containers)
+ #:use-module (gnu image)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages containers)
+ #:use-module (gnu packages docker)
#:use-module (gnu packages file-systems)
#:use-module (gnu services)
#:use-module (gnu services base)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu system)
#:use-module (gnu system accounts)
+ #:use-module (gnu system image)
#:use-module (gnu system shadow)
#:use-module (gnu system pam)
+ #:use-module (guix diagnostics)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
+ #:use-module (guix monads)
#:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
rootless-podman-configuration-fields
@@ -48,7 +60,44 @@ (define-module (gnu services containers)
rootless-podman-shepherd-services
rootless-podman-service-etc
- rootless-podman-service-type))
+ rootless-podman-service-type
+
+ oci-image
+ oci-image?
+ oci-image-fields
+ oci-image-repository
+ oci-image-tag
+ oci-image-value
+ oci-image-pack-options
+ oci-image-target
+ oci-image-system
+ oci-image-grafts?
+
+ oci-container-configuration
+ oci-container-configuration?
+ oci-container-configuration-fields
+ oci-container-configuration-user
+ oci-container-configuration-group
+ oci-container-configuration-command
+ oci-container-configuration-entrypoint
+ oci-container-configuration-host-environment
+ oci-container-configuration-environment
+ oci-container-configuration-image
+ oci-container-configuration-provision
+ oci-container-configuration-requirement
+ oci-container-configuration-log-file
+ oci-container-configuration-auto-start?
+ oci-container-configuration-respawn?
+ oci-container-configuration-shepherd-actions
+ oci-container-configuration-network
+ oci-container-configuration-ports
+ oci-container-configuration-volumes
+ oci-container-configuration-container-user
+ oci-container-configuration-workdir
+ oci-container-configuration-extra-arguments
+
+ oci-container-shepherd-service
+ %oci-container-accounts))
(define (gexp-or-string? value)
(or (gexp? value)
@@ -190,7 +239,7 @@ (define (rootless-podman-cgroups-limits-service config)
rootless-podman-shared-root-fs))
(one-shot? #t)
(documentation
- "Allow setting cgroups limits: cpu, cpuset, memory and
+ "Allow setting cgroups limits: cpu, cpuset, io, memory and
pids.")
(start
#~(make-forkexec-constructor
@@ -244,3 +293,497 @@ (define rootless-podman-service-type
(default-value (rootless-podman-configuration))
(description
"This service configures rootless @code{podman} on the Guix System.")))
+
+
+;;;
+;;; OCI container.
+;;;
+
+(define (oci-sanitize-pair pair delimiter)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (match pair
+ (((? valid? key) . (? valid? value))
+ #~(string-append #$key #$delimiter #$value))
+ (_
+ (raise
+ (formatted-message
+ (G_ "pair members must contain only strings, gexps or file-like objects
+but ~a was found")
+ pair)))))
+
+(define (oci-sanitize-mixed-list name value delimiter)
+ (map
+ (lambda (el)
+ (cond ((string? el) el)
+ ((pair? el) (oci-sanitize-pair el delimiter))
+ (else
+ (raise
+ (formatted-message
+ (G_ "~a members must be either a string or a pair but ~a was
+found!")
+ name el)))))
+ value))
+
+(define (oci-sanitize-host-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "host-environment" value "="))
+
+(define (oci-sanitize-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "environment" value "="))
+
+(define (oci-sanitize-ports value)
+ ;; Expected spec format:
+ ;; '(("8088" . "80") "2022:22")
+ (oci-sanitize-mixed-list "ports" value ":"))
+
+(define (oci-sanitize-volumes value)
+ ;; Expected spec format:
+ ;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
+ (oci-sanitize-mixed-list "volumes" value ":"))
+
+(define (oci-sanitize-shepherd-actions value)
+ (map
+ (lambda (el)
+ (if (shepherd-action? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "shepherd-actions may only be shepherd-action records
+but ~a was found") el))))
+ value))
+
+(define (oci-sanitize-extra-arguments value)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (map
+ (lambda (el)
+ (if (valid? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "extra arguments may only be strings, gexps or file-like objects
+but ~a was found") el))))
+ value))
+
+(define (oci-image-reference image)
+ (if (string? image)
+ image
+ (string-append (oci-image-repository image)
+ ":" (oci-image-tag image))))
+
+(define (oci-lowerable-image? image)
+ (or (manifest? image)
+ (operating-system? image)
+ (gexp? image)
+ (file-like? image)))
+
+(define (string-or-oci-image? image)
+ (or (string? image)
+ (oci-image? image)))
+
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe/no-serialization string)
+
+(define-configuration/no-serialization oci-image
+ (repository
+ (string)
+ "A string like @code{myregistry.local:5000/testing/test-image} that names
+the OCI image.")
+ (tag
+ (string "latest")
+ "A string representing the OCI image tag. Defaults to @code{latest}.")
+ (value
+ (oci-lowerable-image)
+ "A @code{manifest} or @code{operating-system} record that will be lowered
+into an OCI compatible tarball. Otherwise this field's value can be a gexp
+or a file-like object that evaluates to an OCI compatible tarball.")
+ (pack-options
+ (list '())
+ "An optional set of keyword arguments that will be passed to the
+@code{docker-image} procedure from @code{guix scripts pack}. They can be used
+to replicate @command{guix pack} behavior:
+
+@lisp
+(oci-image
+ (repository \"guile\")
+ (tag \"3\")
+ (manifest (specifications->manifest '(\"guile\")))
+ (pack-options
+ '(#:symlinks ((\"/bin/guile\" -> \"bin/guile\"))
+ #:max-layers 2)))
+@end lisp
+
+If the @code{value} field is an @code{operating-system} record, this field's
+value will be ignored.")
+ (system
+ (maybe-string)
+ "Attempt to build for a given system, e.g. \"i686-linux\"")
+ (target
+ (maybe-string)
+ "Attempt to cross-build for a given triple, e.g. \"aarch64-linux-gnu\"")
+ (grafts?
+ (boolean #f)
+ "Whether to allow grafting or not in the pack build."))
+
+(define-configuration/no-serialization oci-container-configuration
+ (user
+ (string "oci-container")
+ "The user under whose authority docker commands will be run.")
+ (group
+ (string "docker")
+ "The group under whose authority docker commands will be run.")
+ (command
+ (list-of-strings '())
+ "Overwrite the default command (@code{CMD}) of the image.")
+ (entrypoint
+ (maybe-string)
+ "Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
+ (host-environment
+ (list '())
+ "Set environment variables in the host environment where @command{docker run}
+is invoked. This is especially useful to pass secrets from the host to the
+container without having them on the @command{docker run}'s command line: by
+setting the @code{MYSQL_PASSWORD} on the host and by passing
+@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
+possible to securely set values in the container environment. This field's
+value can be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to @code{make-forkexec-constructor}."
+ (sanitizer oci-sanitize-host-environment))
+ (environment
+ (list '())
+ "Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-environment))
+ (image
+ (string-or-oci-image)
+ "The image used to build the container. It can be a string or an
+@code{oci-image} record. Strings are resolved by the Docker
+Engine, and follow the usual format
+@code{myregistry.local:5000/testing/test-image:tag}.")
+ (provision
+ (maybe-string)
+ "Set the name of the provisioned Shepherd service.")
+ (requirement
+ (list-of-symbols '())
+ "Set additional Shepherd services dependencies to the provisioned Shepherd
+service.")
+ (log-file
+ (maybe-string)
+ "When @code{log-file} is set, it names the file to which the service’s
+standard output and standard error are redirected. @code{log-file} is created
+if it does not exist, otherwise it is appended to.")
+ (auto-start?
+ (boolean #t)
+ "Whether this service should be started automatically by the Shepherd. If it
+is @code{#f} the service has to be started manually with @command{herd start}.")
+ (respawn?
+ (boolean #f)
+ "Whether to restart the service when it stops, for instance when the
+underlying process dies.")
+ (shepherd-actions
+ (list '())
+ "This is a list of @code{shepherd-action} records defining actions supported
+by the service."
+ (sanitizer oci-sanitize-shepherd-actions))
+ (network
+ (maybe-string)
+ "Set a Docker network for the spawned container.")
+ (ports
+ (list '())
+ "Set the port or port ranges to expose from the spawned container. This can
+be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"8080\" . \"80\")
+ \"10443:443\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-ports))
+ (volumes
+ (list '())
+ "Set volume mappings for the spawned container. This can be a
+list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
+ \"/gnu/store:/gnu/store\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-volumes))
+ (container-user
+ (maybe-string)
+ "Set the current user inside the spawned container. You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#user,upstream}
+documentation for semantics.")
+ (workdir
+ (maybe-string)
+ "Set the current working for the spawned Shepherd service.
+You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
+documentation for semantics.")
+ (extra-arguments
+ (list '())
+ "A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} invokation."
+ (sanitizer oci-sanitize-extra-arguments)))
+
+(define oci-container-configuration->options
+ (lambda (config)
+ (let ((entrypoint
+ (oci-container-configuration-entrypoint config))
+ (network
+ (oci-container-configuration-network config))
+ (user
+ (oci-container-configuration-container-user config))
+ (workdir
+ (oci-container-configuration-workdir config)))
+ (apply append
+ (filter (compose not unspecified?)
+ `(,(if (maybe-value-set? entrypoint)
+ `("--entrypoint" ,entrypoint)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "--env" spec))
+ (oci-container-configuration-environment config))
+ ,(if (maybe-value-set? network)
+ `("--network" ,network)
+ '())
+ ,(if (maybe-value-set? user)
+ `("--user" ,user)
+ '())
+ ,(if (maybe-value-set? workdir)
+ `("--workdir" ,workdir)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "-p" spec))
+ (oci-container-configuration-ports config))
+ ,(append-map
+ (lambda (spec)
+ (list "-v" spec))
+ (oci-container-configuration-volumes config))))))))
+
+(define* (get-keyword-value args keyword #:key (default #f))
+ (let ((kv (memq keyword args)))
+ (if (and kv (>= (length kv) 2))
+ (cadr kv)
+ default)))
+
+(define (lower-operating-system os target system)
+ (mlet* %store-monad
+ ((tarball
+ (lower-object
+ (system-image (os->image os #:type docker-image-type))
+ system
+ #:target target)))
+ (return tarball)))
+
+(define (lower-manifest name image target system)
+ (define value (oci-image-value image))
+ (define options (oci-image-pack-options image))
+ (define image-reference
+ (oci-image-reference image))
+ (define image-tag
+ (let* ((extra-options
+ (get-keyword-value options #:extra-options))
+ (image-tag-option
+ (and extra-options
+ (get-keyword-value extra-options #:image-tag))))
+ (if image-tag-option
+ '()
+ `(#:extra-options (#:image-tag ,image-reference)))))
+
+ (mlet* %store-monad
+ ((_ (set-grafting
+ (oci-image-grafts? image)))
+ (guile (set-guile-for-build (default-guile)))
+ (profile
+ (profile-derivation value
+ #:target target
+ #:system system
+ #:hooks '()
+ #:locales? #f))
+ (tarball (apply pack:docker-image
+ `(,name ,profile
+ ,@options
+ ,@image-tag
+ #:localstatedir? #t))))
+ (return tarball)))
+
+(define (lower-oci-image name image)
+ (define value (oci-image-value image))
+ (define image-target (oci-image-target image))
+ (define image-system (oci-image-system image))
+ (define target
+ (if (maybe-value-set? image-target)
+ image-target
+ (%current-target-system)))
+ (define system
+ (if (maybe-value-set? image-system)
+ image-system
+ (%current-system)))
+ (with-store store
+ (run-with-store store
+ (match value
+ ((? manifest? value)
+ (lower-manifest name image target system))
+ ((? operating-system? value)
+ (lower-operating-system value target system))
+ ((or (? gexp? value)
+ (? file-like? value))
+ value)
+ (_
+ (raise
+ (formatted-message
+ (G_ "oci-image value must contain only manifest,
+operating-system, gexp or file-like records but ~a was found")
+ value))))
+ #:target target
+ #:system system)))
+
+(define (%oci-image-loader name image tag)
+ (let ((docker (file-append docker-cli "/bin/docker"))
+ (tarball (lower-oci-image name image)))
+ (with-imported-modules '((guix build utils))
+ (program-file (format #f "~a-image-loader" name)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+
+ (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
+ (define line
+ (read-line
+ (open-input-pipe
+ (string-append #$docker " load -i " #$tarball))))
+
+ (unless (or (eof-object? line)
+ (string-null? line))
+ (format #t "~a~%" line)
+ (let ((repository&tag
+ (string-drop line
+ (string-length
+ "Loaded image: "))))
+
+ (invoke #$docker "tag" repository&tag #$tag)
+ (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
+
+(define (oci-container-shepherd-service config)
+ (define (guess-name name image)
+ (if (maybe-value-set? name)
+ name
+ (string-append "docker-"
+ (basename
+ (if (string? image)
+ (first (string-split image #\:))
+ (oci-image-repository image))))))
+
+ (let* ((docker (file-append docker-cli "/bin/docker"))
+ (actions (oci-container-configuration-shepherd-actions config))
+ (auto-start?
+ (oci-container-configuration-auto-start? config))
+ (user (oci-container-configuration-user config))
+ (group (oci-container-configuration-group config))
+ (host-environment
+ (oci-container-configuration-host-environment config))
+ (command (oci-container-configuration-command config))
+ (log-file (oci-container-configuration-log-file config))
+ (provision (oci-container-configuration-provi
This message was truncated. Download the full message here.
P
Re: OCI provisioning service
(address . 76081@debbugs.gnu.org)
a559945e-925c-46f5-a591-19154712b269@autistici.org
Hi,

On 2/27/25 00:01, paul wrote:
Toggle quote (50 lines)
> Hi guix,
>
> On 2/18/25 02:20, paul wrote:
>> Hi,
>>
>> On 2/12/25 02:09, paul wrote:
>>> Hi guix,
>>>
>>> On 2/9/25 21:38, paul wrote:
>>>> I'm sending a v3 fixing a bug in the merge algorithm for volumes
>>>> and networks.
>>>>
>>>> On 2/9/25 20:14, paul wrote:
>>>>> Hi,
>>>>>
>>>>> I'm about to send a v2. v2 compared to the first revision features:
>>>>>
>>>>>
>>>>> - it actually compiles all the times :) (rev 1 referenced
>>>>> oci-image too early for it to be working and generated a compile
>>>>> time error, if you recompiled it sometimes went away so I thought
>>>>> it was a problem of my setup. CI caught this)
>>>>> - it allows more values to be overridden by eventual users of the
>>>>> Scheme API
>>>>> - it allows passing extra arguments directly after each podman or
>>>>> docker invokation, allowing for example for overriding podman
>>>>> --root and similar options.
>>>>>
>>>>> All of these tests should pass:
>>>>>
>>>>> guix shell -D guix -CPW -- make check-system TESTS="oci-container
>>>>> oci-service-rootless-podman docker docker-system rootless-podman
>>>>> oci-service-docker"
>>>>>
>>> I'm sending a v4 changing slightly the image loader, the same tests
>>> as before are supposed to pass. Now the Home service [0] is working
>>> for me with rootless podman. I'll try it on different distros if I
>>> manage to.
>>
>> I'm sending a v5 implementing a Home service. The changes compared to
>> v4 are pretty trivial as the plumbing was already there, the only
>> downside is that I'm not able to use for-home? in
>> define-configuration, so I had to reimplement oci-configuration with
>> (guix records) and had to reimplement some validation (gnu services
>> configuration) would figure out magically.
>
> I'm sending  a v6. Compared to v5 it resolves the conflicts with
> master and it should fix the stop action for rootless podman backed
> services.

I'm sending a v7 fixing the restart action for podman backed services.


Thank you for your work,


giacomo
G
G
Giacomo Leidi wrote on 4 Mar 04:40 -0800
[PATCH v7 3/5] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
88bcb0d883db7ad2b1c1a997a3a4e852ef386ea1.1741092002.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/services/containers.scm (%oci-supported-runtimes): New variable;
(oci-runtime-cli): new variable;
(oci-runtime-name): new variable;
(oci-network-configuration): new variable;
(oci-volume-configuration): new variable;
(oci-configuration): new variable;
(oci-extension): new variable;
(oci-networks-shepherd-name): new variable;
(oci-service-type): new variable;
(oci-state->shepherd-services): new variable.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 306 ++++++--
gnu/services/containers.scm | 1320 ++++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 37 +-
gnu/tests/containers.scm | 999 +++++++++++++++++++++++++-
4 files changed, 2410 insertions(+), 252 deletions(-)

Toggle diff (451 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 551bc52f7f6..be5c8cf8fad 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -42727,59 +42727,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@command{docker run} or @command{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources. When unset it will default to @code{docker-cli}
+package for the @code{'docker} runtime or to @code{podman} package for the
+@code{'podman} runtime. When a string is passed it will be interpreted as the
+absolute file-system path of the selected OCI runtime command.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -42799,16 +42902,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @command{docker run}
+or @command{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -42816,22 +42919,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -42859,7 +42964,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -42870,10 +42975,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -42884,25 +42990,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} or @command{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker network create} or @command{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker volume create} or @command{podman volume create}
+invokation.
@end table
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 24f31c756b8..4600846ac3d 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -39,8 +39,10 @@ (define-module (gnu services containers)
#:use-module (guix packages)
#:use-module (guix profiles)
#:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix records)
#:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
@@ -96,8 +98,80 @@ (define-module (gnu services containers)
oci-container-configuration-workdir
oci-container-configuration-extra-arguments
+ list-of-oci-containers?
+ list-of-oci-networks?
+ list-of-oci-volumes?
+
+ %oci-supported-runtimes
+ oci-sanitize-runtime
+ oci-runtime-system-environment
+ oci-runtime-system-extra-arguments
+ oci-runtime-system-group
+ oci-runtime-system-requirement
+ oci-runtime-cli
+ oci-runtime-system-cli
+ oci-runtime-home-cli
+ oci-runtime-name
+ oci-runtime-group
+
+ oci-network-configuration
+ oci-network-configuration?
+ oci-network-configuration-fields
+ oci-network-configuration-name
+ oci-network-configuration-driver
+ oci-network-configuration-gateway
+ oci-network-configuration-internal?
+ oci-network-configuration-ip-range
+ oci-network-configuration-ipam-driver
+ oci-network-configuration-ipv6?
+ oci-network-configuration-subnet
+ oci-network-configuration-labels
+ oci-network-configuration-extra-arguments
+
+ oci-volume-configuration
+ oci-volume-configuration?
+ oci-volume-configuration-fields
+ oci-volume-configuration-name
+ oci-volume-configuration-labels
+ oci-volume-configuration-extra-arguments
+
+ oci-configuration
+ oci-configuration?
+ oci-configuration-runtime
+ oci-con
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 4 Mar 04:40 -0800
[PATCH v7 4/5] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
eecc4358d0e2a0e37fb37789c812183923cd263f.1741092002.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/services/containers.scm: Export lower-oci-image-state.
* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/services/containers.scm | 2 +
gnu/tests/containers.scm | 80 ++++++++++++++---------------
gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
3 files changed, 95 insertions(+), 87 deletions(-)

Toggle diff (274 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 4600846ac3d..57b14868f1a 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -75,6 +75,8 @@ (define-module (gnu services containers)
oci-image-system
oci-image-grafts?
+ lower-oci-image-state
+
oci-container-configuration
oci-container-configuration?
oci-container-configuration-fields
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 5e6f39387e7..8cdd86e7ae3 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -69,13 +69,47 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-rootless-podman-os
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..07edd9d5341 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -57,6 +58,40 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +208,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +217,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +349,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.48.1
G
G
Giacomo Leidi wrote on 4 Mar 04:39 -0800
[PATCH v7 1/5] services: rootless-podman: Use login shell.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
ec7962db7e8300a129f7836ce44733dffb435a1c.1741092002.git.goodoldpaul@autistici.org
This commit allows for having PATH set when changing the owner of
/sys/fs/group.

* gnu/services/containers.scm (crgroups-fs-owner): Use login shell.

Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
---
gnu/services/containers.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Toggle diff (17 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index b3cd109ce6c..d5a211765a6 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
(rootless-podman-configuration-group-name config))
(program-file "cgroups2-fs-owner-entrypoint"
#~(system*
- (string-append #+bash-minimal "/bin/bash") "-c"
+ (string-append #+bash-minimal "/bin/bash") "-l" "-c"
(string-append "echo Setting /sys/fs/cgroup "
"group ownership to " #$group " && chown -v "
"root:" #$group " /sys/fs/cgroup && "

base-commit: 72923a75af53a819f2be9dc4ae3c096aa3147d3f
--
2.48.1
G
G
Giacomo Leidi wrote on 4 Mar 04:40 -0800
[PATCH v7 5/5] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
cacc5fec364e79bba8074bd40792ee9ce14cc089.1741092002.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 50 ++++++++++++++
gnu/local.mk | 1 +
3 files changed, 165 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (195 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index be5c8cf8fad..6c09f929049 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -50288,6 +50288,120 @@ Miscellaneous Home Services
(dicod-configuration @dots{})))
@end lisp
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 00000000000..938dde2f37a
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,50 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (oci-service-extension-wrap-validate
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli)))))
+ (service-extension home-shepherd-service-type
+ (oci-service-extension-wrap-validate
+ oci-configuration->shepherd-services))))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index a7a3238669d..d48e767154c 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -103,6 +103,7 @@ GNU_SYSTEM_MODULES = \
%D%/home.scm \
%D%/home/services.scm \
%D%/home/services/admin.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.48.1
G
G
Giacomo Leidi wrote on 4 Mar 04:39 -0800
[PATCH v7 2/5] services: oci-container-configuration: Move to (gnu services containers).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
e6437536b0e550b1a4cfdff08ddb114b9d016609.1741092002.git.goodoldpaul@autistici.org
This patch moves the oci-container-configuration and related
configuration records to (gnu services containers).
Public symbols are still exported for backwards
compatibility but since the oci-container-service-type will be
deprecated in favor of the more general oci-service-type, everything is
moved outside of the docker related module.

* gnu/services/docker.scm: Move everything related to oci-container-configuration
to...
* gnu/services/containers.scm: ...here.scm.
* gnu/tests/docker.scm: Simplify %test-oci-container test case.

Change-Id: Iae599dd5cc7442eb632f0c1b3b12f6b928397ae7
---
gnu/services/containers.scm | 549 +++++++++++++++++++++++++++++++++-
gnu/services/docker.scm | 577 +++---------------------------------
gnu/tests/docker.scm | 99 +++----
3 files changed, 625 insertions(+), 600 deletions(-)

Toggle diff (534 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index d5a211765a6..24f31c756b8 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,19 +17,31 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services containers)
+ #:use-module (gnu image)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages containers)
+ #:use-module (gnu packages docker)
#:use-module (gnu packages file-systems)
#:use-module (gnu services)
#:use-module (gnu services base)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu system)
#:use-module (gnu system accounts)
+ #:use-module (gnu system image)
#:use-module (gnu system shadow)
#:use-module (gnu system pam)
+ #:use-module (guix diagnostics)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
+ #:use-module (guix monads)
#:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
rootless-podman-configuration-fields
@@ -48,7 +60,44 @@ (define-module (gnu services containers)
rootless-podman-shepherd-services
rootless-podman-service-etc
- rootless-podman-service-type))
+ rootless-podman-service-type
+
+ oci-image
+ oci-image?
+ oci-image-fields
+ oci-image-repository
+ oci-image-tag
+ oci-image-value
+ oci-image-pack-options
+ oci-image-target
+ oci-image-system
+ oci-image-grafts?
+
+ oci-container-configuration
+ oci-container-configuration?
+ oci-container-configuration-fields
+ oci-container-configuration-user
+ oci-container-configuration-group
+ oci-container-configuration-command
+ oci-container-configuration-entrypoint
+ oci-container-configuration-host-environment
+ oci-container-configuration-environment
+ oci-container-configuration-image
+ oci-container-configuration-provision
+ oci-container-configuration-requirement
+ oci-container-configuration-log-file
+ oci-container-configuration-auto-start?
+ oci-container-configuration-respawn?
+ oci-container-configuration-shepherd-actions
+ oci-container-configuration-network
+ oci-container-configuration-ports
+ oci-container-configuration-volumes
+ oci-container-configuration-container-user
+ oci-container-configuration-workdir
+ oci-container-configuration-extra-arguments
+
+ oci-container-shepherd-service
+ %oci-container-accounts))
(define (gexp-or-string? value)
(or (gexp? value)
@@ -190,7 +239,7 @@ (define (rootless-podman-cgroups-limits-service config)
rootless-podman-shared-root-fs))
(one-shot? #t)
(documentation
- "Allow setting cgroups limits: cpu, cpuset, memory and
+ "Allow setting cgroups limits: cpu, cpuset, io, memory and
pids.")
(start
#~(make-forkexec-constructor
@@ -244,3 +293,497 @@ (define rootless-podman-service-type
(default-value (rootless-podman-configuration))
(description
"This service configures rootless @code{podman} on the Guix System.")))
+
+
+;;;
+;;; OCI container.
+;;;
+
+(define (oci-sanitize-pair pair delimiter)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (match pair
+ (((? valid? key) . (? valid? value))
+ #~(string-append #$key #$delimiter #$value))
+ (_
+ (raise
+ (formatted-message
+ (G_ "pair members must contain only strings, gexps or file-like objects
+but ~a was found")
+ pair)))))
+
+(define (oci-sanitize-mixed-list name value delimiter)
+ (map
+ (lambda (el)
+ (cond ((string? el) el)
+ ((pair? el) (oci-sanitize-pair el delimiter))
+ (else
+ (raise
+ (formatted-message
+ (G_ "~a members must be either a string or a pair but ~a was
+found!")
+ name el)))))
+ value))
+
+(define (oci-sanitize-host-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "host-environment" value "="))
+
+(define (oci-sanitize-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "environment" value "="))
+
+(define (oci-sanitize-ports value)
+ ;; Expected spec format:
+ ;; '(("8088" . "80") "2022:22")
+ (oci-sanitize-mixed-list "ports" value ":"))
+
+(define (oci-sanitize-volumes value)
+ ;; Expected spec format:
+ ;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
+ (oci-sanitize-mixed-list "volumes" value ":"))
+
+(define (oci-sanitize-shepherd-actions value)
+ (map
+ (lambda (el)
+ (if (shepherd-action? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "shepherd-actions may only be shepherd-action records
+but ~a was found") el))))
+ value))
+
+(define (oci-sanitize-extra-arguments value)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (map
+ (lambda (el)
+ (if (valid? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "extra arguments may only be strings, gexps or file-like objects
+but ~a was found") el))))
+ value))
+
+(define (oci-image-reference image)
+ (if (string? image)
+ image
+ (string-append (oci-image-repository image)
+ ":" (oci-image-tag image))))
+
+(define (oci-lowerable-image? image)
+ (or (manifest? image)
+ (operating-system? image)
+ (gexp? image)
+ (file-like? image)))
+
+(define (string-or-oci-image? image)
+ (or (string? image)
+ (oci-image? image)))
+
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe/no-serialization string)
+
+(define-configuration/no-serialization oci-image
+ (repository
+ (string)
+ "A string like @code{myregistry.local:5000/testing/test-image} that names
+the OCI image.")
+ (tag
+ (string "latest")
+ "A string representing the OCI image tag. Defaults to @code{latest}.")
+ (value
+ (oci-lowerable-image)
+ "A @code{manifest} or @code{operating-system} record that will be lowered
+into an OCI compatible tarball. Otherwise this field's value can be a gexp
+or a file-like object that evaluates to an OCI compatible tarball.")
+ (pack-options
+ (list '())
+ "An optional set of keyword arguments that will be passed to the
+@code{docker-image} procedure from @code{guix scripts pack}. They can be used
+to replicate @command{guix pack} behavior:
+
+@lisp
+(oci-image
+ (repository \"guile\")
+ (tag \"3\")
+ (manifest (specifications->manifest '(\"guile\")))
+ (pack-options
+ '(#:symlinks ((\"/bin/guile\" -> \"bin/guile\"))
+ #:max-layers 2)))
+@end lisp
+
+If the @code{value} field is an @code{operating-system} record, this field's
+value will be ignored.")
+ (system
+ (maybe-string)
+ "Attempt to build for a given system, e.g. \"i686-linux\"")
+ (target
+ (maybe-string)
+ "Attempt to cross-build for a given triple, e.g. \"aarch64-linux-gnu\"")
+ (grafts?
+ (boolean #f)
+ "Whether to allow grafting or not in the pack build."))
+
+(define-configuration/no-serialization oci-container-configuration
+ (user
+ (string "oci-container")
+ "The user under whose authority docker commands will be run.")
+ (group
+ (string "docker")
+ "The group under whose authority docker commands will be run.")
+ (command
+ (list-of-strings '())
+ "Overwrite the default command (@code{CMD}) of the image.")
+ (entrypoint
+ (maybe-string)
+ "Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
+ (host-environment
+ (list '())
+ "Set environment variables in the host environment where @command{docker run}
+is invoked. This is especially useful to pass secrets from the host to the
+container without having them on the @command{docker run}'s command line: by
+setting the @code{MYSQL_PASSWORD} on the host and by passing
+@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
+possible to securely set values in the container environment. This field's
+value can be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to @code{make-forkexec-constructor}."
+ (sanitizer oci-sanitize-host-environment))
+ (environment
+ (list '())
+ "Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-environment))
+ (image
+ (string-or-oci-image)
+ "The image used to build the container. It can be a string or an
+@code{oci-image} record. Strings are resolved by the Docker
+Engine, and follow the usual format
+@code{myregistry.local:5000/testing/test-image:tag}.")
+ (provision
+ (maybe-string)
+ "Set the name of the provisioned Shepherd service.")
+ (requirement
+ (list-of-symbols '())
+ "Set additional Shepherd services dependencies to the provisioned Shepherd
+service.")
+ (log-file
+ (maybe-string)
+ "When @code{log-file} is set, it names the file to which the service’s
+standard output and standard error are redirected. @code{log-file} is created
+if it does not exist, otherwise it is appended to.")
+ (auto-start?
+ (boolean #t)
+ "Whether this service should be started automatically by the Shepherd. If it
+is @code{#f} the service has to be started manually with @command{herd start}.")
+ (respawn?
+ (boolean #f)
+ "Whether to restart the service when it stops, for instance when the
+underlying process dies.")
+ (shepherd-actions
+ (list '())
+ "This is a list of @code{shepherd-action} records defining actions supported
+by the service."
+ (sanitizer oci-sanitize-shepherd-actions))
+ (network
+ (maybe-string)
+ "Set a Docker network for the spawned container.")
+ (ports
+ (list '())
+ "Set the port or port ranges to expose from the spawned container. This can
+be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"8080\" . \"80\")
+ \"10443:443\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-ports))
+ (volumes
+ (list '())
+ "Set volume mappings for the spawned container. This can be a
+list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
+ \"/gnu/store:/gnu/store\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-volumes))
+ (container-user
+ (maybe-string)
+ "Set the current user inside the spawned container. You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#user,upstream}
+documentation for semantics.")
+ (workdir
+ (maybe-string)
+ "Set the current working for the spawned Shepherd service.
+You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
+documentation for semantics.")
+ (extra-arguments
+ (list '())
+ "A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} invokation."
+ (sanitizer oci-sanitize-extra-arguments)))
+
+(define oci-container-configuration->options
+ (lambda (config)
+ (let ((entrypoint
+ (oci-container-configuration-entrypoint config))
+ (network
+ (oci-container-configuration-network config))
+ (user
+ (oci-container-configuration-container-user config))
+ (workdir
+ (oci-container-configuration-workdir config)))
+ (apply append
+ (filter (compose not unspecified?)
+ `(,(if (maybe-value-set? entrypoint)
+ `("--entrypoint" ,entrypoint)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "--env" spec))
+ (oci-container-configuration-environment config))
+ ,(if (maybe-value-set? network)
+ `("--network" ,network)
+ '())
+ ,(if (maybe-value-set? user)
+ `("--user" ,user)
+ '())
+ ,(if (maybe-value-set? workdir)
+ `("--workdir" ,workdir)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "-p" spec))
+ (oci-container-configuration-ports config))
+ ,(append-map
+ (lambda (spec)
+ (list "-v" spec))
+ (oci-container-configuration-volumes config))))))))
+
+(define* (get-keyword-value args keyword #:key (default #f))
+ (let ((kv (memq keyword args)))
+ (if (and kv (>= (length kv) 2))
+ (cadr kv)
+ default)))
+
+(define (lower-operating-system os target system)
+ (mlet* %store-monad
+ ((tarball
+ (lower-object
+ (system-image (os->image os #:type docker-image-type))
+ system
+ #:target target)))
+ (return tarball)))
+
+(define (lower-manifest name image target system)
+ (define value (oci-image-value image))
+ (define options (oci-image-pack-options image))
+ (define image-reference
+ (oci-image-reference image))
+ (define image-tag
+ (let* ((extra-options
+ (get-keyword-value options #:extra-options))
+ (image-tag-option
+ (and extra-options
+ (get-keyword-value extra-options #:image-tag))))
+ (if image-tag-option
+ '()
+ `(#:extra-options (#:image-tag ,image-reference)))))
+
+ (mlet* %store-monad
+ ((_ (set-grafting
+ (oci-image-grafts? image)))
+ (guile (set-guile-for-build (default-guile)))
+ (profile
+ (profile-derivation value
+ #:target target
+ #:system system
+ #:hooks '()
+ #:locales? #f))
+ (tarball (apply pack:docker-image
+ `(,name ,profile
+ ,@options
+ ,@image-tag
+ #:localstatedir? #t))))
+ (return tarball)))
+
+(define (lower-oci-image name image)
+ (define value (oci-image-value image))
+ (define image-target (oci-image-target image))
+ (define image-system (oci-image-system image))
+ (define target
+ (if (maybe-value-set? image-target)
+ image-target
+ (%current-target-system)))
+ (define system
+ (if (maybe-value-set? image-system)
+ image-system
+ (%current-system)))
+ (with-store store
+ (run-with-store store
+ (match value
+ ((? manifest? value)
+ (lower-manifest name image target system))
+ ((? operating-system? value)
+ (lower-operating-system value target system))
+ ((or (? gexp? value)
+ (? file-like? value))
+ value)
+ (_
+ (raise
+ (formatted-message
+ (G_ "oci-image value must contain only manifest,
+operating-system, gexp or file-like records but ~a was found")
+ value))))
+ #:target target
+ #:system system)))
+
+(define (%oci-image-loader name image tag)
+ (let ((docker (file-append docker-cli "/bin/docker"))
+ (tarball (lower-oci-image name image)))
+ (with-imported-modules '((guix build utils))
+ (program-file (format #f "~a-image-loader" name)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+
+ (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
+ (define line
+ (read-line
+ (open-input-pipe
+ (string-append #$docker " load -i " #$tarball))))
+
+ (unless (or (eof-object? line)
+ (string-null? line))
+ (format #t "~a~%" line)
+ (let ((repository&tag
+ (string-drop line
+ (string-length
+ "Loaded image: "))))
+
+ (invoke #$docker "tag" repository&tag #$tag)
+ (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
+
+(define (oci-container-shepherd-service config)
+ (define (guess-name name image)
+ (if (maybe-value-set? name)
+ name
+ (string-append "docker-"
+ (basename
+ (if (string? image)
+ (first (string-split image #\:))
+ (oci-image-repository image))))))
+
+ (let* ((docker (file-append docker-cli "/bin/docker"))
+ (actions (oci-container-configuration-shepherd-actions config))
+ (auto-start?
+ (oci-container-configuration-auto-start? config))
+ (user (oci-container-configuration-user config))
+ (group (oci-container-configuration-group config))
+ (host-environment
+ (oci-container-configuration-host-environment config))
+ (command (oci-container-configuration-command config))
+ (log-file (oci-container-configuration-log-file config))
+ (provision (oci-container-configuration-provi
This message was truncated. Download the full message here.
P
Re: OCI provisioning service
(address . 76081@debbugs.gnu.org)
afa88170-8a5e-42a3-b024-caeef44da51f@autistici.org
Hi,

On 3/4/25 13:39, paul wrote:
Toggle quote (55 lines)
> Hi,
>
> On 2/27/25 00:01, paul wrote:
>> Hi guix,
>>
>> On 2/18/25 02:20, paul wrote:
>>> Hi,
>>>
>>> On 2/12/25 02:09, paul wrote:
>>>> Hi guix,
>>>>
>>>> On 2/9/25 21:38, paul wrote:
>>>>> I'm sending a v3 fixing a bug in the merge algorithm for volumes
>>>>> and networks.
>>>>>
>>>>> On 2/9/25 20:14, paul wrote:
>>>>>> Hi,
>>>>>>
>>>>>> I'm about to send a v2. v2 compared to the first revision features:
>>>>>>
>>>>>>
>>>>>> - it actually compiles all the times :) (rev 1 referenced
>>>>>> oci-image too early for it to be working and generated a compile
>>>>>> time error, if you recompiled it sometimes went away so I thought
>>>>>> it was a problem of my setup. CI caught this)
>>>>>> - it allows more values to be overridden by eventual users of the
>>>>>> Scheme API
>>>>>> - it allows passing extra arguments directly after each podman or
>>>>>> docker invokation, allowing for example for overriding podman
>>>>>> --root and similar options.
>>>>>>
>>>>>> All of these tests should pass:
>>>>>>
>>>>>> guix shell -D guix -CPW -- make check-system TESTS="oci-container
>>>>>> oci-service-rootless-podman docker docker-system rootless-podman
>>>>>> oci-service-docker"
>>>>>>
>>>> I'm sending a v4 changing slightly the image loader, the same tests
>>>> as before are supposed to pass. Now the Home service [0] is working
>>>> for me with rootless podman. I'll try it on different distros if I
>>>> manage to.
>>>
>>> I'm sending a v5 implementing a Home service. The changes compared
>>> to v4 are pretty trivial as the plumbing was already there, the only
>>> downside is that I'm not able to use for-home? in
>>> define-configuration, so I had to reimplement oci-configuration with
>>> (guix records) and had to reimplement some validation (gnu services
>>> configuration) would figure out magically.
>>
>> I'm sending  a v6. Compared to v5 it resolves the conflicts with
>> master and it should fix the stop action for rootless podman backed
>> services.
>
> I'm sending a v7 fixing the restart action for podman backed services.

I'm sending a v8 fixing some further bugs wrt to the start and stop of
podman backed services.

Thank you for your work,

cheers

giacomo
G
G
Giacomo Leidi wrote on 8 Mar 17:06 -0800
[PATCH v8 3/5] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
c2943e4b72e89e50daadf0a9e30562096bbe3b77.1741482375.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/services/containers.scm (%oci-supported-runtimes): New variable;
(oci-runtime-cli): new variable;
(oci-runtime-name): new variable;
(oci-network-configuration): new variable;
(oci-volume-configuration): new variable;
(oci-configuration): new variable;
(oci-extension): new variable;
(oci-networks-shepherd-name): new variable;
(oci-service-type): new variable;
(oci-state->shepherd-services): new variable.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 306 ++++++--
gnu/services/containers.scm | 1335 ++++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 37 +-
gnu/tests/containers.scm | 999 +++++++++++++++++++++++++-
4 files changed, 2425 insertions(+), 252 deletions(-)

Toggle diff (451 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 7d8a5243ed8..8686380669b 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -42839,59 +42839,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@command{docker run} or @command{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources. When unset it will default to @code{docker-cli}
+package for the @code{'docker} runtime or to @code{podman} package for the
+@code{'podman} runtime. When a string is passed it will be interpreted as the
+absolute file-system path of the selected OCI runtime command.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -42911,16 +43014,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @command{docker run}
+or @command{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -42928,22 +43031,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -42971,7 +43076,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -42982,10 +43087,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -42996,25 +43102,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} or @command{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker network create} or @command{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker volume create} or @command{podman volume create}
+invokation.
@end table
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 24f31c756b8..a78be00f038 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -39,8 +39,10 @@ (define-module (gnu services containers)
#:use-module (guix packages)
#:use-module (guix profiles)
#:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix records)
#:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
@@ -96,8 +98,80 @@ (define-module (gnu services containers)
oci-container-configuration-workdir
oci-container-configuration-extra-arguments
+ list-of-oci-containers?
+ list-of-oci-networks?
+ list-of-oci-volumes?
+
+ %oci-supported-runtimes
+ oci-sanitize-runtime
+ oci-runtime-system-environment
+ oci-runtime-system-extra-arguments
+ oci-runtime-system-group
+ oci-runtime-system-requirement
+ oci-runtime-cli
+ oci-runtime-system-cli
+ oci-runtime-home-cli
+ oci-runtime-name
+ oci-runtime-group
+
+ oci-network-configuration
+ oci-network-configuration?
+ oci-network-configuration-fields
+ oci-network-configuration-name
+ oci-network-configuration-driver
+ oci-network-configuration-gateway
+ oci-network-configuration-internal?
+ oci-network-configuration-ip-range
+ oci-network-configuration-ipam-driver
+ oci-network-configuration-ipv6?
+ oci-network-configuration-subnet
+ oci-network-configuration-labels
+ oci-network-configuration-extra-arguments
+
+ oci-volume-configuration
+ oci-volume-configuration?
+ oci-volume-configuration-fields
+ oci-volume-configuration-name
+ oci-volume-configuration-labels
+ oci-volume-configuration-extra-arguments
+
+ oci-configuration
+ oci-configuration?
+ oci-configuration-runtime
+ oci-con
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 8 Mar 17:06 -0800
[PATCH v8 1/5] services: rootless-podman: Use login shell.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
36e9d9e4474b8d547a86a0225e89f0039af39970.1741482375.git.goodoldpaul@autistici.org
This commit allows for having PATH set when changing the owner of
/sys/fs/group.

* gnu/services/containers.scm (crgroups-fs-owner): Use login shell.

Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
---
gnu/services/containers.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Toggle diff (17 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index b3cd109ce6c..d5a211765a6 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
(rootless-podman-configuration-group-name config))
(program-file "cgroups2-fs-owner-entrypoint"
#~(system*
- (string-append #+bash-minimal "/bin/bash") "-c"
+ (string-append #+bash-minimal "/bin/bash") "-l" "-c"
(string-append "echo Setting /sys/fs/cgroup "
"group ownership to " #$group " && chown -v "
"root:" #$group " /sys/fs/cgroup && "

base-commit: 5adfe1b8e92ff332656bcc7a9d71a35306b3411e
--
2.48.1
G
G
Giacomo Leidi wrote on 8 Mar 17:06 -0800
[PATCH v8 4/5] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
2801dce6518c3f6e99d76ec7010c8108114c224d.1741482375.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/services/containers.scm: Export lower-oci-image-state.
* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/services/containers.scm | 2 +
gnu/tests/containers.scm | 80 ++++++++++++++---------------
gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
3 files changed, 95 insertions(+), 87 deletions(-)

Toggle diff (274 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index a78be00f038..700c7b63603 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -75,6 +75,8 @@ (define-module (gnu services containers)
oci-image-system
oci-image-grafts?
+ lower-oci-image-state
+
oci-container-configuration
oci-container-configuration?
oci-container-configuration-fields
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 5e6f39387e7..8cdd86e7ae3 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -69,13 +69,47 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-rootless-podman-os
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..07edd9d5341 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -57,6 +58,40 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +208,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +217,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +349,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.48.1
G
G
Giacomo Leidi wrote on 8 Mar 17:06 -0800
[PATCH v8 5/5] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
dc0883c8d73e54a7f1e505dbee707f90a7de50c7.1741482375.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 50 ++++++++++++++
gnu/local.mk | 1 +
gnu/services/containers.scm | 5 ++
4 files changed, 170 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (218 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 8686380669b..7ed469f7920 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -50403,6 +50403,120 @@ Miscellaneous Home Services
(dicod-configuration @dots{})))
@end lisp
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 00000000000..938dde2f37a
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,50 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (oci-service-extension-wrap-validate
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli)))))
+ (service-extension home-shepherd-service-type
+ (oci-service-extension-wrap-validate
+ oci-configuration->shepherd-services))))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 9082ed04bfe..e0d1a25a607 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -103,6 +103,7 @@ GNU_SYSTEM_MODULES = \
%D%/home.scm \
%D%/home/services.scm \
%D%/home/services/admin.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 700c7b63603..002bbc1057b 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -762,6 +762,9 @@ (define-configuration/no-serialization oci-network-configuration
(define (list-of-oci-networks? value)
(list-of-oci-records? "networks" oci-network-configuration? value))
+;; (for-home (oci-configuration ...)) is not able to replace for-home? with #t,
+;; pk prints #f. Once for-home will be able to work with (gnu services configuration) the
+;; record can be migrated back to define-configuration.
(define-record-type* <oci-configuration>
oci-configuration
make-oci-configuration
@@ -796,6 +799,8 @@ (define-record-type* <oci-configuration>
(define (package-or-string? value)
(or (package? value) (string? value)))
+;; TODO: This procedure can be dropped once we switch to define-configuration for
+;; oci-configuration.
(define (oci-configuration-valid? config)
(define runtime-cli
(oci-configuration-runtime-cli config))
--
2.48.1
G
G
Giacomo Leidi wrote on 8 Mar 17:06 -0800
[PATCH v8 2/5] services: oci-container-configuration: Move to (gnu services containers).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
5ea9ef46b656b23c6213ec086084fd572b10816a.1741482375.git.goodoldpaul@autistici.org
This patch moves the oci-container-configuration and related
configuration records to (gnu services containers).
Public symbols are still exported for backwards
compatibility but since the oci-container-service-type will be
deprecated in favor of the more general oci-service-type, everything is
moved outside of the docker related module.

* gnu/services/docker.scm: Move everything related to oci-container-configuration
to...
* gnu/services/containers.scm: ...here.scm.
* gnu/tests/docker.scm: Simplify %test-oci-container test case.

Change-Id: Iae599dd5cc7442eb632f0c1b3b12f6b928397ae7
---
gnu/services/containers.scm | 549 +++++++++++++++++++++++++++++++++-
gnu/services/docker.scm | 577 +++---------------------------------
gnu/tests/docker.scm | 99 +++----
3 files changed, 625 insertions(+), 600 deletions(-)

Toggle diff (534 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index d5a211765a6..24f31c756b8 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,19 +17,31 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services containers)
+ #:use-module (gnu image)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages containers)
+ #:use-module (gnu packages docker)
#:use-module (gnu packages file-systems)
#:use-module (gnu services)
#:use-module (gnu services base)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu system)
#:use-module (gnu system accounts)
+ #:use-module (gnu system image)
#:use-module (gnu system shadow)
#:use-module (gnu system pam)
+ #:use-module (guix diagnostics)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
+ #:use-module (guix monads)
#:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
rootless-podman-configuration-fields
@@ -48,7 +60,44 @@ (define-module (gnu services containers)
rootless-podman-shepherd-services
rootless-podman-service-etc
- rootless-podman-service-type))
+ rootless-podman-service-type
+
+ oci-image
+ oci-image?
+ oci-image-fields
+ oci-image-repository
+ oci-image-tag
+ oci-image-value
+ oci-image-pack-options
+ oci-image-target
+ oci-image-system
+ oci-image-grafts?
+
+ oci-container-configuration
+ oci-container-configuration?
+ oci-container-configuration-fields
+ oci-container-configuration-user
+ oci-container-configuration-group
+ oci-container-configuration-command
+ oci-container-configuration-entrypoint
+ oci-container-configuration-host-environment
+ oci-container-configuration-environment
+ oci-container-configuration-image
+ oci-container-configuration-provision
+ oci-container-configuration-requirement
+ oci-container-configuration-log-file
+ oci-container-configuration-auto-start?
+ oci-container-configuration-respawn?
+ oci-container-configuration-shepherd-actions
+ oci-container-configuration-network
+ oci-container-configuration-ports
+ oci-container-configuration-volumes
+ oci-container-configuration-container-user
+ oci-container-configuration-workdir
+ oci-container-configuration-extra-arguments
+
+ oci-container-shepherd-service
+ %oci-container-accounts))
(define (gexp-or-string? value)
(or (gexp? value)
@@ -190,7 +239,7 @@ (define (rootless-podman-cgroups-limits-service config)
rootless-podman-shared-root-fs))
(one-shot? #t)
(documentation
- "Allow setting cgroups limits: cpu, cpuset, memory and
+ "Allow setting cgroups limits: cpu, cpuset, io, memory and
pids.")
(start
#~(make-forkexec-constructor
@@ -244,3 +293,497 @@ (define rootless-podman-service-type
(default-value (rootless-podman-configuration))
(description
"This service configures rootless @code{podman} on the Guix System.")))
+
+
+;;;
+;;; OCI container.
+;;;
+
+(define (oci-sanitize-pair pair delimiter)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (match pair
+ (((? valid? key) . (? valid? value))
+ #~(string-append #$key #$delimiter #$value))
+ (_
+ (raise
+ (formatted-message
+ (G_ "pair members must contain only strings, gexps or file-like objects
+but ~a was found")
+ pair)))))
+
+(define (oci-sanitize-mixed-list name value delimiter)
+ (map
+ (lambda (el)
+ (cond ((string? el) el)
+ ((pair? el) (oci-sanitize-pair el delimiter))
+ (else
+ (raise
+ (formatted-message
+ (G_ "~a members must be either a string or a pair but ~a was
+found!")
+ name el)))))
+ value))
+
+(define (oci-sanitize-host-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "host-environment" value "="))
+
+(define (oci-sanitize-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "environment" value "="))
+
+(define (oci-sanitize-ports value)
+ ;; Expected spec format:
+ ;; '(("8088" . "80") "2022:22")
+ (oci-sanitize-mixed-list "ports" value ":"))
+
+(define (oci-sanitize-volumes value)
+ ;; Expected spec format:
+ ;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
+ (oci-sanitize-mixed-list "volumes" value ":"))
+
+(define (oci-sanitize-shepherd-actions value)
+ (map
+ (lambda (el)
+ (if (shepherd-action? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "shepherd-actions may only be shepherd-action records
+but ~a was found") el))))
+ value))
+
+(define (oci-sanitize-extra-arguments value)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (map
+ (lambda (el)
+ (if (valid? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "extra arguments may only be strings, gexps or file-like objects
+but ~a was found") el))))
+ value))
+
+(define (oci-image-reference image)
+ (if (string? image)
+ image
+ (string-append (oci-image-repository image)
+ ":" (oci-image-tag image))))
+
+(define (oci-lowerable-image? image)
+ (or (manifest? image)
+ (operating-system? image)
+ (gexp? image)
+ (file-like? image)))
+
+(define (string-or-oci-image? image)
+ (or (string? image)
+ (oci-image? image)))
+
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe/no-serialization string)
+
+(define-configuration/no-serialization oci-image
+ (repository
+ (string)
+ "A string like @code{myregistry.local:5000/testing/test-image} that names
+the OCI image.")
+ (tag
+ (string "latest")
+ "A string representing the OCI image tag. Defaults to @code{latest}.")
+ (value
+ (oci-lowerable-image)
+ "A @code{manifest} or @code{operating-system} record that will be lowered
+into an OCI compatible tarball. Otherwise this field's value can be a gexp
+or a file-like object that evaluates to an OCI compatible tarball.")
+ (pack-options
+ (list '())
+ "An optional set of keyword arguments that will be passed to the
+@code{docker-image} procedure from @code{guix scripts pack}. They can be used
+to replicate @command{guix pack} behavior:
+
+@lisp
+(oci-image
+ (repository \"guile\")
+ (tag \"3\")
+ (manifest (specifications->manifest '(\"guile\")))
+ (pack-options
+ '(#:symlinks ((\"/bin/guile\" -> \"bin/guile\"))
+ #:max-layers 2)))
+@end lisp
+
+If the @code{value} field is an @code{operating-system} record, this field's
+value will be ignored.")
+ (system
+ (maybe-string)
+ "Attempt to build for a given system, e.g. \"i686-linux\"")
+ (target
+ (maybe-string)
+ "Attempt to cross-build for a given triple, e.g. \"aarch64-linux-gnu\"")
+ (grafts?
+ (boolean #f)
+ "Whether to allow grafting or not in the pack build."))
+
+(define-configuration/no-serialization oci-container-configuration
+ (user
+ (string "oci-container")
+ "The user under whose authority docker commands will be run.")
+ (group
+ (string "docker")
+ "The group under whose authority docker commands will be run.")
+ (command
+ (list-of-strings '())
+ "Overwrite the default command (@code{CMD}) of the image.")
+ (entrypoint
+ (maybe-string)
+ "Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
+ (host-environment
+ (list '())
+ "Set environment variables in the host environment where @command{docker run}
+is invoked. This is especially useful to pass secrets from the host to the
+container without having them on the @command{docker run}'s command line: by
+setting the @code{MYSQL_PASSWORD} on the host and by passing
+@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
+possible to securely set values in the container environment. This field's
+value can be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to @code{make-forkexec-constructor}."
+ (sanitizer oci-sanitize-host-environment))
+ (environment
+ (list '())
+ "Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-environment))
+ (image
+ (string-or-oci-image)
+ "The image used to build the container. It can be a string or an
+@code{oci-image} record. Strings are resolved by the Docker
+Engine, and follow the usual format
+@code{myregistry.local:5000/testing/test-image:tag}.")
+ (provision
+ (maybe-string)
+ "Set the name of the provisioned Shepherd service.")
+ (requirement
+ (list-of-symbols '())
+ "Set additional Shepherd services dependencies to the provisioned Shepherd
+service.")
+ (log-file
+ (maybe-string)
+ "When @code{log-file} is set, it names the file to which the service’s
+standard output and standard error are redirected. @code{log-file} is created
+if it does not exist, otherwise it is appended to.")
+ (auto-start?
+ (boolean #t)
+ "Whether this service should be started automatically by the Shepherd. If it
+is @code{#f} the service has to be started manually with @command{herd start}.")
+ (respawn?
+ (boolean #f)
+ "Whether to restart the service when it stops, for instance when the
+underlying process dies.")
+ (shepherd-actions
+ (list '())
+ "This is a list of @code{shepherd-action} records defining actions supported
+by the service."
+ (sanitizer oci-sanitize-shepherd-actions))
+ (network
+ (maybe-string)
+ "Set a Docker network for the spawned container.")
+ (ports
+ (list '())
+ "Set the port or port ranges to expose from the spawned container. This can
+be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"8080\" . \"80\")
+ \"10443:443\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-ports))
+ (volumes
+ (list '())
+ "Set volume mappings for the spawned container. This can be a
+list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
+ \"/gnu/store:/gnu/store\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-volumes))
+ (container-user
+ (maybe-string)
+ "Set the current user inside the spawned container. You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#user,upstream}
+documentation for semantics.")
+ (workdir
+ (maybe-string)
+ "Set the current working for the spawned Shepherd service.
+You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
+documentation for semantics.")
+ (extra-arguments
+ (list '())
+ "A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} invokation."
+ (sanitizer oci-sanitize-extra-arguments)))
+
+(define oci-container-configuration->options
+ (lambda (config)
+ (let ((entrypoint
+ (oci-container-configuration-entrypoint config))
+ (network
+ (oci-container-configuration-network config))
+ (user
+ (oci-container-configuration-container-user config))
+ (workdir
+ (oci-container-configuration-workdir config)))
+ (apply append
+ (filter (compose not unspecified?)
+ `(,(if (maybe-value-set? entrypoint)
+ `("--entrypoint" ,entrypoint)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "--env" spec))
+ (oci-container-configuration-environment config))
+ ,(if (maybe-value-set? network)
+ `("--network" ,network)
+ '())
+ ,(if (maybe-value-set? user)
+ `("--user" ,user)
+ '())
+ ,(if (maybe-value-set? workdir)
+ `("--workdir" ,workdir)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "-p" spec))
+ (oci-container-configuration-ports config))
+ ,(append-map
+ (lambda (spec)
+ (list "-v" spec))
+ (oci-container-configuration-volumes config))))))))
+
+(define* (get-keyword-value args keyword #:key (default #f))
+ (let ((kv (memq keyword args)))
+ (if (and kv (>= (length kv) 2))
+ (cadr kv)
+ default)))
+
+(define (lower-operating-system os target system)
+ (mlet* %store-monad
+ ((tarball
+ (lower-object
+ (system-image (os->image os #:type docker-image-type))
+ system
+ #:target target)))
+ (return tarball)))
+
+(define (lower-manifest name image target system)
+ (define value (oci-image-value image))
+ (define options (oci-image-pack-options image))
+ (define image-reference
+ (oci-image-reference image))
+ (define image-tag
+ (let* ((extra-options
+ (get-keyword-value options #:extra-options))
+ (image-tag-option
+ (and extra-options
+ (get-keyword-value extra-options #:image-tag))))
+ (if image-tag-option
+ '()
+ `(#:extra-options (#:image-tag ,image-reference)))))
+
+ (mlet* %store-monad
+ ((_ (set-grafting
+ (oci-image-grafts? image)))
+ (guile (set-guile-for-build (default-guile)))
+ (profile
+ (profile-derivation value
+ #:target target
+ #:system system
+ #:hooks '()
+ #:locales? #f))
+ (tarball (apply pack:docker-image
+ `(,name ,profile
+ ,@options
+ ,@image-tag
+ #:localstatedir? #t))))
+ (return tarball)))
+
+(define (lower-oci-image name image)
+ (define value (oci-image-value image))
+ (define image-target (oci-image-target image))
+ (define image-system (oci-image-system image))
+ (define target
+ (if (maybe-value-set? image-target)
+ image-target
+ (%current-target-system)))
+ (define system
+ (if (maybe-value-set? image-system)
+ image-system
+ (%current-system)))
+ (with-store store
+ (run-with-store store
+ (match value
+ ((? manifest? value)
+ (lower-manifest name image target system))
+ ((? operating-system? value)
+ (lower-operating-system value target system))
+ ((or (? gexp? value)
+ (? file-like? value))
+ value)
+ (_
+ (raise
+ (formatted-message
+ (G_ "oci-image value must contain only manifest,
+operating-system, gexp or file-like records but ~a was found")
+ value))))
+ #:target target
+ #:system system)))
+
+(define (%oci-image-loader name image tag)
+ (let ((docker (file-append docker-cli "/bin/docker"))
+ (tarball (lower-oci-image name image)))
+ (with-imported-modules '((guix build utils))
+ (program-file (format #f "~a-image-loader" name)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+
+ (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
+ (define line
+ (read-line
+ (open-input-pipe
+ (string-append #$docker " load -i " #$tarball))))
+
+ (unless (or (eof-object? line)
+ (string-null? line))
+ (format #t "~a~%" line)
+ (let ((repository&tag
+ (string-drop line
+ (string-length
+ "Loaded image: "))))
+
+ (invoke #$docker "tag" repository&tag #$tag)
+ (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
+
+(define (oci-container-shepherd-service config)
+ (define (guess-name name image)
+ (if (maybe-value-set? name)
+ name
+ (string-append "docker-"
+ (basename
+ (if (string? image)
+ (first (string-split image #\:))
+ (oci-image-repository image))))))
+
+ (let* ((docker (file-append docker-cli "/bin/docker"))
+ (actions (oci-container-configuration-shepherd-actions config))
+ (auto-start?
+ (oci-container-configuration-auto-start? config))
+ (user (oci-container-configuration-user config))
+ (group (oci-container-configuration-group config))
+ (host-environment
+ (oci-container-configuration-host-environment config))
+ (command (oci-container-configuration-command config))
+ (log-file (oci-container-configuration-log-file config))
+ (provision (oci-container-configuration-provi
This message was truncated. Download the full message here.
P
Re: OCI provisioning service
(address . 76081@debbugs.gnu.org)
020bb939-819c-4de3-bc04-ef0f33855c90@autistici.org
Hi,

On 3/9/25 01:27, paul wrote:
Toggle quote (61 lines)
> Hi,
>
> On 3/4/25 13:39, paul wrote:
>> Hi,
>>
>> On 2/27/25 00:01, paul wrote:
>>> Hi guix,
>>>
>>> On 2/18/25 02:20, paul wrote:
>>>> Hi,
>>>>
>>>> On 2/12/25 02:09, paul wrote:
>>>>> Hi guix,
>>>>>
>>>>> On 2/9/25 21:38, paul wrote:
>>>>>> I'm sending a v3 fixing a bug in the merge algorithm for volumes
>>>>>> and networks.
>>>>>>
>>>>>> On 2/9/25 20:14, paul wrote:
>>>>>>> Hi,
>>>>>>>
>>>>>>> I'm about to send a v2. v2 compared to the first revision features:
>>>>>>>
>>>>>>>
>>>>>>> - it actually compiles all the times :) (rev 1 referenced
>>>>>>> oci-image too early for it to be working and generated a compile
>>>>>>> time error, if you recompiled it sometimes went away so I
>>>>>>> thought it was a problem of my setup. CI caught this)
>>>>>>> - it allows more values to be overridden by eventual users of
>>>>>>> the Scheme API
>>>>>>> - it allows passing extra arguments directly after each podman
>>>>>>> or docker invokation, allowing for example for overriding podman
>>>>>>> --root and similar options.
>>>>>>>
>>>>>>> All of these tests should pass:
>>>>>>>
>>>>>>> guix shell -D guix -CPW -- make check-system
>>>>>>> TESTS="oci-container oci-service-rootless-podman docker
>>>>>>> docker-system rootless-podman oci-service-docker"
>>>>>>>
>>>>> I'm sending a v4 changing slightly the image loader, the same
>>>>> tests as before are supposed to pass. Now the Home service [0] is
>>>>> working for me with rootless podman. I'll try it on different
>>>>> distros if I manage to.
>>>>
>>>> I'm sending a v5 implementing a Home service. The changes compared
>>>> to v4 are pretty trivial as the plumbing was already there, the
>>>> only downside is that I'm not able to use for-home? in
>>>> define-configuration, so I had to reimplement oci-configuration
>>>> with (guix records) and had to reimplement some validation (gnu
>>>> services configuration) would figure out magically.
>>>
>>> I'm sending  a v6. Compared to v5 it resolves the conflicts with
>>> master and it should fix the stop action for rootless podman backed
>>> services.
>>
>> I'm sending a v7 fixing the restart action for podman backed services.
>
> I'm sending a v8 fixing some further bugs wrt to the start and stop of
> podman backed services.

In the hope of making the review easier, I'm sending a v9 that is
completely the same of v8 in terms of changes but with smaller commits.
Hopefully this makes it easier to review changes, please let me know if
I can do anything to ease the workload for reviewers of this patch set.

Thank you for your work,

cheers

giacomo
G
G
Giacomo Leidi wrote on 18 Mar 10:51 -0700
[PATCH v9 3/7] tests: oci-container: Set explicit timeouts.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
f4307abc36664f1d8ba3ddcd79709f6164c23860.1742320272.git.goodoldpaul@autistici.org
* gnu/tests/docker.scm: Simplify %test-oci-container test case and add
explicit timeouts to tests outcomes.
---
gnu/tests/docker.scm | 99 ++++++++++++++++++--------------------------
1 file changed, 41 insertions(+), 58 deletions(-)

Toggle diff (127 lines)
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 90c8d0f8508..5dcf05a17e3 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -1,7 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 Danny Milosavljevic <dannym@scratchpost.org>
;;; Copyright © 2019-2023 Ludovic Courtès <ludo@gnu.org>
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -414,71 +414,54 @@ (define (run-oci-container-test)
(test-runner-current (system-test-runner #$output))
(test-begin "oci-container")
- (test-assert "containerd service running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'containerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (test-assert "containerd PID file present"
- (wait-for-file "/run/containerd/containerd.pid" marionette))
-
- (test-assert "dockerd running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'dockerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (sleep 10) ; let service start
+ (wait-for-file "/run/containerd/containerd.pid" marionette)
(test-assert "docker-guile running"
(marionette-eval
'(begin
(use-modules (gnu services herd))
- (match (start-service 'docker-guile)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
+ (wait-for-service 'docker-guile #:timeout 120)
+ #t)
marionette))
- (test-equal "passing host environment variables and volumes"
- '("value" "hello")
- (marionette-eval
- `(begin
- (use-modules (ice-9 popen)
- (ice-9 rdelim))
-
- (define slurp
- (lambda args
- (let* ((port (apply open-pipe* OPEN_READ args))
- (output (let ((line (read-line port)))
- (if (eof-object? line)
- ""
- line)))
- (status (close-pipe port)))
- output)))
- (let* ((response1 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
- (response2 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
+ (test-assert "passing host environment variables and volumes"
+ (begin
+ (define (run-test)
+ (marionette-eval
+ `(begin
+ (use-modules (ice-9 popen)
+ (ice-9 rdelim))
+
+ (define slurp
+ (lambda args
+ (let* ((port (apply open-pipe* OPEN_READ args))
+ (output (let ((line (read-line port)))
+ (if (eof-object? line)
+ ""
+ line)))
+ (status (close-pipe port)))
+ output)))
+ (let* ((response1 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
+ (response2 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
(display (call-with-input-file \"/shared.txt\" read-line)))")))
- (list response1 response2)))
- marionette))
+ (list response1 response2)))
+ marionette))
+ ;; Allow services to come up on slower machines
+ (let loop ((attempts 0))
+ (if (= attempts 60)
+ (error "Service didn't come up after more than 60 seconds")
+ (if (equal? '("value" "hello")
+ (run-test))
+ #t
+ (begin
+ (sleep 1)
+ (loop (+ 1 attempts))))))))
(test-end))))
--
2.48.1
G
G
Giacomo Leidi wrote on 18 Mar 10:51 -0700
[PATCH v9 4/7] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
20663568040d1345979b6166caabcb4be1b28d10.1742320272.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/services/containers.scm (%oci-supported-runtimes): New variable;
(oci-runtime-cli): new variable;
(oci-runtime-name): new variable;
(oci-network-configuration): new variable;
(oci-volume-configuration): new variable;
(oci-configuration): new variable;
(oci-extension): new variable;
(oci-networks-shepherd-name): new variable;
(oci-service-type): new variable;
(oci-state->shepherd-services): new variable.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 306 +++++++--
gnu/services/containers.scm | 1300 ++++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 37 +-
gnu/tests/containers.scm | 999 ++++++++++++++++++++++++++-
4 files changed, 2390 insertions(+), 252 deletions(-)

Toggle diff (450 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 04885593328..521ea28dd5a 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -42914,59 +42914,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@command{docker run} or @command{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources, it can be either a package or a string representing
+an absolute path to the runtime binary entrypoint. When unset it will default
+to @code{docker-cli} package for the @code{'docker} runtime or to @code{podman}
+package for the @code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -42986,16 +43089,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @command{docker run}
+or @command{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -43003,22 +43106,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -43046,7 +43151,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -43057,10 +43162,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -43071,25 +43177,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} or @command{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker network create} or @command{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker volume create} or @command{podman volume create}
+invokation.
@end table
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 24f31c756b8..d1ebfa1736c 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -41,6 +41,7 @@ (define-module (gnu services containers)
#:use-module ((guix scripts pack) #:prefix pack:)
#:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
@@ -96,8 +97,78 @@ (define-module (gnu services containers)
oci-container-configuration-workdir
oci-container-configuration-extra-arguments
+ list-of-oci-containers?
+ list-of-oci-networks?
+ list-of-oci-volumes?
+
+ %oci-supported-runtimes
+ oci-sanitize-runtime
+ oci-runtime-system-environment
+ oci-runtime-system-extra-arguments
+ oci-runtime-system-requirement
+ oci-runtime-cli
+ oci-runtime-system-cli
+ oci-runtime-home-cli
+ oci-runtime-name
+ oci-runtime-group
+
+ oci-network-configuration
+ oci-network-configuration?
+ oci-network-configuration-fields
+ oci-network-configuration-name
+ oci-network-configuration-driver
+ oci-network-configuration-gateway
+ oci-network-configuration-internal?
+ oci-network-configuration-ip-range
+ oci-network-configuration-ipam-driver
+ oci-network-configuration-ipv6?
+ oci-network-configuration-subnet
+ oci-network-configuration-labels
+ oci-network-configuration-extra-arguments
+
+ oci-volume-configuration
+ oci-volume-configuration?
+ oci-volume-configuration-fields
+ oci-volume-configuration-name
+ oci-volume-configuration-labels
+ oci-volume-configuration-extra-arguments
+
+ oci-configuration
+ oci-configuration?
+ oci-configuration-runtime
+ oci-configuration-runtime-cli
+ oci-configuration-runtime-extra-arguments
+ oci-configuration-user
+ oci-configuration-gr
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 18 Mar 10:51 -0700
[PATCH v9 5/7] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
5e11bb8d29e14d75e1b09166efdda8932bb95e7b.1742320272.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/services/containers.scm: Export lower-oci-image-state.
* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/services/containers.scm | 2 +
gnu/tests/containers.scm | 80 ++++++++++++++---------------
gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
3 files changed, 95 insertions(+), 87 deletions(-)

Toggle diff (274 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index d1ebfa1736c..5dac8e80f22 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -74,6 +74,8 @@ (define-module (gnu services containers)
oci-image-system
oci-image-grafts?
+ lower-oci-image-state
+
oci-container-configuration
oci-container-configuration?
oci-container-configuration-fields
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 5e6f39387e7..8cdd86e7ae3 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -69,13 +69,47 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-rootless-podman-os
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..07edd9d5341 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -57,6 +58,40 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +208,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +217,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +349,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.48.1
G
G
Giacomo Leidi wrote on 18 Mar 10:51 -0700
[PATCH v9 1/7] services: rootless-podman: Use login shell.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
15af7d63ead9dfad272154c09bdfda718da1047c.1742320272.git.goodoldpaul@autistici.org
This commit allows for having PATH set when changing the owner of
/sys/fs/group.

* gnu/services/containers.scm (crgroups-fs-owner): Use login shell.

Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
---
gnu/services/containers.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Toggle diff (17 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index b3cd109ce6c..d5a211765a6 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
(rootless-podman-configuration-group-name config))
(program-file "cgroups2-fs-owner-entrypoint"
#~(system*
- (string-append #+bash-minimal "/bin/bash") "-c"
+ (string-append #+bash-minimal "/bin/bash") "-l" "-c"
(string-append "echo Setting /sys/fs/cgroup "
"group ownership to " #$group " && chown -v "
"root:" #$group " /sys/fs/cgroup && "

base-commit: 9879a7d75279762c7634f1a585794442ea2f0503
--
2.48.1
G
G
Giacomo Leidi wrote on 18 Mar 10:51 -0700
[PATCH v9 2/7] services: oci-container-configuration: Move to (gnu services containers).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
d46b8563f47f625d62fb02a50b9a31c66f2a28c0.1742320272.git.goodoldpaul@autistici.org
This patch moves the oci-container-configuration and related
configuration records to (gnu services containers).
Public symbols are still exported for backwards
compatibility but since the oci-container-service-type will be
deprecated in favor of the more general oci-service-type, everything is
moved outside of the docker related module.

* gnu/services/docker.scm: Move everything related to oci-container-configuration
to...
* gnu/services/containers.scm: ...here.scm.

Change-Id: Iae599dd5cc7442eb632f0c1b3b12f6b928397ae7
---
gnu/services/containers.scm | 549 +++++++++++++++++++++++++++++++++-
gnu/services/docker.scm | 577 +++---------------------------------
2 files changed, 584 insertions(+), 542 deletions(-)

Toggle diff (537 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index d5a211765a6..24f31c756b8 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,19 +17,31 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services containers)
+ #:use-module (gnu image)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages containers)
+ #:use-module (gnu packages docker)
#:use-module (gnu packages file-systems)
#:use-module (gnu services)
#:use-module (gnu services base)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu system)
#:use-module (gnu system accounts)
+ #:use-module (gnu system image)
#:use-module (gnu system shadow)
#:use-module (gnu system pam)
+ #:use-module (guix diagnostics)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
+ #:use-module (guix monads)
#:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
rootless-podman-configuration-fields
@@ -48,7 +60,44 @@ (define-module (gnu services containers)
rootless-podman-shepherd-services
rootless-podman-service-etc
- rootless-podman-service-type))
+ rootless-podman-service-type
+
+ oci-image
+ oci-image?
+ oci-image-fields
+ oci-image-repository
+ oci-image-tag
+ oci-image-value
+ oci-image-pack-options
+ oci-image-target
+ oci-image-system
+ oci-image-grafts?
+
+ oci-container-configuration
+ oci-container-configuration?
+ oci-container-configuration-fields
+ oci-container-configuration-user
+ oci-container-configuration-group
+ oci-container-configuration-command
+ oci-container-configuration-entrypoint
+ oci-container-configuration-host-environment
+ oci-container-configuration-environment
+ oci-container-configuration-image
+ oci-container-configuration-provision
+ oci-container-configuration-requirement
+ oci-container-configuration-log-file
+ oci-container-configuration-auto-start?
+ oci-container-configuration-respawn?
+ oci-container-configuration-shepherd-actions
+ oci-container-configuration-network
+ oci-container-configuration-ports
+ oci-container-configuration-volumes
+ oci-container-configuration-container-user
+ oci-container-configuration-workdir
+ oci-container-configuration-extra-arguments
+
+ oci-container-shepherd-service
+ %oci-container-accounts))
(define (gexp-or-string? value)
(or (gexp? value)
@@ -190,7 +239,7 @@ (define (rootless-podman-cgroups-limits-service config)
rootless-podman-shared-root-fs))
(one-shot? #t)
(documentation
- "Allow setting cgroups limits: cpu, cpuset, memory and
+ "Allow setting cgroups limits: cpu, cpuset, io, memory and
pids.")
(start
#~(make-forkexec-constructor
@@ -244,3 +293,497 @@ (define rootless-podman-service-type
(default-value (rootless-podman-configuration))
(description
"This service configures rootless @code{podman} on the Guix System.")))
+
+
+;;;
+;;; OCI container.
+;;;
+
+(define (oci-sanitize-pair pair delimiter)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (match pair
+ (((? valid? key) . (? valid? value))
+ #~(string-append #$key #$delimiter #$value))
+ (_
+ (raise
+ (formatted-message
+ (G_ "pair members must contain only strings, gexps or file-like objects
+but ~a was found")
+ pair)))))
+
+(define (oci-sanitize-mixed-list name value delimiter)
+ (map
+ (lambda (el)
+ (cond ((string? el) el)
+ ((pair? el) (oci-sanitize-pair el delimiter))
+ (else
+ (raise
+ (formatted-message
+ (G_ "~a members must be either a string or a pair but ~a was
+found!")
+ name el)))))
+ value))
+
+(define (oci-sanitize-host-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "host-environment" value "="))
+
+(define (oci-sanitize-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "environment" value "="))
+
+(define (oci-sanitize-ports value)
+ ;; Expected spec format:
+ ;; '(("8088" . "80") "2022:22")
+ (oci-sanitize-mixed-list "ports" value ":"))
+
+(define (oci-sanitize-volumes value)
+ ;; Expected spec format:
+ ;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
+ (oci-sanitize-mixed-list "volumes" value ":"))
+
+(define (oci-sanitize-shepherd-actions value)
+ (map
+ (lambda (el)
+ (if (shepherd-action? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "shepherd-actions may only be shepherd-action records
+but ~a was found") el))))
+ value))
+
+(define (oci-sanitize-extra-arguments value)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (map
+ (lambda (el)
+ (if (valid? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "extra arguments may only be strings, gexps or file-like objects
+but ~a was found") el))))
+ value))
+
+(define (oci-image-reference image)
+ (if (string? image)
+ image
+ (string-append (oci-image-repository image)
+ ":" (oci-image-tag image))))
+
+(define (oci-lowerable-image? image)
+ (or (manifest? image)
+ (operating-system? image)
+ (gexp? image)
+ (file-like? image)))
+
+(define (string-or-oci-image? image)
+ (or (string? image)
+ (oci-image? image)))
+
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe/no-serialization string)
+
+(define-configuration/no-serialization oci-image
+ (repository
+ (string)
+ "A string like @code{myregistry.local:5000/testing/test-image} that names
+the OCI image.")
+ (tag
+ (string "latest")
+ "A string representing the OCI image tag. Defaults to @code{latest}.")
+ (value
+ (oci-lowerable-image)
+ "A @code{manifest} or @code{operating-system} record that will be lowered
+into an OCI compatible tarball. Otherwise this field's value can be a gexp
+or a file-like object that evaluates to an OCI compatible tarball.")
+ (pack-options
+ (list '())
+ "An optional set of keyword arguments that will be passed to the
+@code{docker-image} procedure from @code{guix scripts pack}. They can be used
+to replicate @command{guix pack} behavior:
+
+@lisp
+(oci-image
+ (repository \"guile\")
+ (tag \"3\")
+ (manifest (specifications->manifest '(\"guile\")))
+ (pack-options
+ '(#:symlinks ((\"/bin/guile\" -> \"bin/guile\"))
+ #:max-layers 2)))
+@end lisp
+
+If the @code{value} field is an @code{operating-system} record, this field's
+value will be ignored.")
+ (system
+ (maybe-string)
+ "Attempt to build for a given system, e.g. \"i686-linux\"")
+ (target
+ (maybe-string)
+ "Attempt to cross-build for a given triple, e.g. \"aarch64-linux-gnu\"")
+ (grafts?
+ (boolean #f)
+ "Whether to allow grafting or not in the pack build."))
+
+(define-configuration/no-serialization oci-container-configuration
+ (user
+ (string "oci-container")
+ "The user under whose authority docker commands will be run.")
+ (group
+ (string "docker")
+ "The group under whose authority docker commands will be run.")
+ (command
+ (list-of-strings '())
+ "Overwrite the default command (@code{CMD}) of the image.")
+ (entrypoint
+ (maybe-string)
+ "Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
+ (host-environment
+ (list '())
+ "Set environment variables in the host environment where @command{docker run}
+is invoked. This is especially useful to pass secrets from the host to the
+container without having them on the @command{docker run}'s command line: by
+setting the @code{MYSQL_PASSWORD} on the host and by passing
+@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
+possible to securely set values in the container environment. This field's
+value can be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to @code{make-forkexec-constructor}."
+ (sanitizer oci-sanitize-host-environment))
+ (environment
+ (list '())
+ "Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-environment))
+ (image
+ (string-or-oci-image)
+ "The image used to build the container. It can be a string or an
+@code{oci-image} record. Strings are resolved by the Docker
+Engine, and follow the usual format
+@code{myregistry.local:5000/testing/test-image:tag}.")
+ (provision
+ (maybe-string)
+ "Set the name of the provisioned Shepherd service.")
+ (requirement
+ (list-of-symbols '())
+ "Set additional Shepherd services dependencies to the provisioned Shepherd
+service.")
+ (log-file
+ (maybe-string)
+ "When @code{log-file} is set, it names the file to which the service’s
+standard output and standard error are redirected. @code{log-file} is created
+if it does not exist, otherwise it is appended to.")
+ (auto-start?
+ (boolean #t)
+ "Whether this service should be started automatically by the Shepherd. If it
+is @code{#f} the service has to be started manually with @command{herd start}.")
+ (respawn?
+ (boolean #f)
+ "Whether to restart the service when it stops, for instance when the
+underlying process dies.")
+ (shepherd-actions
+ (list '())
+ "This is a list of @code{shepherd-action} records defining actions supported
+by the service."
+ (sanitizer oci-sanitize-shepherd-actions))
+ (network
+ (maybe-string)
+ "Set a Docker network for the spawned container.")
+ (ports
+ (list '())
+ "Set the port or port ranges to expose from the spawned container. This can
+be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"8080\" . \"80\")
+ \"10443:443\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-ports))
+ (volumes
+ (list '())
+ "Set volume mappings for the spawned container. This can be a
+list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
+ \"/gnu/store:/gnu/store\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-volumes))
+ (container-user
+ (maybe-string)
+ "Set the current user inside the spawned container. You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#user,upstream}
+documentation for semantics.")
+ (workdir
+ (maybe-string)
+ "Set the current working for the spawned Shepherd service.
+You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
+documentation for semantics.")
+ (extra-arguments
+ (list '())
+ "A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} invokation."
+ (sanitizer oci-sanitize-extra-arguments)))
+
+(define oci-container-configuration->options
+ (lambda (config)
+ (let ((entrypoint
+ (oci-container-configuration-entrypoint config))
+ (network
+ (oci-container-configuration-network config))
+ (user
+ (oci-container-configuration-container-user config))
+ (workdir
+ (oci-container-configuration-workdir config)))
+ (apply append
+ (filter (compose not unspecified?)
+ `(,(if (maybe-value-set? entrypoint)
+ `("--entrypoint" ,entrypoint)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "--env" spec))
+ (oci-container-configuration-environment config))
+ ,(if (maybe-value-set? network)
+ `("--network" ,network)
+ '())
+ ,(if (maybe-value-set? user)
+ `("--user" ,user)
+ '())
+ ,(if (maybe-value-set? workdir)
+ `("--workdir" ,workdir)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "-p" spec))
+ (oci-container-configuration-ports config))
+ ,(append-map
+ (lambda (spec)
+ (list "-v" spec))
+ (oci-container-configuration-volumes config))))))))
+
+(define* (get-keyword-value args keyword #:key (default #f))
+ (let ((kv (memq keyword args)))
+ (if (and kv (>= (length kv) 2))
+ (cadr kv)
+ default)))
+
+(define (lower-operating-system os target system)
+ (mlet* %store-monad
+ ((tarball
+ (lower-object
+ (system-image (os->image os #:type docker-image-type))
+ system
+ #:target target)))
+ (return tarball)))
+
+(define (lower-manifest name image target system)
+ (define value (oci-image-value image))
+ (define options (oci-image-pack-options image))
+ (define image-reference
+ (oci-image-reference image))
+ (define image-tag
+ (let* ((extra-options
+ (get-keyword-value options #:extra-options))
+ (image-tag-option
+ (and extra-options
+ (get-keyword-value extra-options #:image-tag))))
+ (if image-tag-option
+ '()
+ `(#:extra-options (#:image-tag ,image-reference)))))
+
+ (mlet* %store-monad
+ ((_ (set-grafting
+ (oci-image-grafts? image)))
+ (guile (set-guile-for-build (default-guile)))
+ (profile
+ (profile-derivation value
+ #:target target
+ #:system system
+ #:hooks '()
+ #:locales? #f))
+ (tarball (apply pack:docker-image
+ `(,name ,profile
+ ,@options
+ ,@image-tag
+ #:localstatedir? #t))))
+ (return tarball)))
+
+(define (lower-oci-image name image)
+ (define value (oci-image-value image))
+ (define image-target (oci-image-target image))
+ (define image-system (oci-image-system image))
+ (define target
+ (if (maybe-value-set? image-target)
+ image-target
+ (%current-target-system)))
+ (define system
+ (if (maybe-value-set? image-system)
+ image-system
+ (%current-system)))
+ (with-store store
+ (run-with-store store
+ (match value
+ ((? manifest? value)
+ (lower-manifest name image target system))
+ ((? operating-system? value)
+ (lower-operating-system value target system))
+ ((or (? gexp? value)
+ (? file-like? value))
+ value)
+ (_
+ (raise
+ (formatted-message
+ (G_ "oci-image value must contain only manifest,
+operating-system, gexp or file-like records but ~a was found")
+ value))))
+ #:target target
+ #:system system)))
+
+(define (%oci-image-loader name image tag)
+ (let ((docker (file-append docker-cli "/bin/docker"))
+ (tarball (lower-oci-image name image)))
+ (with-imported-modules '((guix build utils))
+ (program-file (format #f "~a-image-loader" name)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+
+ (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
+ (define line
+ (read-line
+ (open-input-pipe
+ (string-append #$docker " load -i " #$tarball))))
+
+ (unless (or (eof-object? line)
+ (string-null? line))
+ (format #t "~a~%" line)
+ (let ((repository&tag
+ (string-drop line
+ (string-length
+ "Loaded image: "))))
+
+ (invoke #$docker "tag" repository&tag #$tag)
+ (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
+
+(define (oci-container-shepherd-service config)
+ (define (guess-name name image)
+ (if (maybe-value-set? name)
+ name
+ (string-append "docker-"
+ (basename
+ (if (string? image)
+ (first (string-split image #\:))
+ (oci-image-repository image))))))
+
+ (let* ((docker (file-append docker-cli "/bin/docker"))
+ (actions (oci-container-configuration-shepherd-actions config))
+ (auto-start?
+ (oci-container-configuration-auto-start? config))
+ (user (oci-container-configuration-user config))
+ (group (oci-container-configuration-group config))
+ (host-environment
+ (oci-container-configuration-host-environment config))
+ (command (oci-container-configuration-command config))
+ (log-file (oci-container-configuration-log-file config))
+ (provision (oci-container-configuration-provision config))
+ (requirement (oci-container-configuration-requirement config))
+ (respawn?

This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 18 Mar 10:51 -0700
[PATCH v9 7/7] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
43bf916116776f60f35cfbce848540c43607c589.1742320272.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 50 ++++++++++++++
gnu/local.mk | 1 +
3 files changed, 165 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (195 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 521ea28dd5a..8f3017c2f69 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -50478,6 +50478,120 @@ Miscellaneous Home Services
(dicod-configuration @dots{})))
@end lisp
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 00000000000..938dde2f37a
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,50 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (oci-service-extension-wrap-validate
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli)))))
+ (service-extension home-shepherd-service-type
+ (oci-service-extension-wrap-validate
+ oci-configuration->shepherd-services))))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 01d13a11ae8..0f6502d979b 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -103,6 +103,7 @@ GNU_SYSTEM_MODULES = \
%D%/home.scm \
%D%/home/services.scm \
%D%/home/services/admin.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.48.1
G
G
Giacomo Leidi wrote on 18 Mar 10:51 -0700
[PATCH v9 6/7] services: oci: Migrate oci-configuration to (guix records).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
f9d50ef80bf873d0e51c1536e6607b545a68a316.1742320272.git.goodoldpaul@autistici.org
This commit migrates oci-configuration to (guix records) singe it
appears (for-home (oci-configuration ...)) does not work as expected
with (gnu services configuration). This is supposed to be completely
transparent for users and can be reverted in the
future once this has been implemented.

* gnu/service/containers.scm: Migrate oci-configuration to (guix records).
---
gnu/services/containers.scm | 199 +++++++++++++++++++++---------------
1 file changed, 117 insertions(+), 82 deletions(-)

Toggle diff (283 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 5dac8e80f22..7a44e1c0f7c 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -39,6 +39,7 @@ (define-module (gnu services containers)
#:use-module (guix packages)
#:use-module (guix profiles)
#:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix records)
#:use-module (guix store)
#:use-module (srfi srfi-1)
#:use-module (ice-9 format)
@@ -164,6 +165,7 @@ (define-module (gnu services containers)
oci-container-shepherd-service
oci-objects-merge-lst
oci-extension-merge
+ oci-service-extension-wrap-validate
oci-service-type
oci-service-accounts
oci-service-profile
@@ -391,7 +393,7 @@ (define (oci-runtime-name runtime)
(define (oci-runtime-group runtime maybe-group)
"Implement the logic behind selection of the group that is to be used by
Shepherd to execute OCI commands."
- (if (not (maybe-value-set? maybe-group))
+ (if (eq? maybe-group #f)
(if (eq? 'podman runtime)
"cgroup"
"docker")
@@ -762,62 +764,74 @@ (define (list-of-oci-networks? value)
(define (package-or-string? value)
(or (package? value) (string? value)))
-(define-maybe/no-serialization package-or-string)
-
-(define-configuration/no-serialization oci-configuration
- (runtime
- (symbol 'docker)
- "The OCI runtime to use to run commands. It can be either @code{'docker} or
-@code{'podman}."
- (sanitizer oci-sanitize-runtime))
- (runtime-cli
- (maybe-package-or-string)
- "The OCI runtime command line to be installed in the system profile and used
-to provision OCI resources, it can be either a package or a string representing
-an absolute path to the runtime binary entrypoint. When unset it will default
-to @code{docker-cli} package for the @code{'docker} runtime or to @code{podman}
-package for the @code{'podman} runtime.")
- (runtime-extra-arguments
- (list '())
- "A list of strings, gexps or file-like objects that will be placed
-after each @command{docker} or @command{podman} invokation.")
- (user
- (string "oci-container")
- "The user name under whose authority OCI runtime commands will be run.")
- (group
- (maybe-string)
- "The group name under whose authority OCI commands will be run. When
-using the @code{'podman} OCI runtime, this field will be ignored and the
-default group of the user configured in the @code{user} field will be used.")
- (subuids-range
- (maybe-subid-range)
- "An optional @code{subid-range} record allocating subuids for the user from
-the @code{user} field. When unset, with the rootless Podman OCI runtime, it
-defaults to @code{(subid-range (name \"oci-container\"))}.")
- (subgids-range
- (maybe-subid-range)
- "An optional @code{subid-range} record allocating subgids for the user from
-the @code{user} field. When unset, with the rootless Podman OCI runtime, it
-defaults to @code{(subid-range (name \"oci-container\"))}.")
- (containers
- (list-of-oci-containers '())
- "The list of @code{oci-container-configuration} records representing the
-containers to provision. Most users are supposed not to use this field and use
-the @code{oci-extension} record instead.")
- (networks
- (list-of-oci-networks '())
- "The list of @code{oci-network-configuration} records representing the
-networks to provision. Most users are supposed not to use this field and use
-the @code{oci-extension} record instead.")
- (volumes
- (list-of-oci-volumes '())
- "The list of @code{oci-volume-configuration} records representing the
-volumes to provision. Most users are supposed not to use this field and use
-the @code{oci-extension} record instead.")
- (verbose?
- (boolean #f)
- "When true, additional output will be printed, allowing to better follow the
-flow of execution."))
+;; (for-home (oci-configuration ...)) is not able to replace for-home? with #t,
+;; pk prints #f. Once for-home will be able to work with (gnu services configuration) the
+;; record can be migrated back to define-configuration.
+(define-record-type* <oci-configuration>
+ oci-configuration
+ make-oci-configuration
+ oci-configuration?
+ this-oci-configuration
+
+ (runtime oci-configuration-runtime
+ (default 'docker))
+ (runtime-cli oci-configuration-runtime-cli
+ (default #f)) ; package or string
+ (runtime-extra-arguments oci-configuration-runtime-extra-arguments ; strings or gexps
+ (default '())) ; or file-like objects
+ (user oci-configuration-user
+ (default "oci-container"))
+ (group oci-configuration-group ; string
+ (default #f))
+ (subuids-range oci-configuration-subuids-range ; subid-range
+ (default #f))
+ (subgids-range oci-configuration-subgids-range ; subid-range
+ (default #f))
+ (containers oci-configuration-containers ; oci-container-configurations
+ (default '()))
+ (networks oci-configuration-networks ; oci-network-configurations
+ (default '()))
+ (volumes oci-configuration-volumes ; oci-volume-configurations
+ (default '()))
+ (verbose? oci-configuration-verbose?
+ (default #f))
+ (home-service? oci-configuration-home-service?
+ (default for-home?) (innate)))
+
+;; TODO: This procedure can be dropped once we switch to define-configuration for
+;; oci-configuration.
+(define (oci-configuration-valid? config)
+ (define runtime-cli
+ (oci-configuration-runtime-cli config))
+ (define group
+ (oci-configuration-group config))
+ (define subuids-range
+ (oci-configuration-subuids-range config))
+ (define subgids-range
+ (oci-configuration-subgids-range config))
+ (and
+ (symbol?
+ (oci-sanitize-runtime (oci-configuration-runtime config)))
+ (or (eq? runtime-cli #f)
+ (package-or-string? runtime-cli))
+ (list? (oci-configuration-runtime-extra-arguments config))
+ (string? (oci-configuration-user config))
+ (or (eq? group #f)
+ (string? group))
+ (or (eq? subuids-range #f)
+ (subid-range? subuids-range))
+ (or (eq? subgids-range #f)
+ (subid-range? subgids-range))
+ (list-of-oci-containers?
+ (oci-configuration-containers config))
+ (list-of-oci-networks?
+ (oci-configuration-networks config))
+ (list-of-oci-volumes?
+ (oci-configuration-volumes config))
+ (boolean?
+ (oci-configuration-verbose? config))
+ (boolean?
+ (oci-configuration-home-service? config))))
(define (oci-runtime-system-environment runtime user)
(if (eq? runtime 'podman)
@@ -833,7 +847,7 @@ (define (oci-runtime-cli runtime runtime-cli path)
;; It is a user defined absolute path
runtime-cli
#~(string-append
- #$(if (not (maybe-value-set? runtime-cli))
+ #$(if (eq? runtime-cli #f)
path
runtime-cli)
#$(if (eq? 'podman runtime)
@@ -1577,18 +1591,27 @@ (define (oci-configuration->shepherd-services config)
(passwd:gid
(getpwnam #$user))))
(oci-runtime-group config (oci-configuration-group config))))
- (verbose? (oci-configuration-verbose? config)))
- (oci-state->shepherd-services runtime system-runtime-cli containers networks volumes
- #:user user
- #:group group
- #:verbose? verbose?
- #:runtime-extra-arguments
- runtime-extra-arguments
- #:runtime-environment
- (oci-runtime-system-environment runtime user)
- #:runtime-requirement
- (oci-runtime-system-requirement runtime)
- #:networks-requirement '(networking))))
+ (verbose? (oci-configuration-verbose? config))
+ (home-service?
+ (oci-configuration-home-service? config)))
+ (if home-service?
+ (oci-state->shepherd-services runtime home-runtime-cli containers networks volumes
+ #:verbose? verbose?
+ #:networks-name
+ (oci-networks-home-shepherd-name runtime)
+ #:volumes-name
+ (oci-volumes-home-shepherd-name runtime))
+ (oci-state->shepherd-services runtime system-runtime-cli containers networks volumes
+ #:user user
+ #:group group
+ #:verbose? verbose?
+ #:runtime-extra-arguments
+ runtime-extra-arguments
+ #:runtime-environment
+ (oci-runtime-system-environment runtime user)
+ #:runtime-requirement
+ (oci-runtime-system-requirement runtime)
+ #:networks-requirement '(networking)))))
(define (oci-service-subids config)
"Return a subids-extension record representing subuids and subgids required by
@@ -1616,14 +1639,14 @@ (define (oci-service-subids config)
(define subgid-ranges
(delete-duplicate-ranges
(cons
- (if (not (maybe-value-set? subgids))
+ (if (eq? subgids #f)
(subid-range (name user))
subgids)
container-users)))
(define subuid-ranges
(delete-duplicate-ranges
(cons
- (if (not (maybe-value-set? subuids))
+ (if (eq? subuids #f)
(subid-range (name user))
subuids)
container-users)))
@@ -1682,13 +1705,21 @@ (define (oci-service-profile runtime runtime-cli)
'()
(list
(cond
- ((maybe-value-set? runtime-cli)
+ ((not (eq? runtime-cli #f))
runtime-cli)
((eq? 'podman runtime)
podman)
(else
docker-cli))))))
+(define (oci-service-extension-wrap-validate extension)
+ (lambda (config)
+ (if (oci-configuration-valid? config)
+ (extension config)
+ (raise
+ (formatted-message
+ (G_ "Invalide oci-configuration ~a.") config)))))
+
(define (oci-configuration-extend config extension)
(oci-configuration
(inherit config)
@@ -1717,18 +1748,22 @@ (define oci-service-type
(extensions
(list
(service-extension profile-service-type
- (lambda (config)
- (let ((runtime-cli
- (oci-configuration-runtime-cli config))
- (runtime
- (oci-configuration-runtime config)))
- (oci-service-profile runtime runtime-cli))))
+ (oci-service-extension-wrap-validate
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli)))))
(service-extension subids-service-type
- oci-service-subids)
+ (oci-service-extension-wrap-validate
+ oci-service-subids))
(service-extension account-service-type
- oci-service-accounts)
+ (oci-service-extension-wrap-validate
+ oci-service-accounts))
(service-extension shepherd-root-service-type
- oci-configuration->shepherd-services)))
+ (oci-service-extension-wrap-validate
+ oci-configuration->shepherd-services))))
;; Concatenate OCI object lists.
(compose (lambda (args)
(fold oci-extension-merge
--
2.48.1
P
Re: OCI provisioning service
(address . 76081@debbugs.gnu.org)
06196cdd-ac29-4a03-b09c-98aed5f85a39@autistici.org
Hi,

On 3/18/25 18:50, paul wrote:
Toggle quote (71 lines)
> Hi,
>
> On 3/9/25 01:27, paul wrote:
>> Hi,
>>
>> On 3/4/25 13:39, paul wrote:
>>> Hi,
>>>
>>> On 2/27/25 00:01, paul wrote:
>>>> Hi guix,
>>>>
>>>> On 2/18/25 02:20, paul wrote:
>>>>> Hi,
>>>>>
>>>>> On 2/12/25 02:09, paul wrote:
>>>>>> Hi guix,
>>>>>>
>>>>>> On 2/9/25 21:38, paul wrote:
>>>>>>> I'm sending a v3 fixing a bug in the merge algorithm for volumes
>>>>>>> and networks.
>>>>>>>
>>>>>>> On 2/9/25 20:14, paul wrote:
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> I'm about to send a v2. v2 compared to the first revision
>>>>>>>> features:
>>>>>>>>
>>>>>>>>
>>>>>>>> - it actually compiles all the times :) (rev 1 referenced
>>>>>>>> oci-image too early for it to be working and generated a
>>>>>>>> compile time error, if you recompiled it sometimes went away so
>>>>>>>> I thought it was a problem of my setup. CI caught this)
>>>>>>>> - it allows more values to be overridden by eventual users of
>>>>>>>> the Scheme API
>>>>>>>> - it allows passing extra arguments directly after each podman
>>>>>>>> or docker invokation, allowing for example for overriding
>>>>>>>> podman --root and similar options.
>>>>>>>>
>>>>>>>> All of these tests should pass:
>>>>>>>>
>>>>>>>> guix shell -D guix -CPW -- make check-system
>>>>>>>> TESTS="oci-container oci-service-rootless-podman docker
>>>>>>>> docker-system rootless-podman oci-service-docker"
>>>>>>>>
>>>>>> I'm sending a v4 changing slightly the image loader, the same
>>>>>> tests as before are supposed to pass. Now the Home service [0] is
>>>>>> working for me with rootless podman. I'll try it on different
>>>>>> distros if I manage to.
>>>>>
>>>>> I'm sending a v5 implementing a Home service. The changes compared
>>>>> to v4 are pretty trivial as the plumbing was already there, the
>>>>> only downside is that I'm not able to use for-home? in
>>>>> define-configuration, so I had to reimplement oci-configuration
>>>>> with (guix records) and had to reimplement some validation (gnu
>>>>> services configuration) would figure out magically.
>>>>
>>>> I'm sending  a v6. Compared to v5 it resolves the conflicts with
>>>> master and it should fix the stop action for rootless podman backed
>>>> services.
>>>
>>> I'm sending a v7 fixing the restart action for podman backed services.
>>
>> I'm sending a v8 fixing some further bugs wrt to the start and stop
>> of podman backed services.
>
> In the hope of making the review easier, I'm sending a v9 that is
> completely the same of v8 in terms of changes but with smaller
> commits. Hopefully this makes it easier to review changes, please let
> me know if I can do anything to ease the workload for reviewers of
> this patch set.

I'm sending a v10 rebased on current master. The only difference with v9
is that this revision exports the
oci-{container,network,volume}-configuration->options procedures , that
can be useful when writing services that extend the oci-service-type.


cheers,

giacomo
G
G
Giacomo Leidi wrote on 5 May 00:57 -0700
[PATCH v10 4/7] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
e3227ad2c6019866db8f7f6e59652ecaec2b7b6f.1746431874.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/services/containers.scm (%oci-supported-runtimes): New variable;
(oci-runtime-cli): new variable;
(oci-runtime-name): new variable;
(oci-network-configuration): new variable;
(oci-volume-configuration): new variable;
(oci-configuration): new variable;
(oci-extension): new variable;
(oci-networks-shepherd-name): new variable;
(oci-service-type): new variable;
(oci-state->shepherd-services): new variable.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 306 ++++++--
gnu/services/containers.scm | 1304 ++++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 37 +-
gnu/tests/containers.scm | 999 ++++++++++++++++++++++++++-
4 files changed, 2394 insertions(+), 252 deletions(-)

Toggle diff (450 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 7f796c5fc94..de0d7ccfd5a 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -43782,59 +43782,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@command{docker run} or @command{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources, it can be either a package or a string representing
+an absolute path to the runtime binary entrypoint. When unset it will default
+to @code{docker-cli} package for the @code{'docker} runtime or to @code{podman}
+package for the @code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. Most users are supposed not to use this field and use
+the @code{oci-extension} record instead.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -43854,16 +43957,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @command{docker run}
+or @command{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -43871,22 +43974,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -43914,7 +44019,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -43925,10 +44030,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -43939,25 +44045,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} or @command{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker network create} or @command{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker volume create} or @command{podman volume create}
+invokation.
@end table
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 24f31c756b8..66b46456800 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -41,6 +41,7 @@ (define-module (gnu services containers)
#:use-module ((guix scripts pack) #:prefix pack:)
#:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
@@ -96,8 +97,82 @@ (define-module (gnu services containers)
oci-container-configuration-workdir
oci-container-configuration-extra-arguments
+ list-of-oci-containers?
+ list-of-oci-networks?
+ list-of-oci-volumes?
+
+ %oci-supported-runtimes
+ oci-sanitize-runtime
+ oci-runtime-system-environment
+ oci-runtime-system-extra-arguments
+ oci-runtime-system-requirement
+ oci-runtime-cli
+ oci-runtime-system-cli
+ oci-runtime-home-cli
+ oci-runtime-name
+ oci-runtime-group
+
+ oci-network-configuration
+ oci-network-configuration?
+ oci-network-configuration-fields
+ oci-network-configuration-name
+ oci-network-configuration-driver
+ oci-network-configuration-gateway
+ oci-network-configuration-internal?
+ oci-network-configuration-ip-range
+ oci-network-configuration-ipam-driver
+ oci-network-configuration-ipv6?
+ oci-network-configuration-subnet
+ oci-network-configuration-labels
+ oci-network-configuration-extra-arguments
+
+ oci-volume-configuration
+ oci-volume-configuration?
+ oci-volume-configuration-fields
+ oci-volume-configuration-name
+ oci-volume-configuration-labels
+ oci-volume-configuration-extra-arguments
+
+ oci-configuration
+ oci-configuration?
+ oci-configuration-runtime
+ oci-configuration-runtime-cli
+ oci-configuration-runtime-extra-arguments
+ oci-configuration-user
+ oci-configuration-gro
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 5 May 00:57 -0700
[PATCH v10 1/7] services: rootless-podman: Use login shell.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
2b78b4ce9b0a3a6c0bdbdec5bb16702a5b5083a3.1746431874.git.goodoldpaul@autistici.org
This commit allows for having PATH set when changing the owner of
/sys/fs/group.

* gnu/services/containers.scm (crgroups-fs-owner): Use login shell.

Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
---
gnu/services/containers.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Toggle diff (17 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index b3cd109ce6c..d5a211765a6 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
(rootless-podman-configuration-group-name config))
(program-file "cgroups2-fs-owner-entrypoint"
#~(system*
- (string-append #+bash-minimal "/bin/bash") "-c"
+ (string-append #+bash-minimal "/bin/bash") "-l" "-c"
(string-append "echo Setting /sys/fs/cgroup "
"group ownership to " #$group " && chown -v "
"root:" #$group " /sys/fs/cgroup && "

base-commit: 63088c295d81cc3d0e808c478d4fe479a2c90102
--
2.49.0
G
G
Giacomo Leidi wrote on 5 May 00:57 -0700
[PATCH v10 3/7] tests: oci-container: Set explicit timeouts.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
b129e5ea7e6d09888d784f4622c3ad6690e6e8eb.1746431874.git.goodoldpaul@autistici.org
* gnu/tests/docker.scm: Simplify %test-oci-container test case and add
explicit timeouts to tests outcomes.
---
gnu/tests/docker.scm | 99 ++++++++++++++++++--------------------------
1 file changed, 41 insertions(+), 58 deletions(-)

Toggle diff (127 lines)
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 90c8d0f8508..5dcf05a17e3 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -1,7 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 Danny Milosavljevic <dannym@scratchpost.org>
;;; Copyright © 2019-2023 Ludovic Courtès <ludo@gnu.org>
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -414,71 +414,54 @@ (define (run-oci-container-test)
(test-runner-current (system-test-runner #$output))
(test-begin "oci-container")
- (test-assert "containerd service running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'containerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (test-assert "containerd PID file present"
- (wait-for-file "/run/containerd/containerd.pid" marionette))
-
- (test-assert "dockerd running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'dockerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (sleep 10) ; let service start
+ (wait-for-file "/run/containerd/containerd.pid" marionette)
(test-assert "docker-guile running"
(marionette-eval
'(begin
(use-modules (gnu services herd))
- (match (start-service 'docker-guile)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
+ (wait-for-service 'docker-guile #:timeout 120)
+ #t)
marionette))
- (test-equal "passing host environment variables and volumes"
- '("value" "hello")
- (marionette-eval
- `(begin
- (use-modules (ice-9 popen)
- (ice-9 rdelim))
-
- (define slurp
- (lambda args
- (let* ((port (apply open-pipe* OPEN_READ args))
- (output (let ((line (read-line port)))
- (if (eof-object? line)
- ""
- line)))
- (status (close-pipe port)))
- output)))
- (let* ((response1 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
- (response2 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
+ (test-assert "passing host environment variables and volumes"
+ (begin
+ (define (run-test)
+ (marionette-eval
+ `(begin
+ (use-modules (ice-9 popen)
+ (ice-9 rdelim))
+
+ (define slurp
+ (lambda args
+ (let* ((port (apply open-pipe* OPEN_READ args))
+ (output (let ((line (read-line port)))
+ (if (eof-object? line)
+ ""
+ line)))
+ (status (close-pipe port)))
+ output)))
+ (let* ((response1 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
+ (response2 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
(display (call-with-input-file \"/shared.txt\" read-line)))")))
- (list response1 response2)))
- marionette))
+ (list response1 response2)))
+ marionette))
+ ;; Allow services to come up on slower machines
+ (let loop ((attempts 0))
+ (if (= attempts 60)
+ (error "Service didn't come up after more than 60 seconds")
+ (if (equal? '("value" "hello")
+ (run-test))
+ #t
+ (begin
+ (sleep 1)
+ (loop (+ 1 attempts))))))))
(test-end))))
--
2.49.0
G
G
Giacomo Leidi wrote on 5 May 00:57 -0700
[PATCH v10 5/7] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
cd5b10a3f3ecb770539718e8d6db11362087d73d.1746431874.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/services/containers.scm: Export lower-oci-image-state.
* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/services/containers.scm | 2 +
gnu/tests/containers.scm | 80 ++++++++++++++---------------
gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
3 files changed, 95 insertions(+), 87 deletions(-)

Toggle diff (274 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index 66b46456800..a8d10d842da 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -74,6 +74,8 @@ (define-module (gnu services containers)
oci-image-system
oci-image-grafts?
+ lower-oci-image-state
+
oci-container-configuration
oci-container-configuration?
oci-container-configuration-fields
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 5e6f39387e7..8cdd86e7ae3 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -69,13 +69,47 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-rootless-podman-os
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..07edd9d5341 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -57,6 +58,40 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments `(#:guile ,guile-3.0
+ #:builder
+ (let ((out (assoc-ref %outputs "out")))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port)))
+ #t)))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +208,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +217,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +349,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.49.0
G
G
Giacomo Leidi wrote on 5 May 00:57 -0700
[PATCH v10 7/7] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
91c9920430684861541c86a01c28bd46423102c8.1746431874.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 50 ++++++++++++++
gnu/local.mk | 1 +
3 files changed, 165 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (195 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index de0d7ccfd5a..3f4da28af72 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -51429,6 +51429,120 @@ Miscellaneous Home Services
(dicod-configuration @dots{})))
@end lisp
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 00000000000..938dde2f37a
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,50 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (oci-service-extension-wrap-validate
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli)))))
+ (service-extension home-shepherd-service-type
+ (oci-service-extension-wrap-validate
+ oci-configuration->shepherd-services))))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index e25fcc115be..1be38d6e217 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -103,6 +103,7 @@ GNU_SYSTEM_MODULES = \
%D%/home.scm \
%D%/home/services.scm \
%D%/home/services/admin.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.49.0
G
G
Giacomo Leidi wrote on 5 May 00:57 -0700
[PATCH v10 2/7] services: oci-container-configuration: Move to (gnu services containers).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
b4dd6bb5064d097efd8bb28a8c77b2072c8749f3.1746431874.git.goodoldpaul@autistici.org
This patch moves the oci-container-configuration and related
configuration records to (gnu services containers).
Public symbols are still exported for backwards
compatibility but since the oci-container-service-type will be
deprecated in favor of the more general oci-service-type, everything is
moved outside of the docker related module.

* gnu/services/docker.scm: Move everything related to oci-container-configuration
to...
* gnu/services/containers.scm: ...here.scm.

Change-Id: Iae599dd5cc7442eb632f0c1b3b12f6b928397ae7
---
gnu/services/containers.scm | 549 +++++++++++++++++++++++++++++++++-
gnu/services/docker.scm | 577 +++---------------------------------
2 files changed, 584 insertions(+), 542 deletions(-)

Toggle diff (537 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index d5a211765a6..24f31c756b8 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -17,19 +17,31 @@
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu services containers)
+ #:use-module (gnu image)
+ #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages containers)
+ #:use-module (gnu packages docker)
#:use-module (gnu packages file-systems)
#:use-module (gnu services)
#:use-module (gnu services base)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
+ #:use-module (gnu system)
#:use-module (gnu system accounts)
+ #:use-module (gnu system image)
#:use-module (gnu system shadow)
#:use-module (gnu system pam)
+ #:use-module (guix diagnostics)
#:use-module (guix gexp)
+ #:use-module (guix i18n)
+ #:use-module (guix monads)
#:use-module (guix packages)
+ #:use-module (guix profiles)
+ #:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix store)
#:use-module (srfi srfi-1)
+ #:use-module (ice-9 match)
#:export (rootless-podman-configuration
rootless-podman-configuration?
rootless-podman-configuration-fields
@@ -48,7 +60,44 @@ (define-module (gnu services containers)
rootless-podman-shepherd-services
rootless-podman-service-etc
- rootless-podman-service-type))
+ rootless-podman-service-type
+
+ oci-image
+ oci-image?
+ oci-image-fields
+ oci-image-repository
+ oci-image-tag
+ oci-image-value
+ oci-image-pack-options
+ oci-image-target
+ oci-image-system
+ oci-image-grafts?
+
+ oci-container-configuration
+ oci-container-configuration?
+ oci-container-configuration-fields
+ oci-container-configuration-user
+ oci-container-configuration-group
+ oci-container-configuration-command
+ oci-container-configuration-entrypoint
+ oci-container-configuration-host-environment
+ oci-container-configuration-environment
+ oci-container-configuration-image
+ oci-container-configuration-provision
+ oci-container-configuration-requirement
+ oci-container-configuration-log-file
+ oci-container-configuration-auto-start?
+ oci-container-configuration-respawn?
+ oci-container-configuration-shepherd-actions
+ oci-container-configuration-network
+ oci-container-configuration-ports
+ oci-container-configuration-volumes
+ oci-container-configuration-container-user
+ oci-container-configuration-workdir
+ oci-container-configuration-extra-arguments
+
+ oci-container-shepherd-service
+ %oci-container-accounts))
(define (gexp-or-string? value)
(or (gexp? value)
@@ -190,7 +239,7 @@ (define (rootless-podman-cgroups-limits-service config)
rootless-podman-shared-root-fs))
(one-shot? #t)
(documentation
- "Allow setting cgroups limits: cpu, cpuset, memory and
+ "Allow setting cgroups limits: cpu, cpuset, io, memory and
pids.")
(start
#~(make-forkexec-constructor
@@ -244,3 +293,497 @@ (define rootless-podman-service-type
(default-value (rootless-podman-configuration))
(description
"This service configures rootless @code{podman} on the Guix System.")))
+
+
+;;;
+;;; OCI container.
+;;;
+
+(define (oci-sanitize-pair pair delimiter)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (match pair
+ (((? valid? key) . (? valid? value))
+ #~(string-append #$key #$delimiter #$value))
+ (_
+ (raise
+ (formatted-message
+ (G_ "pair members must contain only strings, gexps or file-like objects
+but ~a was found")
+ pair)))))
+
+(define (oci-sanitize-mixed-list name value delimiter)
+ (map
+ (lambda (el)
+ (cond ((string? el) el)
+ ((pair? el) (oci-sanitize-pair el delimiter))
+ (else
+ (raise
+ (formatted-message
+ (G_ "~a members must be either a string or a pair but ~a was
+found!")
+ name el)))))
+ value))
+
+(define (oci-sanitize-host-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "host-environment" value "="))
+
+(define (oci-sanitize-environment value)
+ ;; Expected spec format:
+ ;; '(("HOME" . "/home/nobody") "JAVA_HOME=/java")
+ (oci-sanitize-mixed-list "environment" value "="))
+
+(define (oci-sanitize-ports value)
+ ;; Expected spec format:
+ ;; '(("8088" . "80") "2022:22")
+ (oci-sanitize-mixed-list "ports" value ":"))
+
+(define (oci-sanitize-volumes value)
+ ;; Expected spec format:
+ ;; '(("/mnt/dir" . "/dir") "/run/current-system/profile:/java")
+ (oci-sanitize-mixed-list "volumes" value ":"))
+
+(define (oci-sanitize-shepherd-actions value)
+ (map
+ (lambda (el)
+ (if (shepherd-action? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "shepherd-actions may only be shepherd-action records
+but ~a was found") el))))
+ value))
+
+(define (oci-sanitize-extra-arguments value)
+ (define (valid? member)
+ (or (string? member)
+ (gexp? member)
+ (file-like? member)))
+ (map
+ (lambda (el)
+ (if (valid? el)
+ el
+ (raise
+ (formatted-message
+ (G_ "extra arguments may only be strings, gexps or file-like objects
+but ~a was found") el))))
+ value))
+
+(define (oci-image-reference image)
+ (if (string? image)
+ image
+ (string-append (oci-image-repository image)
+ ":" (oci-image-tag image))))
+
+(define (oci-lowerable-image? image)
+ (or (manifest? image)
+ (operating-system? image)
+ (gexp? image)
+ (file-like? image)))
+
+(define (string-or-oci-image? image)
+ (or (string? image)
+ (oci-image? image)))
+
+(define list-of-symbols?
+ (list-of symbol?))
+
+(define-maybe/no-serialization string)
+
+(define-configuration/no-serialization oci-image
+ (repository
+ (string)
+ "A string like @code{myregistry.local:5000/testing/test-image} that names
+the OCI image.")
+ (tag
+ (string "latest")
+ "A string representing the OCI image tag. Defaults to @code{latest}.")
+ (value
+ (oci-lowerable-image)
+ "A @code{manifest} or @code{operating-system} record that will be lowered
+into an OCI compatible tarball. Otherwise this field's value can be a gexp
+or a file-like object that evaluates to an OCI compatible tarball.")
+ (pack-options
+ (list '())
+ "An optional set of keyword arguments that will be passed to the
+@code{docker-image} procedure from @code{guix scripts pack}. They can be used
+to replicate @command{guix pack} behavior:
+
+@lisp
+(oci-image
+ (repository \"guile\")
+ (tag \"3\")
+ (manifest (specifications->manifest '(\"guile\")))
+ (pack-options
+ '(#:symlinks ((\"/bin/guile\" -> \"bin/guile\"))
+ #:max-layers 2)))
+@end lisp
+
+If the @code{value} field is an @code{operating-system} record, this field's
+value will be ignored.")
+ (system
+ (maybe-string)
+ "Attempt to build for a given system, e.g. \"i686-linux\"")
+ (target
+ (maybe-string)
+ "Attempt to cross-build for a given triple, e.g. \"aarch64-linux-gnu\"")
+ (grafts?
+ (boolean #f)
+ "Whether to allow grafting or not in the pack build."))
+
+(define-configuration/no-serialization oci-container-configuration
+ (user
+ (string "oci-container")
+ "The user under whose authority docker commands will be run.")
+ (group
+ (string "docker")
+ "The group under whose authority docker commands will be run.")
+ (command
+ (list-of-strings '())
+ "Overwrite the default command (@code{CMD}) of the image.")
+ (entrypoint
+ (maybe-string)
+ "Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.")
+ (host-environment
+ (list '())
+ "Set environment variables in the host environment where @command{docker run}
+is invoked. This is especially useful to pass secrets from the host to the
+container without having them on the @command{docker run}'s command line: by
+setting the @code{MYSQL_PASSWORD} on the host and by passing
+@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
+possible to securely set values in the container environment. This field's
+value can be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to @code{make-forkexec-constructor}."
+ (sanitizer oci-sanitize-host-environment))
+ (environment
+ (list '())
+ "Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
+
+@lisp
+(list '(\"LANGUAGE\" . \"eo:ca:eu\")
+ \"JAVA_HOME=/opt/java\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-environment))
+ (image
+ (string-or-oci-image)
+ "The image used to build the container. It can be a string or an
+@code{oci-image} record. Strings are resolved by the Docker
+Engine, and follow the usual format
+@code{myregistry.local:5000/testing/test-image:tag}.")
+ (provision
+ (maybe-string)
+ "Set the name of the provisioned Shepherd service.")
+ (requirement
+ (list-of-symbols '())
+ "Set additional Shepherd services dependencies to the provisioned Shepherd
+service.")
+ (log-file
+ (maybe-string)
+ "When @code{log-file} is set, it names the file to which the service’s
+standard output and standard error are redirected. @code{log-file} is created
+if it does not exist, otherwise it is appended to.")
+ (auto-start?
+ (boolean #t)
+ "Whether this service should be started automatically by the Shepherd. If it
+is @code{#f} the service has to be started manually with @command{herd start}.")
+ (respawn?
+ (boolean #f)
+ "Whether to restart the service when it stops, for instance when the
+underlying process dies.")
+ (shepherd-actions
+ (list '())
+ "This is a list of @code{shepherd-action} records defining actions supported
+by the service."
+ (sanitizer oci-sanitize-shepherd-actions))
+ (network
+ (maybe-string)
+ "Set a Docker network for the spawned container.")
+ (ports
+ (list '())
+ "Set the port or port ranges to expose from the spawned container. This can
+be a list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"8080\" . \"80\")
+ \"10443:443\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-ports))
+ (volumes
+ (list '())
+ "Set volume mappings for the spawned container. This can be a
+list of pairs or strings, even mixed:
+
+@lisp
+(list '(\"/root/data/grafana\" . \"/var/lib/grafana\")
+ \"/gnu/store:/gnu/store\")
+@end lisp
+
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the Docker CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
+documentation for semantics."
+ (sanitizer oci-sanitize-volumes))
+ (container-user
+ (maybe-string)
+ "Set the current user inside the spawned container. You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#user,upstream}
+documentation for semantics.")
+ (workdir
+ (maybe-string)
+ "Set the current working for the spawned Shepherd service.
+You can refer to the
+@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
+documentation for semantics.")
+ (extra-arguments
+ (list '())
+ "A list of strings, gexps or file-like objects that will be directly passed
+to the @command{docker run} invokation."
+ (sanitizer oci-sanitize-extra-arguments)))
+
+(define oci-container-configuration->options
+ (lambda (config)
+ (let ((entrypoint
+ (oci-container-configuration-entrypoint config))
+ (network
+ (oci-container-configuration-network config))
+ (user
+ (oci-container-configuration-container-user config))
+ (workdir
+ (oci-container-configuration-workdir config)))
+ (apply append
+ (filter (compose not unspecified?)
+ `(,(if (maybe-value-set? entrypoint)
+ `("--entrypoint" ,entrypoint)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "--env" spec))
+ (oci-container-configuration-environment config))
+ ,(if (maybe-value-set? network)
+ `("--network" ,network)
+ '())
+ ,(if (maybe-value-set? user)
+ `("--user" ,user)
+ '())
+ ,(if (maybe-value-set? workdir)
+ `("--workdir" ,workdir)
+ '())
+ ,(append-map
+ (lambda (spec)
+ (list "-p" spec))
+ (oci-container-configuration-ports config))
+ ,(append-map
+ (lambda (spec)
+ (list "-v" spec))
+ (oci-container-configuration-volumes config))))))))
+
+(define* (get-keyword-value args keyword #:key (default #f))
+ (let ((kv (memq keyword args)))
+ (if (and kv (>= (length kv) 2))
+ (cadr kv)
+ default)))
+
+(define (lower-operating-system os target system)
+ (mlet* %store-monad
+ ((tarball
+ (lower-object
+ (system-image (os->image os #:type docker-image-type))
+ system
+ #:target target)))
+ (return tarball)))
+
+(define (lower-manifest name image target system)
+ (define value (oci-image-value image))
+ (define options (oci-image-pack-options image))
+ (define image-reference
+ (oci-image-reference image))
+ (define image-tag
+ (let* ((extra-options
+ (get-keyword-value options #:extra-options))
+ (image-tag-option
+ (and extra-options
+ (get-keyword-value extra-options #:image-tag))))
+ (if image-tag-option
+ '()
+ `(#:extra-options (#:image-tag ,image-reference)))))
+
+ (mlet* %store-monad
+ ((_ (set-grafting
+ (oci-image-grafts? image)))
+ (guile (set-guile-for-build (default-guile)))
+ (profile
+ (profile-derivation value
+ #:target target
+ #:system system
+ #:hooks '()
+ #:locales? #f))
+ (tarball (apply pack:docker-image
+ `(,name ,profile
+ ,@options
+ ,@image-tag
+ #:localstatedir? #t))))
+ (return tarball)))
+
+(define (lower-oci-image name image)
+ (define value (oci-image-value image))
+ (define image-target (oci-image-target image))
+ (define image-system (oci-image-system image))
+ (define target
+ (if (maybe-value-set? image-target)
+ image-target
+ (%current-target-system)))
+ (define system
+ (if (maybe-value-set? image-system)
+ image-system
+ (%current-system)))
+ (with-store store
+ (run-with-store store
+ (match value
+ ((? manifest? value)
+ (lower-manifest name image target system))
+ ((? operating-system? value)
+ (lower-operating-system value target system))
+ ((or (? gexp? value)
+ (? file-like? value))
+ value)
+ (_
+ (raise
+ (formatted-message
+ (G_ "oci-image value must contain only manifest,
+operating-system, gexp or file-like records but ~a was found")
+ value))))
+ #:target target
+ #:system system)))
+
+(define (%oci-image-loader name image tag)
+ (let ((docker (file-append docker-cli "/bin/docker"))
+ (tarball (lower-oci-image name image)))
+ (with-imported-modules '((guix build utils))
+ (program-file (format #f "~a-image-loader" name)
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 popen)
+ (ice-9 rdelim))
+
+ (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
+ (define line
+ (read-line
+ (open-input-pipe
+ (string-append #$docker " load -i " #$tarball))))
+
+ (unless (or (eof-object? line)
+ (string-null? line))
+ (format #t "~a~%" line)
+ (let ((repository&tag
+ (string-drop line
+ (string-length
+ "Loaded image: "))))
+
+ (invoke #$docker "tag" repository&tag #$tag)
+ (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
+
+(define (oci-container-shepherd-service config)
+ (define (guess-name name image)
+ (if (maybe-value-set? name)
+ name
+ (string-append "docker-"
+ (basename
+ (if (string? image)
+ (first (string-split image #\:))
+ (oci-image-repository image))))))
+
+ (let* ((docker (file-append docker-cli "/bin/docker"))
+ (actions (oci-container-configuration-shepherd-actions config))
+ (auto-start?
+ (oci-container-configuration-auto-start? config))
+ (user (oci-container-configuration-user config))
+ (group (oci-container-configuration-group config))
+ (host-environment
+ (oci-container-configuration-host-environment config))
+ (command (oci-container-configuration-command config))
+ (log-file (oci-container-configuration-log-file config))
+ (provision (oci-container-configuration-provision config))
+ (requirement (oci-container-configuration-requirement config))
+ (respawn?

This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 5 May 00:57 -0700
[PATCH v10 6/7] services: oci: Migrate oci-configuration to (guix records).
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
1f148e2994e78ed4614efe885380c06740515d0b.1746431874.git.goodoldpaul@autistici.org
This commit migrates oci-configuration to (guix records) singe it
appears (for-home (oci-configuration ...)) does not work as expected
with (gnu services configuration). This is supposed to be completely
transparent for users and can be reverted in the
future once this has been implemented.

* gnu/service/containers.scm: Migrate oci-configuration to (guix records).
---
gnu/services/containers.scm | 199 +++++++++++++++++++++---------------
1 file changed, 117 insertions(+), 82 deletions(-)

Toggle diff (283 lines)
diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
index a8d10d842da..a974227e164 100644
--- a/gnu/services/containers.scm
+++ b/gnu/services/containers.scm
@@ -39,6 +39,7 @@ (define-module (gnu services containers)
#:use-module (guix packages)
#:use-module (guix profiles)
#:use-module ((guix scripts pack) #:prefix pack:)
+ #:use-module (guix records)
#:use-module (guix store)
#:use-module (srfi srfi-1)
#:use-module (ice-9 format)
@@ -168,6 +169,7 @@ (define-module (gnu services containers)
oci-container-shepherd-service
oci-objects-merge-lst
oci-extension-merge
+ oci-service-extension-wrap-validate
oci-service-type
oci-service-accounts
oci-service-profile
@@ -395,7 +397,7 @@ (define (oci-runtime-name runtime)
(define (oci-runtime-group runtime maybe-group)
"Implement the logic behind selection of the group that is to be used by
Shepherd to execute OCI commands."
- (if (not (maybe-value-set? maybe-group))
+ (if (eq? maybe-group #f)
(if (eq? 'podman runtime)
"cgroup"
"docker")
@@ -766,62 +768,74 @@ (define (list-of-oci-networks? value)
(define (package-or-string? value)
(or (package? value) (string? value)))
-(define-maybe/no-serialization package-or-string)
-
-(define-configuration/no-serialization oci-configuration
- (runtime
- (symbol 'docker)
- "The OCI runtime to use to run commands. It can be either @code{'docker} or
-@code{'podman}."
- (sanitizer oci-sanitize-runtime))
- (runtime-cli
- (maybe-package-or-string)
- "The OCI runtime command line to be installed in the system profile and used
-to provision OCI resources, it can be either a package or a string representing
-an absolute path to the runtime binary entrypoint. When unset it will default
-to @code{docker-cli} package for the @code{'docker} runtime or to @code{podman}
-package for the @code{'podman} runtime.")
- (runtime-extra-arguments
- (list '())
- "A list of strings, gexps or file-like objects that will be placed
-after each @command{docker} or @command{podman} invokation.")
- (user
- (string "oci-container")
- "The user name under whose authority OCI runtime commands will be run.")
- (group
- (maybe-string)
- "The group name under whose authority OCI commands will be run. When
-using the @code{'podman} OCI runtime, this field will be ignored and the
-default group of the user configured in the @code{user} field will be used.")
- (subuids-range
- (maybe-subid-range)
- "An optional @code{subid-range} record allocating subuids for the user from
-the @code{user} field. When unset, with the rootless Podman OCI runtime, it
-defaults to @code{(subid-range (name \"oci-container\"))}.")
- (subgids-range
- (maybe-subid-range)
- "An optional @code{subid-range} record allocating subgids for the user from
-the @code{user} field. When unset, with the rootless Podman OCI runtime, it
-defaults to @code{(subid-range (name \"oci-container\"))}.")
- (containers
- (list-of-oci-containers '())
- "The list of @code{oci-container-configuration} records representing the
-containers to provision. Most users are supposed not to use this field and use
-the @code{oci-extension} record instead.")
- (networks
- (list-of-oci-networks '())
- "The list of @code{oci-network-configuration} records representing the
-networks to provision. Most users are supposed not to use this field and use
-the @code{oci-extension} record instead.")
- (volumes
- (list-of-oci-volumes '())
- "The list of @code{oci-volume-configuration} records representing the
-volumes to provision. Most users are supposed not to use this field and use
-the @code{oci-extension} record instead.")
- (verbose?
- (boolean #f)
- "When true, additional output will be printed, allowing to better follow the
-flow of execution."))
+;; (for-home (oci-configuration ...)) is not able to replace for-home? with #t,
+;; pk prints #f. Once for-home will be able to work with (gnu services configuration) the
+;; record can be migrated back to define-configuration.
+(define-record-type* <oci-configuration>
+ oci-configuration
+ make-oci-configuration
+ oci-configuration?
+ this-oci-configuration
+
+ (runtime oci-configuration-runtime
+ (default 'docker))
+ (runtime-cli oci-configuration-runtime-cli
+ (default #f)) ; package or string
+ (runtime-extra-arguments oci-configuration-runtime-extra-arguments ; strings or gexps
+ (default '())) ; or file-like objects
+ (user oci-configuration-user
+ (default "oci-container"))
+ (group oci-configuration-group ; string
+ (default #f))
+ (subuids-range oci-configuration-subuids-range ; subid-range
+ (default #f))
+ (subgids-range oci-configuration-subgids-range ; subid-range
+ (default #f))
+ (containers oci-configuration-containers ; oci-container-configurations
+ (default '()))
+ (networks oci-configuration-networks ; oci-network-configurations
+ (default '()))
+ (volumes oci-configuration-volumes ; oci-volume-configurations
+ (default '()))
+ (verbose? oci-configuration-verbose?
+ (default #f))
+ (home-service? oci-configuration-home-service?
+ (default for-home?) (innate)))
+
+;; TODO: This procedure can be dropped once we switch to define-configuration for
+;; oci-configuration.
+(define (oci-configuration-valid? config)
+ (define runtime-cli
+ (oci-configuration-runtime-cli config))
+ (define group
+ (oci-configuration-group config))
+ (define subuids-range
+ (oci-configuration-subuids-range config))
+ (define subgids-range
+ (oci-configuration-subgids-range config))
+ (and
+ (symbol?
+ (oci-sanitize-runtime (oci-configuration-runtime config)))
+ (or (eq? runtime-cli #f)
+ (package-or-string? runtime-cli))
+ (list? (oci-configuration-runtime-extra-arguments config))
+ (string? (oci-configuration-user config))
+ (or (eq? group #f)
+ (string? group))
+ (or (eq? subuids-range #f)
+ (subid-range? subuids-range))
+ (or (eq? subgids-range #f)
+ (subid-range? subgids-range))
+ (list-of-oci-containers?
+ (oci-configuration-containers config))
+ (list-of-oci-networks?
+ (oci-configuration-networks config))
+ (list-of-oci-volumes?
+ (oci-configuration-volumes config))
+ (boolean?
+ (oci-configuration-verbose? config))
+ (boolean?
+ (oci-configuration-home-service? config))))
(define (oci-runtime-system-environment runtime user)
(if (eq? runtime 'podman)
@@ -837,7 +851,7 @@ (define (oci-runtime-cli runtime runtime-cli path)
;; It is a user defined absolute path
runtime-cli
#~(string-append
- #$(if (not (maybe-value-set? runtime-cli))
+ #$(if (eq? runtime-cli #f)
path
runtime-cli)
#$(if (eq? 'podman runtime)
@@ -1581,18 +1595,27 @@ (define (oci-configuration->shepherd-services config)
(passwd:gid
(getpwnam #$user))))
(oci-runtime-group config (oci-configuration-group config))))
- (verbose? (oci-configuration-verbose? config)))
- (oci-state->shepherd-services runtime system-runtime-cli containers networks volumes
- #:user user
- #:group group
- #:verbose? verbose?
- #:runtime-extra-arguments
- runtime-extra-arguments
- #:runtime-environment
- (oci-runtime-system-environment runtime user)
- #:runtime-requirement
- (oci-runtime-system-requirement runtime)
- #:networks-requirement '(networking))))
+ (verbose? (oci-configuration-verbose? config))
+ (home-service?
+ (oci-configuration-home-service? config)))
+ (if home-service?
+ (oci-state->shepherd-services runtime home-runtime-cli containers networks volumes
+ #:verbose? verbose?
+ #:networks-name
+ (oci-networks-home-shepherd-name runtime)
+ #:volumes-name
+ (oci-volumes-home-shepherd-name runtime))
+ (oci-state->shepherd-services runtime system-runtime-cli containers networks volumes
+ #:user user
+ #:group group
+ #:verbose? verbose?
+ #:runtime-extra-arguments
+ runtime-extra-arguments
+ #:runtime-environment
+ (oci-runtime-system-environment runtime user)
+ #:runtime-requirement
+ (oci-runtime-system-requirement runtime)
+ #:networks-requirement '(networking)))))
(define (oci-service-subids config)
"Return a subids-extension record representing subuids and subgids required by
@@ -1620,14 +1643,14 @@ (define (oci-service-subids config)
(define subgid-ranges
(delete-duplicate-ranges
(cons
- (if (not (maybe-value-set? subgids))
+ (if (eq? subgids #f)
(subid-range (name user))
subgids)
container-users)))
(define subuid-ranges
(delete-duplicate-ranges
(cons
- (if (not (maybe-value-set? subuids))
+ (if (eq? subuids #f)
(subid-range (name user))
subuids)
container-users)))
@@ -1686,13 +1709,21 @@ (define (oci-service-profile runtime runtime-cli)
'()
(list
(cond
- ((maybe-value-set? runtime-cli)
+ ((not (eq? runtime-cli #f))
runtime-cli)
((eq? 'podman runtime)
podman)
(else
docker-cli))))))
+(define (oci-service-extension-wrap-validate extension)
+ (lambda (config)
+ (if (oci-configuration-valid? config)
+ (extension config)
+ (raise
+ (formatted-message
+ (G_ "Invalide oci-configuration ~a.") config)))))
+
(define (oci-configuration-extend config extension)
(oci-configuration
(inherit config)
@@ -1721,18 +1752,22 @@ (define oci-service-type
(extensions
(list
(service-extension profile-service-type
- (lambda (config)
- (let ((runtime-cli
- (oci-configuration-runtime-cli config))
- (runtime
- (oci-configuration-runtime config)))
- (oci-service-profile runtime runtime-cli))))
+ (oci-service-extension-wrap-validate
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli)))))
(service-extension subids-service-type
- oci-service-subids)
+ (oci-service-extension-wrap-validate
+ oci-service-subids))
(service-extension account-service-type
- oci-service-accounts)
+ (oci-service-extension-wrap-validate
+ oci-service-accounts))
(service-extension shepherd-root-service-type
- oci-configuration->shepherd-services)))
+ (oci-service-extension-wrap-validate
+ oci-configuration->shepherd-services))))
;; Concatenate OCI object lists.
(compose (lambda (args)
(fold oci-extension-merge
--
2.49.0
M
M
Maxim Cournoyer wrote on 14 May 21:53 -0700
Re: [bug#76081] [PATCH v10 1/7] services: rootless-podman: Use login shell.
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)(address . 76081@debbugs.gnu.org)
878qmyjwyi.fsf@gmail.com
Hi,

Giacomo Leidi <goodoldpaul@autistici.org> writes:

Toggle quote (26 lines)
> This commit allows for having PATH set when changing the owner of
> /sys/fs/group.
>
> * gnu/services/containers.scm (crgroups-fs-owner): Use login shell.
>
> Change-Id: I9510c637a5332325e05ca5ebc9dfd4de32685c50
> ---
> gnu/services/containers.scm | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
> index b3cd109ce6c..d5a211765a6 100644
> --- a/gnu/services/containers.scm
> +++ b/gnu/services/containers.scm
> @@ -140,7 +140,7 @@ (define (cgroups-fs-owner-entrypoint config)
> (rootless-podman-configuration-group-name config))
> (program-file "cgroups2-fs-owner-entrypoint"
> #~(system*
> - (string-append #+bash-minimal "/bin/bash") "-c"
> + (string-append #+bash-minimal "/bin/bash") "-l" "-c"
> (string-append "echo Setting /sys/fs/cgroup "
> "group ownership to " #$group " && chown -v "
> "root:" #$group " /sys/fs/cgroup && "
>
> base-commit: 63088c295d81cc3d0e808c478d4fe479a2c90102

I pushed this one.

--
Thanks,
Maxim
M
M
Maxim Cournoyer wrote on 14 May 21:54 -0700
Re: [bug#76081] [PATCH v10 2/7] services: oci-container-configuration: Move to (gnu services containers).
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)(address . 76081@debbugs.gnu.org)
874ixmjwy1.fsf@gmail.com
Hi,

Giacomo Leidi <goodoldpaul@autistici.org> writes:

Toggle quote (11 lines)
> This patch moves the oci-container-configuration and related
> configuration records to (gnu services containers).
> Public symbols are still exported for backwards
> compatibility but since the oci-container-service-type will be
> deprecated in favor of the more general oci-service-type, everything is
> moved outside of the docker related module.
>
> * gnu/services/docker.scm: Move everything related to oci-container-configuration
> to...
> * gnu/services/containers.scm: ...here.scm.

This one also.

--
Thanks,
Maxim
M
M
Maxim Cournoyer wrote on 15 May 00:21 -0700
Re: [bug#76081] [PATCH v10 3/7] tests: oci-container: Set explicit timeouts.
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)(address . 76081@debbugs.gnu.org)
87r00qibjd.fsf@gmail.com
Hi,

Giacomo Leidi <goodoldpaul@autistici.org> writes:

Toggle quote (3 lines)
> * gnu/tests/docker.scm: Simplify %test-oci-container test case and add
> explicit timeouts to tests outcomes.

[...]

Toggle quote (12 lines)
> (test-assert "docker-guile running"
> (marionette-eval
> '(begin
> (use-modules (gnu services herd))
> - (match (start-service 'docker-guile)
> - (#f #f)
> - (('service response-parts ...)
> - (match (assq-ref response-parts 'running)
> - ((pid) pid)))))
> + (wait-for-service 'docker-guile #:timeout 120)
> + #t)

You can drop the trailing '#t'.

Toggle quote (52 lines)
> marionette))
>
> - (test-equal "passing host environment variables and volumes"
> - '("value" "hello")
> - (marionette-eval
> - `(begin
> - (use-modules (ice-9 popen)
> - (ice-9 rdelim))
> -
> - (define slurp
> - (lambda args
> - (let* ((port (apply open-pipe* OPEN_READ args))
> - (output (let ((line (read-line port)))
> - (if (eof-object? line)
> - ""
> - line)))
> - (status (close-pipe port)))
> - output)))
> - (let* ((response1 (slurp
> - ,(string-append #$docker-cli "/bin/docker")
> - "exec" "docker-guile"
> - "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
> - (response2 (slurp
> - ,(string-append #$docker-cli "/bin/docker")
> - "exec" "docker-guile"
> - "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
> + (test-assert "passing host environment variables and volumes"
> + (begin
> + (define (run-test)
> + (marionette-eval
> + `(begin
> + (use-modules (ice-9 popen)
> + (ice-9 rdelim))
> +
> + (define slurp
> + (lambda args
> + (let* ((port (apply open-pipe* OPEN_READ args))
> + (output (let ((line (read-line port)))
> + (if (eof-object? line)
> + ""
> + line)))
> + (status (close-pipe port)))
> + output)))
> + (let* ((response1 (slurp
> + ,(string-append #$docker-cli "/bin/docker")
> + "exec" "docker-guile"
> + "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
> + (response2 (slurp
> + ,(string-append #$docker-cli "/bin/docker")
> + "exec" "docker-guile"
> + "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))

Please mind the 80 columns maximum width :-).

Toggle quote (7 lines)
> (display (call-with-input-file \"/shared.txt\" read-line)))")))
> - (list response1 response2)))
> - marionette))
> + (list response1 response2)))
> + marionette))
> + ;; Allow services to come up on slower machines

Please use complete punctuation for standalone comments.

Toggle quote (4 lines)
> + (let loop ((attempts 0))
> + (if (= attempts 60)
> + (error "Service didn't come up after more than 60 seconds")

I'm curious as to why this is necessary, given in the previous test we
wait for the docker-guile service to be ready?

--
Thanks,
Maxim
M
M
Maxim Cournoyer wrote on 15 May 18:38 -0700
Re: [bug#76081] [PATCH v10 4/7] services: Add oci-service-type.
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
87jz6hibbl.fsf@gmail.com
Hi,

Giacomo Leidi <goodoldpaul@autistici.org> writes:

Toggle quote (12 lines)
> This patch implements a generalization of the
> oci-container-service-type, which consequently is made deprecated. The
> oci-service-type, in addition to all the features from the
> oci-container-service-type, can now provision OCI networks and volumes.
> It only handles OCI objects creation, the user is supposed to handle
> state once the objects are provsioned.
>
> It currently supports two different OCI runtimes: Docker and rootless
> Podman. Both runtimes are tested to make sure provisioned containers
> can connect to each other through provisioned networks and can
> read/write data with provisioned volumes.

Sounds useful!

Toggle quote (14 lines)
> At last the Scheme API is thought to facilitate the implementation of a
> Guix Home service in the future.
>
> * gnu/services/containers.scm (%oci-supported-runtimes): New variable;
> (oci-runtime-cli): new variable;
> (oci-runtime-name): new variable;
> (oci-network-configuration): new variable;
> (oci-volume-configuration): new variable;
> (oci-configuration): new variable;
> (oci-extension): new variable;
> (oci-networks-shepherd-name): new variable;
> (oci-service-type): new variable;
> (oci-state->shepherd-services): new variable.

Nitpick: you can combine multiple variables/procedures, like:

(oci-runtime-cli, oci-runtime-name)
(oci-network-configuration, oci-volume-configuration)
[...]
(oci-service-type): New variables.

The above change log appears incomplete, for example oci-image-reference
was changed but isn't listed.

[...]

Toggle quote (57 lines)
> @lisp
> -(service oci-container-service-type
> - (list
> - (oci-container-configuration
> - (network "host")
> - (image
> - (oci-image
> - (repository "guile")
> - (tag "3")
> - (value (specifications->manifest '("guile")))
> - (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
> - #:max-layers 2))))
> - (entrypoint "/bin/guile")
> - (command
> - '("-c" "(display \"hello!\n\")")))
> - (oci-container-configuration
> - (image "prom/prometheus")
> - (ports
> - '(("9000" . "9000")
> - ("9090" . "9090"))))
> - (oci-container-configuration
> - (image "grafana/grafana:10.0.1")
> - (network "host")
> - (volumes
> - '("/var/lib/grafana:/var/lib/grafana")))))
> +(simple-service 'oci-provisioning
> + oci-service-type
> + (oci-extension
> + (networks
> + (list
> + (oci-network-configuration (name "monitoring"))))
> + (containers
> + (list
> + (oci-container-configuration
> + (network "monitoring")
> + (image
> + (oci-image
> + (repository "guile")
> + (tag "3")
> + (value (specifications->manifest '("guile")))
> + (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
> + #:max-layers 2))))
> + (entrypoint "/bin/guile")
> + (command
> + '("-c" "(display \"hello!\n\")")))
> + (oci-container-configuration
> + (image "prom/prometheus")
> + (network "host")
> + (ports
> + '(("9000" . "9000")
> + ("9090" . "9090"))))
> + (oci-container-configuration
> + (image "grafana/grafana:10.0.1")
> + (network "host")
> + (volumes
> + '("/var/lib/grafana:/var/lib/grafana")))))))

I was curious: was is the gain to have by using the extension mechanism
instead of directly accepting a list of 'oci-configuration' or similarly
named record to the oci-service-type?

Toggle quote (12 lines)
> @end lisp
>
> In this example three different Shepherd services are going to be added to the
> system. Each @code{oci-container-configuration} record translates to a
> -@code{docker run} invocation and its fields directly map to options. You can
> -refer to the
> -@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
> -documentation for the semantics of each value. If the images are not found,
> -they will be
> -@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
> +@command{docker run} or @command{podman run} invocation and its fields directly

I know not all of these instances are newly added, but @command should
only be used with a single command name. For a command line, use @samp.

Toggle quote (29 lines)
> +map to options. You can refer to the
> +@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
> +or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
> +upstream documentation for semantics of each value. If the images are not found,
> +they will be pulled. You can refer to the
> +@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
> +or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
> +upstream documentation for semantics. The
> services with @code{(network "host")} are going to be attached to the
> host network and are supposed to behave like native processes with regard to
> networking.
>
> @end defvar
>
> +@c %start of fragment
> +
> +@deftp {Data Type} oci-configuration
> +Available @code{oci-configuration} fields are:
> +
> +@table @asis
> +@item @code{runtime} (default: @code{'docker}) (type: symbol)
> +The OCI runtime to use to run commands. It can be either @code{'docker} or
> +@code{'podman}.
> +
> +@item @code{runtime-cli} (type: maybe-package-or-string)
> +The OCI runtime command line to be installed in the system profile and used
> +to provision OCI resources, it can be either a package or a string representing
> +an absolute path to the runtime binary entrypoint. When unset it will default

s/absolute path/absolute file name/

[...]

Toggle quote (5 lines)
> +@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
> +The list of @code{oci-container-configuration} records representing the
> +containers to provision. Most users are supposed not to use this field and use
> +the @code{oci-extension} record instead.

Maybe streamline to 'The use of the @code{oci-extension} record should
be preferred for most cases.' As I wondered above, what is the
benefit?

Toggle quote (6 lines)
> +
> +@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
> +The list of @code{oci-network-configuration} records representing the
> +containers to provision. Most users are supposed not to use this field and use
> +the @code{oci-extension} record instead.

Ditto.

Toggle quote (5 lines)
> +@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
> +The list of @code{oci-volumes-configuration} records representing the
> +containers to provision. Most users are supposed not to use this field and use
> +the @code{oci-extension} record instead.

Ditto.

[...]

Toggle quote (5 lines)
> diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
> index 24f31c756b8..66b46456800 100644
> --- a/gnu/services/containers.scm
> +++ b/gnu/services/containers.scm

[...]

Toggle quote (69 lines)
> +(define-configuration/no-serialization oci-volume-configuration
> + (name
> + (string)
> + "The name of the OCI volume to provision.")
> + (labels
> + (list '())
> + "The list of labels that will be used to tag the current volume."
> + (sanitizer oci-sanitize-labels))
> + (extra-arguments
> + (list '())
> + "A list of strings, gexps or file-like objects that will be directly passed
> +to the @command{docker volume create} or @command{podman volume create}
> +invocation."
> + (sanitizer oci-sanitize-extra-arguments)))
> +
> +(define (list-of-oci-volumes? value)
> + (list-of-oci-records? "volumes" oci-volume-configuration? value))
> +
> +(define-configuration/no-serialization oci-network-configuration
> + (name
> + (string)
> + "The name of the OCI network to provision.")
> + (driver
> + (maybe-string)
> + "The driver to manage the network.")
> + (gateway
> + (maybe-string)
> + "IPv4 or IPv6 gateway for the subnet.")
> + (internal?
> + (boolean #f)
> + "Restrict external access to the network")
> + (ip-range
> + (maybe-string)
> + "Allocate container ip from a sub-range in CIDR format.")
> + (ipam-driver
> + (maybe-string)
> + "IP Address Management Driver.")
> + (ipv6?
> + (boolean #f)
> + "Enable IPv6 networking.")
> + (subnet
> + (maybe-string)
> + "Subnet in CIDR format that represents a network segment.")
> + (labels
> + (list '())
> + "The list of labels that will be used to tag the current volume."
> + (sanitizer oci-sanitize-labels))
> + (extra-arguments
> + (list '())
> + "A list of strings, gexps or file-like objects that will be directly passed
> +to the @command{docker network create} or @command{podman network create}
> +invocation."
> + (sanitizer oci-sanitize-extra-arguments)))
> +
> +(define (list-of-oci-networks? value)
> + (list-of-oci-records? "networks" oci-network-configuration? value))
> +
> +(define (package-or-string? value)
> + (or (package? value) (string? value)))
> +
> +(define-maybe/no-serialization package-or-string)
> +
> +(define-configuration/no-serialization oci-configuration
> + (runtime
> + (symbol 'docker)
> + "The OCI runtime to use to run commands. It can be either @code{'docker} or
> +@code{'podman}."
> + (sanitizer oci-sanitize-runtime))

Instead of using explicit sanitizers, it'd be better to define a
oci-runtime? predicate and use it as a type in the configuration. This
way errors are reported with source location information.

[...]

Toggle quote (38 lines)
> +
> +(define (oci-runtime-system-environment runtime user)
> + (if (eq? runtime 'podman)
> + (list
> + #~(string-append
> + "HOME=" (passwd:dir (getpwnam #$user))))
> + #~()))
> +
> +(define (oci-runtime-cli runtime runtime-cli path)
> + "Return a gexp that, when lowered, evaluates to the file system path of the OCI
> +runtime command requested by the user."
> + (if (string? runtime-cli)
> + ;; It is a user defined absolute path
> + runtime-cli
> + #~(string-append
> + #$(if (not (maybe-value-set? runtime-cli))
> + path
> + runtime-cli)
> + #$(if (eq? 'podman runtime)
> + "/bin/podman"
> + "/bin/docker"))))
> +
> +(define* (oci-runtime-system-cli config #:key (path "/run/current-system/profile"))
> + (let ((runtime-cli
> + (oci-configuration-runtime-cli config))
> + (runtime
> + (oci-configuration-runtime config)))
> + (oci-runtime-cli runtime runtime-cli path)))
> +
> +(define (oci-runtime-home-cli config)
> + (let ((runtime-cli
> + (oci-configuration-runtime-cli config))
> + (runtime
> + (oci-configuration-runtime config)))
> + (oci-runtime-cli runtime runtime-cli
> + (string-append (getenv "HOME")
> + "/.guix-home/profile"))))

I'd replace the 'path' instances in the source with 'file' or 'file-name', to
better match our GNU conventions.

[...]

Toggle quote (40 lines)
> +(define (oci-object-exists? runtime runtime-cli object verbose?)
> + #~(lambda* (name #:key (format-string "{{.Name}}"))
> + (use-modules (ice-9 format)
> + (ice-9 match)
> + (ice-9 popen)
> + (ice-9 rdelim)
> + (srfi srfi-1))
> +
> + (define (read-lines file-or-port)
> + (define (loop-lines port)
> + (let loop ((lines '()))
> + (match (read-line port)
> + ((? eof-object?)
> + (reverse lines))
> + (line
> + (loop (cons line lines))))))
> +
> + (if (port? file-or-port)
> + (loop-lines file-or-port)
> + (call-with-input-file file-or-port
> + loop-lines)))
> +
> + #$(if (eq? runtime 'podman)
> + #~(let ((command
> + (list #$runtime-cli
> + #$object "exists" name)))
> + (when #$verbose?
> + (format #t "Running~{ ~a~}~%" command))
> + (define exit-code (status:exit-val (apply system* command)))
> + (when #$verbose?
> + (format #t "Exit code: ~a~%" exit-code))
> + (equal? EXIT_SUCCESS exit-code))
> + #~(let ((command
> + (string-append #$runtime-cli
> + " " #$object " ls --format "
> + "\"" format-string "\"")))
> + (when #$verbose?
> + (format #t "Running ~a~%" command))
> + (member name (read-lines (open-input-pipe command)))))))

That's a complex piece of staged code; have you considered placing it in
a supporting build-side module, e.g. gnu/build/oci-containers.scm ?
Then it could be more simply imported in the gexp environment and used.
See the gnu/build/jami-service.scm for an example.

Toggle quote (93 lines)
> +(define* (oci-image-loader runtime runtime-cli name image tag #:key (verbose? #f))
> + "Return a file-like object that, once lowered, will evaluate to a program able
> +to load IMAGE through RUNTIME-CLI and to tag it with TAG afterwards."
> + (let ((tarball (lower-oci-image name image)))
> (with-imported-modules '((guix build utils))
> - (program-file (format #f "~a-image-loader" name)
> + (program-file
> + (format #f "~a-image-loader" name)
> #~(begin
> (use-modules (guix build utils)
> + (ice-9 match)
> (ice-9 popen)
> - (ice-9 rdelim))
> -
> - (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
> - (define line
> - (read-line
> - (open-input-pipe
> - (string-append #$docker " load -i " #$tarball))))
> -
> - (unless (or (eof-object? line)
> - (string-null? line))
> - (format #t "~a~%" line)
> - (let ((repository&tag
> - (string-drop line
> - (string-length
> - "Loaded image: "))))
> -
> - (invoke #$docker "tag" repository&tag #$tag)
> - (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
> -
> -(define (oci-container-shepherd-service config)
> - (define (guess-name name image)
> - (if (maybe-value-set? name)
> - name
> - (string-append "docker-"
> - (basename
> - (if (string? image)
> - (first (string-split image #\:))
> - (oci-image-repository image))))))
> -
> - (let* ((docker (file-append docker-cli "/bin/docker"))
> - (actions (oci-container-configuration-shepherd-actions config))
> + (ice-9 rdelim)
> + (srfi srfi-1))
> + (define object-exists?
> + #$(oci-object-exists? runtime runtime-cli "image" verbose?))
> + (define load-command
> + (string-append #$runtime-cli
> + " load -i " #$tarball))
> +
> + (if (object-exists? #$tag #:format-string "{{.Repository}}:{{.Tag}}")
> + (format #t "~a image already exists, skipping.~%" #$tag)
> + (begin
> + (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
> + (when #$verbose?
> + (format #t "Running ~a~%" load-command))
> + (let ((line (read-line
> + (open-input-pipe load-command))))
> + (unless (or (eof-object? line)
> + (string-null? line))
> + (format #t "~a~%" line)
> + (let* ((repository&tag
> + (string-drop line
> + (string-length
> + "Loaded image: ")))
> + (tag-command
> + (list #$runtime-cli "tag" repository&tag #$tag))
> + (drop-old-tag-command
> + (list #$runtime-cli "image" "rm" "-f" repository&tag)))
> +
> + (unless (string=? repository&tag #$tag)
> + (when #$verbose?
> + (format #t "Running~{ ~a~}~%" tag-command))
> +
> + (let ((exit-code
> + (status:exit-val (apply system* tag-command))))
> + (format #t "Tagged ~a with ~a...~%" #$tarball #$tag)
> +
> + (when #$verbose?
> + (format #t "Exit code: ~a~%" exit-code))
> +
> + (when (equal? EXIT_SUCCESS exit-code)
> + (when #$verbose?
> + (format #t "Running~{ ~a~}~%" drop-old-tag-command))
> + (let ((drop-exit-code
> + (status:exit-val (apply system* drop-old-tag-command))))
> + (when #$verbose?
> + (format #t "Exit code: ~a~%" drop-exit-code))))))))))))))))
> +
> +(define (oci-container-run-invocation runtime runtime-cli name command image-reference
> + options runtime-extra-arguments run-extra-arguments)

That's a lot of arguments! Perhaps it could accept a record instead?
Please also mind the 80 chars max width.

Toggle quote (17 lines)
> + "Return a list representing the OCI runtime
> +invocation for running containers."
> + ;; run [OPTIONS] IMAGE [COMMAND] [ARG...]
> + `(,runtime-cli ,@runtime-extra-arguments "run" "--rm"
> + ,@(if (eq? runtime 'podman)
> + ;; This is because podman takes some time to
> + ;; release container names. --replace seems
> + ;; to be required to be able to restart services.
> + '("--replace")
> + '())
> + "--name" ,name
> + ,@options ,@run-extra-arguments
> + ,image-reference ,@command))
> +
> +(define* (oci-container-entrypoint runtime runtime-cli name image image-reference
> + invocation #:key (verbose? #f) (pre-script #~()))

The default value for a keyword argument is #f, so I'd omit the explicit
default for verbose?.

Toggle quote (15 lines)
> + "Return a file-like object that, once lowered, will evaluate to the entrypoint
> +for the Shepherd service that will run IMAGE through RUNTIME-CLI."
> + (program-file
> + (string-append "oci-entrypoint-" name)
> + #~(begin
> + (use-modules (ice-9 format)
> + (srfi srfi-1))
> + (when #$verbose?
> + (format #t "Running in verbose mode...~%")
> + (format #t "Current user: ~a ~a~%"
> + (getuid) (passwd:name (getpwuid (getuid))))
> + (format #t "Current group: ~a ~a~%"
> + (getgid) (group:name (getgrgid (getgid))))
> + (format #t "Current directory ~a~%" (getcwd)))

I'd use a single format call; you can format the string naturally like

(format #t "\
Running in verbose mode...
Current user: ~a ~a
...
" arg1 arg2, ...)


Toggle quote (6 lines)
> + (define invocation (list #$@invocation))
> + #$@pre-script
> + (when #$verbose?
> + (format #t "Running~{ ~a~}~%" invocation))
> + (apply execlp `(,(first invocation) ,@invocation)))))

You can more simply: (apply execlp (first invocation) inv
This message was truncated. Download the full message here.
M
M
Maxim Cournoyer wrote on 15 May 18:45 -0700
Re: [bug#76081] [PATCH v10 5/7] tests: Use lower-oci-image-state in container tests.
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)(address . 76081@debbugs.gnu.org)
87frh5ib0j.fsf@gmail.com
Hi,

Giacomo Leidi <goodoldpaul@autistici.org> writes:

Toggle quote (5 lines)
> This patch replaces boilerplate in container related tests with
> oci-image plumbing from (gnu services containers).
>
> * gnu/services/containers.scm: Export lower-oci-image-state.

I don't think that's a nice API to export; you can instead use the

(define lower-oci-image-state
(@@ (gnu services containers) lower-oci-image-state)

trick to expose internals (white box testing).

Toggle quote (15 lines)
> * gnu/tests/containers.scm (%oci-tarball): New variable;
> (run-rootless-podman-test): use %oci-tarball;
> (build-tarball&run-rootless-podman-test): drop procedure.
> * gnu/tests/docker.scm (%docker-tarball): New variable;
> (build-tarball&run-docker-test): use %docker-tarball;
> (%docker-system-tarball): New variable;
> (build-tarball&run-docker-system-test): new procedure.
>
> Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
> ---
> gnu/services/containers.scm | 2 +
> gnu/tests/containers.scm | 80 ++++++++++++++---------------
> gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
> 3 files changed, 95 insertions(+), 87 deletions(-)

The diffstats suggest the result is not that impactful in terms of
reducing boilerplate, ah!

Toggle quote (37 lines)
> diff --git a/gnu/services/containers.scm b/gnu/services/containers.scm
> index 66b46456800..a8d10d842da 100644
> --- a/gnu/services/containers.scm
> +++ b/gnu/services/containers.scm
> @@ -74,6 +74,8 @@ (define-module (gnu services containers)
> oci-image-system
> oci-image-grafts?
>
> + lower-oci-image-state
> +
> oci-container-configuration
> oci-container-configuration?
> oci-container-configuration-fields
> diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
> index 5e6f39387e7..8cdd86e7ae3 100644
> --- a/gnu/tests/containers.scm
> +++ b/gnu/tests/containers.scm
> @@ -69,13 +69,47 @@ (define %rootless-podman-os
> (supplementary-groups '("wheel" "netdev" "cgroup"
> "audio" "video")))))))
>
> -(define (run-rootless-podman-test oci-tarball)
> +(define %oci-tarball
> + (lower-oci-image-state
> + "guile-guest"
> + (packages->manifest
> + (list
> + guile-3.0 guile-json-3
> + (package
> + (name "guest-script")
> + (version "0")
> + (source #f)
> + (build-system trivial-build-system)
> + (arguments `(#:guile ,guile-3.0
> + #:builder
> + (let ((out (assoc-ref %outputs "out")))

I'd use a gexp for the builder expression here, with gexp variables,
e.g. #$output.

Toggle quote (6 lines)
> + (mkdir out)
> + (call-with-output-file (string-append out "/a.scm")
> + (lambda (port)
> + (display "(display \"hello world\n\")" port)))
> + #t)))

Trailing #t are no longer part of phases/builders in Guix :-).

Toggle quote (122 lines)
> + (synopsis "Display hello world using Guile")
> + (description "This package displays the text \"hello world\" on the
> +standard output device and then enters a new line.")
> + (home-page #f)
> + (license license:public-domain))))
> + '(#:entry-point "bin/guile"
> + #:localstatedir? #t
> + #:extra-options (#:image-tag "guile-guest")
> + #:symlinks (("/bin/Guile" -> "bin/guile")
> + ("aa.scm" -> "a.scm")))
> + "guile-guest"
> + (%current-target-system)
> + (%current-system)
> + #f))
> +
> +(define (run-rootless-podman-test)
>
> (define os
> (marionette-operating-system
> (operating-system-with-gc-roots
> %rootless-podman-os
> - (list oci-tarball))
> + (list %oci-tarball))
> #:imported-modules '((gnu services herd)
> (guix combinators))))
>
> @@ -254,7 +288,7 @@ (define (run-rootless-podman-test oci-tarball)
> (let* ((loaded (slurp ,(string-append #$podman
> "/bin/podman")
> "load" "-i"
> - ,#$oci-tarball))
> + ,#$%oci-tarball))
> (repository&tag "localhost/guile-guest:latest")
> (response1 (slurp
> ,(string-append #$podman "/bin/podman")
> @@ -307,49 +341,11 @@ (define (run-rootless-podman-test oci-tarball)
>
> (gexp->derivation "rootless-podman-test" test))
>
> -(define (build-tarball&run-rootless-podman-test)
> - (mlet* %store-monad
> - ((_ (set-grafting #f))
> - (guile (set-guile-for-build (default-guile)))
> - (guest-script-package ->
> - (package
> - (name "guest-script")
> - (version "0")
> - (source #f)
> - (build-system trivial-build-system)
> - (arguments `(#:guile ,guile-3.0
> - #:builder
> - (let ((out (assoc-ref %outputs "out")))
> - (mkdir out)
> - (call-with-output-file (string-append out "/a.scm")
> - (lambda (port)
> - (display "(display \"hello world\n\")" port)))
> - #t)))
> - (synopsis "Display hello world using Guile")
> - (description "This package displays the text \"hello world\" on the
> -standard output device and then enters a new line.")
> - (home-page #f)
> - (license license:public-domain)))
> - (profile (profile-derivation (packages->manifest
> - (list guile-3.0 guile-json-3
> - guest-script-package))
> - #:hooks '()
> - #:locales? #f))
> - (tarball (pack:docker-image
> - "docker-pack" profile
> - #:symlinks '(("/bin/Guile" -> "bin/guile")
> - ("aa.scm" -> "a.scm"))
> - #:extra-options
> - '(#:image-tag "guile-guest")
> - #:entry-point "bin/guile"
> - #:localstatedir? #t)))
> - (run-rootless-podman-test tarball)))
> -
> (define %test-rootless-podman
> (system-test
> (name "rootless-podman")
> (description "Test rootless Podman service.")
> - (value (build-tarball&run-rootless-podman-test))))
> + (value (run-rootless-podman-test))))
>
>
> (define %oci-rootless-podman-os
> diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
> index 5dcf05a17e3..07edd9d5341 100644
> --- a/gnu/tests/docker.scm
> +++ b/gnu/tests/docker.scm
> @@ -26,6 +26,7 @@ (define-module (gnu tests docker)
> #:use-module (gnu system image)
> #:use-module (gnu system vm)
> #:use-module (gnu services)
> + #:use-module (gnu services containers)
> #:use-module (gnu services dbus)
> #:use-module (gnu services networking)
> #:use-module (gnu services docker)
> @@ -57,6 +58,40 @@ (define %docker-os
> (service containerd-service-type)
> (service docker-service-type)))
>
> +(define %docker-tarball
> + (lower-oci-image-state
> + "guile-guest"
> + (packages->manifest
> + (list
> + guile-3.0 guile-json-3
> + (package
> + (name "guest-script")
> + (version "0")
> + (source #f)
> + (build-system trivial-build-system)
> + (arguments `(#:guile ,guile-3.0
> + #:builder
> + (let ((out (assoc-ref %outputs "out")))
> + (mkdir out)
> + (call-with-output-file (string-append out "/a.scm")
> + (lambda (port)
> + (display "(display \"hello world\n\")" port)))
> + #t)))

Ditto.

Otherwise LGTM.

--
Thanks,
Maxim
M
M
Maxim Cournoyer wrote on 15 May 19:02 -0700
Re: [bug#76081] [PATCH v10 6/7] services: oci: Migrate oci-configuration to (guix records).
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)(address . 76081@debbugs.gnu.org)
87bjrtia85.fsf@gmail.com
Hi,

Giacomo Leidi <goodoldpaul@autistici.org> writes:

Toggle quote (8 lines)
> This commit migrates oci-configuration to (guix records) singe it
> appears (for-home (oci-configuration ...)) does not work as expected
> with (gnu services configuration). This is supposed to be completely
> transparent for users and can be reverted in the
> future once this has been implemented.
>
> * gnu/service/containers.scm: Migrate oci-configuration to (guix records).

I'd like to understand and resolve this issue and avoid pushing this
commit. What is the errors you are seeing? It at least byte-compiles
fine with this commit reverted and the following diff applied:

Toggle snippet (27 lines)
modified gnu/home/services/containers.scm
@@ -32,16 +32,14 @@ (define home-oci-service-type
(extensions
(list
(service-extension home-profile-service-type
- (oci-service-extension-wrap-validate
- (lambda (config)
- (let ((runtime-cli
- (oci-configuration-runtime-cli config))
- (runtime
- (oci-configuration-runtime config)))
- (oci-service-profile runtime runtime-cli)))))
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli))))
(service-extension home-shepherd-service-type
- (oci-service-extension-wrap-validate
- oci-configuration->shepherd-services))))
+ oci-configuration->shepherd-services)))
(extend
(lambda (config extension)
(for-home

Also, there are other home services using a define-configuration'd
record with for-home, such as the home-snuik-service-type, so it should
be possible to make it work.

--
Thanks,
Maxim
M
M
Maxim Cournoyer wrote on 15 May 19:07 -0700
Re: [bug#76081] [PATCH v10 7/7] home: Add home-oci-service-type.
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
877c2hi9zh.fsf@gmail.com
Hi,

Giacomo Leidi <goodoldpaul@autistici.org> writes:

Toggle quote (4 lines)
> * gnu/home/service/containers.scm: New file;
> * gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
> * doc/guix.texi (OCI backed services): Document it.

LGTM, but please break long lines so they fit in 80 columns. This
shouldn't be merged until 6/7 has been understood; if it can be
eliminated this will need to be adjusted like:

Toggle snippet (27 lines)
modified gnu/home/services/containers.scm
@@ -32,16 +32,14 @@ (define home-oci-service-type
(extensions
(list
(service-extension home-profile-service-type
- (oci-service-extension-wrap-validate
- (lambda (config)
- (let ((runtime-cli
- (oci-configuration-runtime-cli config))
- (runtime
- (oci-configuration-runtime config)))
- (oci-service-profile runtime runtime-cli)))))
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli))))
(service-extension home-shepherd-service-type
- (oci-service-extension-wrap-validate
- oci-configuration->shepherd-services))))
+ oci-configuration->shepherd-services)))
(extend
(lambda (config extension)
(for-home

--
Thanks,
Maxim
M
M
Maxim Cournoyer wrote on 15 May 19:07 -0700
control message for bug #76081
(address . control@debbugs.gnu.org)
875xi1i9z6.fsf@gmail.com
tags 76081 + moreinfo
quit
P
Re: [bug#76081] [PATCH v10 3/7] tests: oci-container: Set explicit timeouts.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 76081@debbugs.gnu.org)
edfa6206-2784-40cf-92b4-cbe28cb42282@autistici.org
Hi Maxim,

thank you for your feedback, I should have addressed all your comments.

On 5/15/25 09:21, Maxim Cournoyer wrote:
Toggle quote (6 lines)
>> + (let loop ((attempts 0))
>> + (if (= attempts 60)
>> + (error "Service didn't come up after more than 60 seconds")
> I'm curious as to why this is necessary, given in the previous test we
> wait for the docker-guile service to be ready?
>
I think it is because between once the docker run command is run and the
container is actually started and available through docker ps, there
seems to be a delay. If the system is not overloaded it is minimal, but
it happened to me that similarly to what happened in issue 72740
sometimes container are slow to start.

I think the core problem is that the Shepherd services goes to early in
the running state, I should make it stay in starting state until the
container is actually started but I don't know how. If you have any
pointers on how to achieve this I would be very happy to learn more. I
would address it in a separate issue if you agree.

It happened to me that running the test suite while compiling other guix
packages could make the test fail, so I think if we keep this patch we
could be safer against flaky tests. What do you think?

thank you,


cheers,

giacomo
Attachment: file
M
M
Maxim Cournoyer wrote on 20 May 02:23 -0700
(name . paul)(address . goodoldpaul@autistici.org)(address . 76081@debbugs.gnu.org)
8734czbppq.fsf@gmail.com
Hi Paul,

paul <goodoldpaul@autistici.org> writes:

Toggle quote (27 lines)
> Hi Maxim,
>
> thank you for your feedback, I should have addressed all your comments.
>
> On 5/15/25 09:21, Maxim Cournoyer wrote:
>>> + (let loop ((attempts 0))
>>> + (if (= attempts 60)
>>> + (error "Service didn't come up after more than 60 seconds")
>> I'm curious as to why this is necessary, given in the previous test we
>> wait for the docker-guile service to be ready?
>>
> I think it is because between once the docker run command is run and
> the container is actually started and available through docker ps,
> there seems to be a delay. If the system is not overloaded it is
> minimal, but it happened to me that similarly to what happened in
> issue 72740 sometimes container are slow to start.
>
> I think the core problem is that the Shepherd services goes to early
> in the running state, I should make it stay in starting state until
> the container is actually started but I don't know how. If you have
> any pointers on how to achieve this I would be very happy to learn
> more. I would address it in a separate issue if you agree.
>
> It happened to me that running the test suite while compiling other
> guix packages could make the test fail, so I think if we keep this
> patch we could be safer against flaky tests. What do you think?

Thanks for explaining; yes, it seems reasonable to retry for now, and
it'd be nice if you could address the root cause in the future.

--
Thanks,
Maxim
P
Re: [bug#76081] [PATCH v10 4/7] services: Add oci-service-type.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)
e3a79b4d-701c-4054-bee4-0bd3ca60b186@autistici.org
Hi,

On 5/16/25 03:38, Maxim Cournoyer wrote:
Toggle quote (59 lines)
>> @lisp
>> -(service oci-container-service-type
>> - (list
>> - (oci-container-configuration
>> - (network "host")
>> - (image
>> - (oci-image
>> - (repository "guile")
>> - (tag "3")
>> - (value (specifications->manifest '("guile")))
>> - (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
>> - #:max-layers 2))))
>> - (entrypoint "/bin/guile")
>> - (command
>> - '("-c" "(display \"hello!\n\")")))
>> - (oci-container-configuration
>> - (image "prom/prometheus")
>> - (ports
>> - '(("9000" . "9000")
>> - ("9090" . "9090"))))
>> - (oci-container-configuration
>> - (image "grafana/grafana:10.0.1")
>> - (network "host")
>> - (volumes
>> - '("/var/lib/grafana:/var/lib/grafana")))))
>> +(simple-service 'oci-provisioning
>> + oci-service-type
>> + (oci-extension
>> + (networks
>> + (list
>> + (oci-network-configuration (name "monitoring"))))
>> + (containers
>> + (list
>> + (oci-container-configuration
>> + (network "monitoring")
>> + (image
>> + (oci-image
>> + (repository "guile")
>> + (tag "3")
>> + (value (specifications->manifest '("guile")))
>> + (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
>> + #:max-layers 2))))
>> + (entrypoint "/bin/guile")
>> + (command
>> + '("-c" "(display \"hello!\n\")")))
>> + (oci-container-configuration
>> + (image "prom/prometheus")
>> + (network "host")
>> + (ports
>> + '(("9000" . "9000")
>> + ("9090" . "9090"))))
>> + (oci-container-configuration
>> + (image "grafana/grafana:10.0.1")
>> + (network "host")
>> + (volumes
>> + '("/var/lib/grafana:/var/lib/grafana")))))))
> I was curious: was is the gain to have by using the extension mechanism
> instead of directly accepting a list of 'oci-configuration' or similarly
> named record to the oci-service-type?
One can always pass a list of
'oci-{container,network,volume}-configuration's to an 'oci-configuration':

(service oci-service-type
(oci-configuration
(runtime 'podman)
(containers
(list
(oci-container-configuration ...)))))


but you loose the deduplication checks done by the extension mechanisms.
This is useful especially for containers: both System/Home services and
users of plain 'simple-service' in the system configuration can add
Shepherd services for different reasons, if users do not set an explicit
value to the 'provision' field of 'oci-container-configuration', the
Shepherd service name will be derived from the image. So the
'oci-extension' suggestion in the documentation is to take care of
duplicates.
Toggle quote (43 lines)
>> +(define (oci-object-exists? runtime runtime-cli object verbose?)
>> + #~(lambda* (name #:key (format-string "{{.Name}}"))
>> + (use-modules (ice-9 format)
>> + (ice-9 match)
>> + (ice-9 popen)
>> + (ice-9 rdelim)
>> + (srfi srfi-1))
>> +
>> + (define (read-lines file-or-port)
>> + (define (loop-lines port)
>> + (let loop ((lines '()))
>> + (match (read-line port)
>> + ((? eof-object?)
>> + (reverse lines))
>> + (line
>> + (loop (cons line lines))))))
>> +
>> + (if (port? file-or-port)
>> + (loop-lines file-or-port)
>> + (call-with-input-file file-or-port
>> + loop-lines)))
>> +
>> + #$(if (eq? runtime 'podman)
>> + #~(let ((command
>> + (list #$runtime-cli
>> + #$object "exists" name)))
>> + (when #$verbose?
>> + (format #t "Running~{ ~a~}~%" command))
>> + (define exit-code (status:exit-val (apply system* command)))
>> + (when #$verbose?
>> + (format #t "Exit code: ~a~%" exit-code))
>> + (equal? EXIT_SUCCESS exit-code))
>> + #~(let ((command
>> + (string-append #$runtime-cli
>> + " " #$object " ls --format "
>> + "\"" format-string "\"")))
>> + (when #$verbose?
>> + (format #t "Running ~a~%" command))
>> + (member name (read-lines (open-input-pipe command)))))))
> That's a complex piece of staged code; have you considered placing it in
> a supporting build-side module, e.g. gnu/build/oci-containers.scm ?
> Then it could be more simply imported in the gexp environment and used.
> See the gnu/build/jami-service.scm for an example.
Thank you I was not aware of this option, I tried to move as much as I
could to gnu/build/oci-containers.scm.
Toggle quote (94 lines)
>> +(define* (oci-image-loader runtime runtime-cli name image tag #:key (verbose? #f))
>> + "Return a file-like object that, once lowered, will evaluate to a program able
>> +to load IMAGE through RUNTIME-CLI and to tag it with TAG afterwards."
>> + (let ((tarball (lower-oci-image name image)))
>> (with-imported-modules '((guix build utils))
>> - (program-file (format #f "~a-image-loader" name)
>> + (program-file
>> + (format #f "~a-image-loader" name)
>> #~(begin
>> (use-modules (guix build utils)
>> + (ice-9 match)
>> (ice-9 popen)
>> - (ice-9 rdelim))
>> -
>> - (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
>> - (define line
>> - (read-line
>> - (open-input-pipe
>> - (string-append #$docker " load -i " #$tarball))))
>> -
>> - (unless (or (eof-object? line)
>> - (string-null? line))
>> - (format #t "~a~%" line)
>> - (let ((repository&tag
>> - (string-drop line
>> - (string-length
>> - "Loaded image: "))))
>> -
>> - (invoke #$docker "tag" repository&tag #$tag)
>> - (format #t "Tagged ~a with ~a...~%" #$tarball #$tag))))))))
>> -
>> -(define (oci-container-shepherd-service config)
>> - (define (guess-name name image)
>> - (if (maybe-value-set? name)
>> - name
>> - (string-append "docker-"
>> - (basename
>> - (if (string? image)
>> - (first (string-split image #\:))
>> - (oci-image-repository image))))))
>> -
>> - (let* ((docker (file-append docker-cli "/bin/docker"))
>> - (actions (oci-container-configuration-shepherd-actions config))
>> + (ice-9 rdelim)
>> + (srfi srfi-1))
>> + (define object-exists?
>> + #$(oci-object-exists? runtime runtime-cli "image" verbose?))
>> + (define load-command
>> + (string-append #$runtime-cli
>> + " load -i " #$tarball))
>> +
>> + (if (object-exists? #$tag #:format-string "{{.Repository}}:{{.Tag}}")
>> + (format #t "~a image already exists, skipping.~%" #$tag)
>> + (begin
>> + (format #t "Loading image for ~a from ~a...~%" #$name #$tarball)
>> + (when #$verbose?
>> + (format #t "Running ~a~%" load-command))
>> + (let ((line (read-line
>> + (open-input-pipe load-command))))
>> + (unless (or (eof-object? line)
>> + (string-null? line))
>> + (format #t "~a~%" line)
>> + (let* ((repository&tag
>> + (string-drop line
>> + (string-length
>> + "Loaded image: ")))
>> + (tag-command
>> + (list #$runtime-cli "tag" repository&tag #$tag))
>> + (drop-old-tag-command
>> + (list #$runtime-cli "image" "rm" "-f" repository&tag)))
>> +
>> + (unless (string=? repository&tag #$tag)
>> + (when #$verbose?
>> + (format #t "Running~{ ~a~}~%" tag-command))
>> +
>> + (let ((exit-code
>> + (status:exit-val (apply system* tag-command))))
>> + (format #t "Tagged ~a with ~a...~%" #$tarball #$tag)
>> +
>> + (when #$verbose?
>> + (format #t "Exit code: ~a~%" exit-code))
>> +
>> + (when (equal? EXIT_SUCCESS exit-code)
>> + (when #$verbose?
>> + (format #t "Running~{ ~a~}~%" drop-old-tag-command))
>> + (let ((drop-exit-code
>> + (status:exit-val (apply system* drop-old-tag-command))))
>> + (when #$verbose?
>> + (format #t "Exit code: ~a~%" drop-exit-code))))))))))))))))
>> +
>> +(define (oci-container-run-invocation runtime runtime-cli name command image-reference
>> + options runtime-extra-arguments run-extra-arguments)
> That's a lot of arguments! Perhaps it could accept a record instead?
> Please also mind the 80 chars max width.
Good idea, I made an intermediate oci-container-invocation record.
Toggle quote (29 lines)
>> + "Return a list representing the OCI runtime
>> +invocation for running containers."
>> + ;; run [OPTIONS] IMAGE [COMMAND] [ARG...]
>> + `(,runtime-cli ,@runtime-extra-arguments "run" "--rm"
>> + ,@(if (eq? runtime 'podman)
>> + ;; This is because podman takes some time to
>> + ;; release container names. --replace seems
>> + ;; to be required to be able to restart services.
>> + '("--replace")
>> + '())
>> + "--name" ,name
>> + ,@options ,@run-extra-arguments
>> + ,image-reference ,@command))
>> +
>> +(define* (oci-container-entrypoint runtime runtime-cli name image image-reference
>> + invocation #:key (verbose? #f) (pre-script #~()))
> The default value for a keyword argument is #f, so I'd omit the explicit
> default for verbose?.
>> + (begin
>> + (when #$verbose?
>> + (format #t "Running~{ ~a~}~%" invocation))
>> + (let ((exit-code (status:exit-val (apply system* invocation))))
>> + (when #$verbose?
>> + (format #t "Exit code: ~a~%" exit-code))))))
>> + (list #$@invocations)))))
>> +
>> +(define* (oci-object-shepherd-service object runtime runtime-cli name requirement invocations
> Max width :-).

I should have dropped the default #f for keywords in all procedure, thanks.

Toggle quote (29 lines)
>> + "Return a file-like object that, once lowered, will evaluate to the entrypoint
>> +for the Shepherd service that will run IMAGE through RUNTIME-CLI."
>> + (program-file
>> + (string-append "oci-entrypoint-" name)
>> + #~(begin
>> + (use-modules (ice-9 format)
>> + (srfi srfi-1))
>> + (when #$verbose?
>> + (format #t "Running in verbose mode...~%")
>> + (format #t "Current user: ~a ~a~%"
>> + (getuid) (passwd:name (getpwuid (getuid))))
>> + (format #t "Current group: ~a ~a~%"
>> + (getgid) (group:name (getgrgid (getgid))))
>> + (format #t "Current directory ~a~%" (getcwd)))
> I'd use a single format call; you can format the string naturally like
>
> (format #t "\
> Running in verbose mode...
> Current user: ~a ~a
> ...
> " arg1 arg2, ...)
>
>
>> + (define invocation (list #$@invocation))
>> + #$@pre-script
>> + (when #$verbose?
>> + (format #t "Running~{ ~a~}~%" invocation))
>> + (apply execlp `(,(first invocation) ,@invocation)))))
> You can more simply: (apply execlp (first invocation) invocation)
Thank you, I have addressed this in gnu/build/oci-containers.scm
Toggle quote (67 lines)
>> +
>> +(define* (oci-container-shepherd-service runtime runtime-cli config
>> + #:key
>> + (runtime-environment #~())
>> + (runtime-extra-arguments '())
>> + (oci-requirement '())
>> + (user #f)
>> + (group #f)
>> + (verbose? #f))
>> + "Return a Shepherd service object that will run the OCI container represented
>> +by CONFIG through RUNTIME-CLI."
>> + (let* ((actions (oci-container-configuration-shepherd-actions config))
>> (auto-start?
>> (oci-container-configuration-auto-start? config))
>> - (user (oci-container-configuration-user config))
>> - (group (oci-container-configuration-group config))
>> + (maybe-user (oci-container-configuration-user config))
>> + (maybe-group (oci-container-configuration-group config))
>> + (user (if (maybe-value-set? maybe-user)
>> + maybe-user
>> + user))
>> + (group (if (maybe-value-set? maybe-group)
>> + maybe-group
>> + group))
>> (host-environment
>> (oci-container-configuration-host-environment config))
>> (command (oci-container-configuration-command config))
>> (log-file (oci-container-configuration-log-file config))
>> - (provision (oci-container-configuration-provision config))
>> (requirement (oci-container-configuration-requirement config))
>> (respawn?
>> (oci-container-configuration-respawn? config))
>> (image (oci-container-configuration-image config))
>> (image-reference (oci-image-reference image))
>> (options (oci-container-configuration->options config))
>> - (name (guess-name provision image))
>> + (name
>> + (oci-container-shepherd-name runtime config))
>> (extra-arguments
>> - (oci-container-configuration-extra-arguments config)))
>> + (oci-container-configuration-extra-arguments config))
>> + (invocation
>> + (oci-container-run-invocation
>> + runtime runtime-cli name command image-reference
>> + options runtime-extra-arguments extra-arguments))
>> + (container-action
>> + (lambda* (command #:key (environment-variables #f))
>> + #~(lambda _
>> + (fork+exec-command
>> + (list #$@command)
>> + #$@(if user (list #:user user) '())
>> + #$@(if group (list #:group group) '())
>> + #$@(if (maybe-value-set? log-file)
>> + (list #:log-file log-file)
>> + '())
>> + #$@(if (and user (eq? runtime 'podman))
>> + (list #:directory
>> + #~(passwd:dir (getpwnam #$user)))
>> + '())
>> + #$@(if environment-variables
>> + (list #:environment-variables
>> + environment-variables)
>> + '()))))))
> That's a very long let. Since many values appear to simply be accessed
> from theh oci-container-configuration, match-record could be used to
> great effect. For readability, I'd also define the action procedures
> using nested 'define' inside the let/match-record.
I tried to address this but it's really difficult to trim much, please
let me know if there's some better way.
Toggle quote (12 lines)
>> +(define (oci-service-subids config)
>> + "Return a subids-extension record representing subuids and subgids required by
>> +the rootless Podman backend."
>> + (define (delete-duplicate-ranges ranges)
>> + (delete-duplicates ranges
>> + (lambda args
>> + (apply string=? (map subid-range-name ranges)))))
> I don't understand how the above 'delete-duplicate-ranges' procedure
> works; the 2nd argument of delete-duplicates is normally a predicate
> that accepts two values, but here it's a lambda that disregards the
> arguments: it seems it'd be wasteful to compute the same result over and
> over?
It is, I just replace this with a more descriptive error that allows the
users to drop the duplicate without silently dropping a random one
between the two.
Toggle quote (16 lines)
>> + (response1
>> + (slurp bash "-c"
>> + (string-append "ls -la /sys/fs/cgroup | "
>> + "grep -E ' \\./?$' | awk '{ print $4 }'")))
>> + (response2 (slurp bash "-c"
>> + (string-append "ls -l /sys/fs/cgroup/cgroup"
>> + ".{procs,subtree_control,threads} | "
>> + "awk '{ print $4 }' | sort -u"))))
> Is there perhaps an easier way to check that the podman services were
> started successfully? Perhaps using the podman command directly to
> query the state of the running containers?
>
> 'ls' is usually frowned for scripting; it's meant for interactive use.
> Perhaps you could use the shell wildcard feature or the find command
> instead? I comment explaining what the above does/check exactly would
> be useful; it's not obvious.
I think this test can be dropped anyway since we later wait for Shepherd
services to be started, it is a development leftover.
Toggle quote (15 lines)
>> + (let loop ((attempts 0))
>> + (if (= attempts 60)
>> + (error "Services didn't come up after more than 60 seconds")
>> + (if (equal? '("cgroup" "cgroup")
>> + (run-test))
>> + #t
>> + (begin
>> + (sleep 1)
>> + (format #t "Services didn't come up yet, retrying with attempt ~a~%"
>> + (+ 1 attempts))
>> + (loop (+ 1 attempts))))))))
> Note that we have a 'with-retries' definition in (gnu build
> jami-service) which could be used here; perhaps it should be relocated
> elsewhere.

Thank you for the hint, I created gnu/build/utils.scm and tested my
changes with:

guix shell -CPWN --writable-root nss-certs -m manifest.scm -- make
check-system TESTS="pounce jami ngircd"

Toggle quote (6 lines)
>> + (lambda (port)
>> + (display (string-join response "\n") port)))))
>> + (lambda ()
>> + (primitive-exit 127))))
> The fork is used to be able to run the podman command with the right
> passwd and uid, right? Ma
This message was truncated. Download the full message here.
Attachment: file
P
Re: [bug#76081] [PATCH v10 5/7] tests: Use lower-oci-image-state in container tests.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 76081@debbugs.gnu.org)
4e650b54-c892-4bd0-af84-7601d895e560@autistici.org
Hi,

On 5/16/25 03:45, Maxim Cournoyer wrote:
Toggle quote (14 lines)
> Hi,
>
> Giacomo Leidi <goodoldpaul@autistici.org> writes:
>
>> This patch replaces boilerplate in container related tests with
>> oci-image plumbing from (gnu services containers).
>>
>> * gnu/services/containers.scm: Export lower-oci-image-state.
> I don't think that's a nice API to export; you can instead use the
>
> (define lower-oci-image-state
> (@@ (gnu services containers) lower-oci-image-state)
>
> trick to expose internals (white box testing).
Good point, addressed.
Toggle quote (16 lines)
>> * gnu/tests/containers.scm (%oci-tarball): New variable;
>> (run-rootless-podman-test): use %oci-tarball;
>> (build-tarball&run-rootless-podman-test): drop procedure.
>> * gnu/tests/docker.scm (%docker-tarball): New variable;
>> (build-tarball&run-docker-test): use %docker-tarball;
>> (%docker-system-tarball): New variable;
>> (build-tarball&run-docker-system-test): new procedure.
>>
>> Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
>> ---
>> gnu/services/containers.scm | 2 +
>> gnu/tests/containers.scm | 80 ++++++++++++++---------------
>> gnu/tests/docker.scm | 100 ++++++++++++++++++++----------------
>> 3 files changed, 95 insertions(+), 87 deletions(-)
> The diffstats suggest the result is not that impactful in terms of
> reducing boilerplate, ah!
I would like to work to a single Scheme API in the future, that can be
common to guix pack, guix system internals and the oci-service-type
internals. But yeah the boilerplate is not much less.

v11 should have all your comments addressed, thank you for your review.

cheers,
giacomo
P
Re: [bug#76081] [PATCH v10 6/7] services: oci: Migrate oci-configuration to (guix records).
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)(address . 76081@debbugs.gnu.org)
e6bcaa84-bfec-4ec3-9158-5360528b7803@autistici.org
Hi,

On 5/16/25 04:02, Maxim Cournoyer wrote:
Toggle quote (4 lines)
> Also, there are other home services using a define-configuration'd
> record with for-home, such as the home-snuik-service-type, so it should
> be possible to make it work.

You are right, thank you! I was probably mistaken. I dropped the patch.

cheers,

giacomo
Attachment: file
P
Re: [bug#76081] [PATCH v10 7/7] home: Add home-oci-service-type.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)
772c4c9b-ae5b-4073-9029-fbfa34c2ae6d@autistici.org
Hi Maxim,

thank you for your extensive help. I'm sending a v11 which hopefully
addresses everything. I tested my changes with:

guix shell -CPWN --writable-root nss-certs -m manifest.scm -- make check-system TESTS="pounce jami ngircd oci-service-rootless-podman oci-service-docker oci-container rootless-podman docker docker-system"

cheers,
giacomo
Attachment: file
G
G
Giacomo Leidi wrote on 31 May 19:51 -0700
[PATCH v11 3/5] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
289b2dbdb1aea74d65d04aa1290fd7fa2d39afaf.1748746278.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/build/oci-containers.scm: New file containg OCI runtime business
logic used in OCI backed Shepherd services.
oci-read-lines
(oci-system*,oci-object-exists?,oci-object-service-available?
oci-image-load,oci-log-verbose,oci-container-execlp,oci-object-create):
New procedures.
* gnu/local.mk: Add it.
* gnu/services/containers.scm (list-of-oci-containers?,
list-of-oci-networks?,list-of-oci-volumes?,%oci-supported-runtimes,
oci-runtime?,oci-runtime-system-environment,oci-runtime-system-extra-arguments,
oci-runtime-system-requirement,oci-runtime-cli,oci-runtime-system-cli,
oci-runtime-home-cli,oci-runtime-name,oci-runtime-group,
oci-container-shepherd-name,oci-networks-shepherd-name,
oci-networks-home-shepherd-name,oci-volumes-shepherd-name,
oci-volumes-home-shepherd-name,oci-container-configuration->options,
oci-network-configuration->options,oci-volume-configuration->options,
oci-container-shepherd-service,oci-objects-merge-lst,oci-extension-merge,
oci-service-accounts,oci-service-profile,oci-service-subids,
oci-configuration->shepherd-services,oci-configuration-extend): New
procedures.
(image-reference): Implement unambiguous naming convention, that paired
with the new implementation for listing caches images with docker ls or
podman ls, allows for more efficient image caching.
(oci-container-configuration)[user,group]: Change default-type to
maybe-string, since by default containers will run under the user and
group declared in oci-configuration records. When unset the
oci-service-type will derive their value from the OCI runtime state.
[runtime,host-environment,environment,shepherd-actions,ports,extra-arguments]:
define a predicate and use it as a type in the configuration. This
way errors are reported with source location information.
(lower-manifest): Defer to caller the logic of setting up an image tag.
(lower-oci-image): Rename to load-oci-image-state.
(oci-runtime-state): Intermediate representation of the OCI runtime
details. It is supposed to be an internal API.
(oci-state): Intermediate representation of the OCI provisioning state,
such as containers and networks. It is supposed to be an internal API.
(oci-container-invocation): Intermediate representation of the OCI
runtime run command to start a container. It is supposed to be an
internal API.
(%oci-image-loader): Rename to oci-image-loader and use
oci-runtime-state and (gnu build oci-containers).
(oci-container-shepherd-service): Use oci-state and oci-runtime-state,
add command-line action.
(oci-network-configuration,oci-volume-configuration,oci-configuration,
oci-extension): New record types.
(oci-service-type): New service-type.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
doc/guix.texi | 306 ++++++--
gnu/build/oci-containers.scm | 210 ++++++
gnu/local.mk | 1 +
gnu/services/containers.scm | 1365 +++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 38 +-
gnu/tests/containers.scm | 561 +++++++++++++-
6 files changed, 2208 insertions(+), 273 deletions(-)
create mode 100644 gnu/build/oci-containers.scm

Toggle diff (387 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 5fefba36342..ae49d0c547b 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -43937,59 +43937,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@samp{docker run} or @samp{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources, it can be either a package or a string representing
+an absolute file name to the runtime binary entrypoint. When unset it will default
+to @code{docker-cli} package for the @code{'docker} runtime or to @code{podman}
+package for the @code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -44009,16 +44112,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @samp{docker run}
+or @samp{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -44026,22 +44129,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -44069,7 +44174,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -44080,10 +44185,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -44094,25 +44200,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker run} or @samp{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker network create} or @samp{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker volume create} or @
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 31 May 19:51 -0700
[PATCH v11 4/5] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
32e4957092890faccffd06b9161c50583179523d.1748746278.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
gnu/tests/containers.scm | 84 +++++++++++++++----------------
gnu/tests/docker.scm | 104 ++++++++++++++++++++++-----------------
2 files changed, 101 insertions(+), 87 deletions(-)

Toggle diff (283 lines)
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 051a4c740bf..0db22e3a07f 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -46,6 +46,9 @@ (define-module (gnu tests containers)
%test-oci-service-rootless-podman
%test-oci-service-docker))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %rootless-podman-os
(simple-operating-system
@@ -69,13 +72,48 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +292,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +345,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-network
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..1f264d52486 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -48,6 +49,9 @@ (define-module (gnu tests docker)
%test-docker-system
%test-oci-container))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %docker-os
(simple-operating-system
(service dhcp-client-service-type)
@@ -57,6 +61,41 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +212,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +221,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +353,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.49.0
G
G
Giacomo Leidi wrote on 31 May 19:51 -0700
[PATCH v11 1/5] tests: oci-container: Set explicit timeouts.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
8ac2edf51988af974ed0b4cd4ac63da23ce17118.1748746278.git.goodoldpaul@autistici.org
* gnu/tests/docker.scm: Simplify %test-oci-container test case and add
explicit timeouts to tests outcomes.

Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
gnu/tests/docker.scm | 99 ++++++++++++++++++--------------------------
1 file changed, 41 insertions(+), 58 deletions(-)

Toggle diff (129 lines)
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 90c8d0f8508..5dcf05a17e3 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -1,7 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 Danny Milosavljevic <dannym@scratchpost.org>
;;; Copyright © 2019-2023 Ludovic Courtès <ludo@gnu.org>
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -414,71 +414,54 @@ (define (run-oci-container-test)
(test-runner-current (system-test-runner #$output))
(test-begin "oci-container")
- (test-assert "containerd service running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'containerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (test-assert "containerd PID file present"
- (wait-for-file "/run/containerd/containerd.pid" marionette))
-
- (test-assert "dockerd running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'dockerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (sleep 10) ; let service start
+ (wait-for-file "/run/containerd/containerd.pid" marionette)
(test-assert "docker-guile running"
(marionette-eval
'(begin
(use-modules (gnu services herd))
- (match (start-service 'docker-guile)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
+ (wait-for-service 'docker-guile #:timeout 120)
+ #t)
marionette))
- (test-equal "passing host environment variables and volumes"
- '("value" "hello")
- (marionette-eval
- `(begin
- (use-modules (ice-9 popen)
- (ice-9 rdelim))
-
- (define slurp
- (lambda args
- (let* ((port (apply open-pipe* OPEN_READ args))
- (output (let ((line (read-line port)))
- (if (eof-object? line)
- ""
- line)))
- (status (close-pipe port)))
- output)))
- (let* ((response1 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
- (response2 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
+ (test-assert "passing host environment variables and volumes"
+ (begin
+ (define (run-test)
+ (marionette-eval
+ `(begin
+ (use-modules (ice-9 popen)
+ (ice-9 rdelim))
+
+ (define slurp
+ (lambda args
+ (let* ((port (apply open-pipe* OPEN_READ args))
+ (output (let ((line (read-line port)))
+ (if (eof-object? line)
+ ""
+ line)))
+ (status (close-pipe port)))
+ output)))
+ (let* ((response1 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
+ (response2 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
(display (call-with-input-file \"/shared.txt\" read-line)))")))
- (list response1 response2)))
- marionette))
+ (list response1 response2)))
+ marionette))
+ ;; Allow services to come up on slower machines
+ (let loop ((attempts 0))
+ (if (= attempts 60)
+ (error "Service didn't come up after more than 60 seconds")
+ (if (equal? '("value" "hello")
+ (run-test))
+ #t
+ (begin
+ (sleep 1)
+ (loop (+ 1 attempts))))))))
(test-end))))

base-commit: e29c57ab81517424b03579147910553d92246212
--
2.49.0
G
G
Giacomo Leidi wrote on 31 May 19:51 -0700
[PATCH v11 2/5] gnu: Move with-retries outside dbus-service.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
21078384fabb96961c84bebee6b05a0ce85ee9b4.1748746278.git.goodoldpaul@autistici.org
This patch moves with-retries outside of (gnu build dbus-service) into a
more general (gnu build utils) which can be imported without
unnecessarily importing dbus related symbols.

* gnu/build/dbus-service.scm (sleep,with-retries): Move to...
* gnu/build/utils.scm: ...here.
* gnu/local.mk: Add gnu/build/utils.scm.
* gnu/build/jami-service.scm: Import (gnu build utils).
* gnu/services/telephony.scm (jami-account->alist): Format.
(jami-shepherd-services): Import (gnu build utils).
* gnu/test/messaging.scm (run-ngircd-test): Import (gnu build utils).
(run-pounce-test): Import (gnu build utils).
* gnu/test/telephony.scm (run-jami-test): Import (gnu build utils) and
format.

Change-Id: I3c1768f884ca46d0820a801bd0310c2ec8f3da54
---
gnu/build/dbus-service.scm | 39 +++------------------------
gnu/build/jami-service.scm | 1 +
gnu/build/utils.scm | 55 ++++++++++++++++++++++++++++++++++++++
gnu/local.mk | 1 +
gnu/services/telephony.scm | 9 ++++---
gnu/tests/messaging.scm | 8 +++---
gnu/tests/telephony.scm | 11 ++++----
7 files changed, 76 insertions(+), 48 deletions(-)
create mode 100644 gnu/build/utils.scm

Toggle diff (279 lines)
diff --git a/gnu/build/dbus-service.scm b/gnu/build/dbus-service.scm
index 688afe44c3d..9bbcd457512 100644
--- a/gnu/build/dbus-service.scm
+++ b/gnu/build/dbus-service.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021, 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,6 +25,7 @@
;;; Code:
(define-module (gnu build dbus-service)
+ #:use-module (gnu build utils)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-19)
@@ -54,45 +56,10 @@ (define-module (gnu build dbus-service)
call-dbus-method
dbus-available-services
- dbus-service-available?
-
- with-retries))
+ dbus-service-available?))
(define %dbus-query-timeout 2) ;in seconds
-;;; Use Fibers' sleep to enable cooperative scheduling in Shepherd >= 0.9.0,
-;;; which is required at least for the Jami service.
-(define sleep*
- (lambda () ;delay execution
- (if (resolve-module '(fibers) #f #:ensure #f)
- (module-ref (resolve-interface '(fibers)) 'sleep)
- (begin
- (format #t "Fibers not available -- blocking 'sleep' in use~%")
- sleep))))
-
-;;;
-;;; Utilities.
-;;;
-
-(define-syntax-rule (with-retries n delay body ...)
- "Retry the code in BODY up to N times until it doesn't raise an exception nor
-return #f, else raise an error. A delay of DELAY seconds is inserted before
-each retry."
- (let loop ((attempts 0))
- (catch #t
- (lambda ()
- (let ((result (begin body ...)))
- (if (not result)
- (error "failed attempt" attempts)
- result)))
- (lambda args
- (if (< attempts n)
- (begin
- ((sleep*) delay) ;else wait and retry
- (loop (+ 1 attempts)))
- (error "maximum number of retry attempts reached"
- (quote body ...) args))))))
-
;;;
;;; Low level wrappers above AC/D-Bus.
diff --git a/gnu/build/jami-service.scm b/gnu/build/jami-service.scm
index a00785f699b..7c2c48d821a 100644
--- a/gnu/build/jami-service.scm
+++ b/gnu/build/jami-service.scm
@@ -25,6 +25,7 @@
(define-module (gnu build jami-service)
#:use-module (gnu build dbus-service)
+ #:use-module (gnu build utils)
#:use-module (ice-9 format)
#:use-module (ice-9 match)
#:use-module (ice-9 rdelim)
diff --git a/gnu/build/utils.scm b/gnu/build/utils.scm
new file mode 100644
index 00000000000..1aa72358bd8
--- /dev/null
+++ b/gnu/build/utils.scm
@@ -0,0 +1,55 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;;
+;;; This module contains helpers that could useful to any service.
+;;;
+;;; Code:
+
+(define-module (gnu build utils)
+ #:export (with-retries))
+
+;;; Use Fibers' sleep to enable cooperative scheduling in Shepherd >= 0.9.0,
+;;; which is required at least for the Jami service.
+(define sleep*
+ (lambda () ;delay execution
+ (if (resolve-module '(fibers) #f #:ensure #f)
+ (module-ref (resolve-interface '(fibers)) 'sleep)
+ (begin
+ (format #t "Fibers not available -- blocking 'sleep' in use~%")
+ sleep))))
+
+(define-syntax-rule (with-retries n delay body ...)
+ "Retry the code in BODY up to N times until it doesn't raise an exception nor
+return #f, else raise an error. A delay of DELAY seconds is inserted before
+each retry."
+ (let loop ((attempts 0))
+ (catch #t
+ (lambda ()
+ (let ((result (begin body ...)))
+ (if (not result)
+ (error "failed attempt" attempts)
+ result)))
+ (lambda args
+ (if (< attempts n)
+ (begin
+ ((sleep*) delay) ;else wait and retry
+ (loop (+ 1 attempts)))
+ (error "maximum number of retry attempts reached"
+ (quote body ...) args))))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 2948bfb1bff..4500bfc6d33 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -841,6 +841,7 @@ GNU_SYSTEM_MODULES = \
%D%/build/linux-modules.scm \
%D%/build/marionette.scm \
%D%/build/secret-service.scm \
+ %D%/build/utils.scm \
\
%D%/tests.scm \
%D%/tests/audio.scm \
diff --git a/gnu/services/telephony.scm b/gnu/services/telephony.scm
index 9926f4107de..ad6959e161b 100644
--- a/gnu/services/telephony.scm
+++ b/gnu/services/telephony.scm
@@ -210,7 +210,7 @@ (define (jami-account->alist jami-account-object)
(tfilter-maybe-value jami-account-object)
(tmap (lambda (field)
(let* ((name (field-name->account-detail
- (configuration-field-name field)))
+ (configuration-field-name field)))
(value ((configuration-field-serializer field)
name ((configuration-field-getter field)
jami-account-object))))
@@ -360,7 +360,8 @@ (define (jami-shepherd-services config)
;; variant of the 'sleep' procedure.
guile-fibers)
(with-imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
+ (gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)))
@@ -541,7 +542,8 @@ (define (jami-shepherd-services config)
(list (shepherd-service
(documentation "Run a D-Bus session for the Jami daemon.")
(provision '(jami-dbus-session))
- (modules `((gnu build dbus-service)
+ (modules `((gnu build utils)
+ (gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)
,@%default-modules))
@@ -587,6 +589,7 @@ (define (jami-shepherd-services config)
(ice-9 receive)
(srfi srfi-1)
(srfi srfi-26)
+ (gnu build utils)
(gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)
diff --git a/gnu/tests/messaging.scm b/gnu/tests/messaging.scm
index 8df67433a7f..2eb99331b52 100644
--- a/gnu/tests/messaging.scm
+++ b/gnu/tests/messaging.scm
@@ -269,7 +269,7 @@ (define (run-ngircd-test)
(marionette-operating-system
%ngircd-os
#:imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
(guix build utils)
(gnu services herd)))))))
@@ -298,7 +298,7 @@ (define (run-ngircd-test)
(test-assert "basic irc operations function as expected"
(marionette-eval
'(begin
- (use-modules ((gnu build dbus-service) #:select (with-retries))
+ (use-modules (gnu build utils)
(ice-9 textual-ports))
(define (write-command command)
@@ -437,7 +437,7 @@ (define (run-pounce-test)
(marionette-operating-system
%pounce-os
#:imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
(guix build utils)
(gnu services herd)))))
(memory-size 1024)))
@@ -470,7 +470,7 @@ (define (run-pounce-test)
(test-assert "pounce functions as an irc bouncer"
(marionette-eval
'(begin
- (use-modules ((gnu build dbus-service) #:select (with-retries))
+ (use-modules (gnu build utils)
(guix build utils)
(ice-9 textual-ports))
diff --git a/gnu/tests/telephony.scm b/gnu/tests/telephony.scm
index f03ea963f7e..3a085762323 100644
--- a/gnu/tests/telephony.scm
+++ b/gnu/tests/telephony.scm
@@ -143,7 +143,8 @@ (define* (run-jami-test #:key provisioning? partial?)
#:imported-modules '((gnu services herd)
(guix combinators)
(gnu build jami-service)
- (gnu build dbus-service))))
+ (gnu build dbus-service)
+ (gnu build utils))))
(define vm (virtual-machine
(operating-system os)
(memory-size 512)))
@@ -209,7 +210,7 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "service can be stopped"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu build jami-service)
(gnu services herd)
(rnrs base))
@@ -223,10 +224,10 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "service can be restarted"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu build jami-service)
(gnu services herd)
- (rnrs base) )
+ (rnrs base))
;; Start the service.
(start-service 'jami)
(with-retries 40 1 (jami-service-available?))
@@ -239,7 +240,7 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "jami accounts provisioning, account present"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu services herd)
(rnrs base))
;; Accounts take some time to appear after being added.
--
2.49.0
G
G
Giacomo Leidi wrote on 31 May 19:51 -0700
[PATCH v11 5/5] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
a9adb140b8f41ac2b1dab8a04d07e4568f762568.1748746278.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 49 +++++++++++++
gnu/local.mk | 1 +
3 files changed, 164 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (194 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index ae49d0c547b..2821712e8b4 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -52192,6 +52192,120 @@ Miscellaneous Home Services
documentation of the system service (@pxref{Miscellaneous Services,
@code{readymedia-service-type}}).
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 00000000000..1ccdb3b2464
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,49 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type
+ (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli))))
+ (service-extension home-shepherd-service-type
+ oci-configuration->shepherd-services)))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 09ccd64b40a..5dfc20da9ca 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -104,6 +104,7 @@ GNU_SYSTEM_MODULES = \
%D%/home/services.scm \
%D%/home/services/admin.scm \
%D%/home/services/backup.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.49.0
P
Re: [bug#76081] [PATCH v10 7/7] home: Add home-oci-service-type.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)
0ac0cdfc-7997-4b97-ab13-906c41eacc09@autistici.org
Hi I'm sending a v12 dropping the annoying verbose log printing whether
the evaluation was happening in Guix Home context or not. It brings no
additional informations.

The new revision is also rebased on top of
b7fc28345c8eae2fe68d0c2c740a809926c69b87 .

thank you for your work,

cheers

giacomo
G
G
Giacomo Leidi wrote on 13 Jun 11:10 -0700
[PATCH v12 3/5] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
eba4216bfdcdc6d9c98d521167eb82352c9a3bec.1749838208.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/build/oci-containers.scm: New file containg OCI runtime business
logic used in OCI backed Shepherd services.
oci-read-lines
(oci-system*,oci-object-exists?,oci-object-service-available?
oci-image-load,oci-log-verbose,oci-container-execlp,oci-object-create):
New procedures.
* gnu/local.mk: Add it.
* gnu/services/containers.scm (list-of-oci-containers?,
list-of-oci-networks?,list-of-oci-volumes?,%oci-supported-runtimes,
oci-runtime?,oci-runtime-system-environment,oci-runtime-system-extra-arguments,
oci-runtime-system-requirement,oci-runtime-cli,oci-runtime-system-cli,
oci-runtime-home-cli,oci-runtime-name,oci-runtime-group,
oci-container-shepherd-name,oci-networks-shepherd-name,
oci-networks-home-shepherd-name,oci-volumes-shepherd-name,
oci-volumes-home-shepherd-name,oci-container-configuration->options,
oci-network-configuration->options,oci-volume-configuration->options,
oci-container-shepherd-service,oci-objects-merge-lst,oci-extension-merge,
oci-service-accounts,oci-service-profile,oci-service-subids,
oci-configuration->shepherd-services,oci-configuration-extend): New
procedures.
(image-reference): Implement unambiguous naming convention, that paired
with the new implementation for listing caches images with docker ls or
podman ls, allows for more efficient image caching.
(oci-container-configuration)[user,group]: Change default-type to
maybe-string, since by default containers will run under the user and
group declared in oci-configuration records. When unset the
oci-service-type will derive their value from the OCI runtime state.
[runtime,host-environment,environment,shepherd-actions,ports,extra-arguments]:
define a predicate and use it as a type in the configuration. This
way errors are reported with source location information.
(lower-manifest): Defer to caller the logic of setting up an image tag.
(lower-oci-image): Rename to load-oci-image-state.
(oci-runtime-state): Intermediate representation of the OCI runtime
details. It is supposed to be an internal API.
(oci-state): Intermediate representation of the OCI provisioning state,
such as containers and networks. It is supposed to be an internal API.
(oci-container-invocation): Intermediate representation of the OCI
runtime run command to start a container. It is supposed to be an
internal API.
(%oci-image-loader): Rename to oci-image-loader and use
oci-runtime-state and (gnu build oci-containers).
(oci-container-shepherd-service): Use oci-state and oci-runtime-state,
add command-line action.
(oci-network-configuration,oci-volume-configuration,oci-configuration,
oci-extension): New record types.
(oci-service-type): New service-type.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
doc/guix.texi | 306 ++++++--
gnu/build/oci-containers.scm | 210 ++++++
gnu/local.mk | 1 +
gnu/services/containers.scm | 1363 +++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 38 +-
gnu/tests/containers.scm | 561 +++++++++++++-
6 files changed, 2206 insertions(+), 273 deletions(-)
create mode 100644 gnu/build/oci-containers.scm

Toggle diff (387 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index eb55d337f57..fbffead4f9f 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -44108,59 +44108,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@samp{docker run} or @samp{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources, it can be either a package or a string representing
+an absolute file name to the runtime binary entrypoint. When unset it will default
+to @code{docker-cli} package for the @code{'docker} runtime or to @code{podman}
+package for the @code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -44180,16 +44283,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @samp{docker run}
+or @samp{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -44197,22 +44300,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -44240,7 +44345,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -44251,10 +44356,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -44265,25 +44371,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker run} or @samp{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker network create} or @samp{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker volume create} or @
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 13 Jun 11:10 -0700
[PATCH v12 2/5] gnu: Move with-retries outside dbus-service.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
051132a771d9e1277891eeb3dcac57440fd059dc.1749838208.git.goodoldpaul@autistici.org
This patch moves with-retries outside of (gnu build dbus-service) into a
more general (gnu build utils) which can be imported without
unnecessarily importing dbus related symbols.

* gnu/build/dbus-service.scm (sleep,with-retries): Move to...
* gnu/build/utils.scm: ...here.
* gnu/local.mk: Add gnu/build/utils.scm.
* gnu/build/jami-service.scm: Import (gnu build utils).
* gnu/services/telephony.scm (jami-account->alist): Format.
(jami-shepherd-services): Import (gnu build utils).
* gnu/test/messaging.scm (run-ngircd-test): Import (gnu build utils).
(run-pounce-test): Import (gnu build utils).
* gnu/test/telephony.scm (run-jami-test): Import (gnu build utils) and
format.

Change-Id: I3c1768f884ca46d0820a801bd0310c2ec8f3da54
---
gnu/build/dbus-service.scm | 39 +++------------------------
gnu/build/jami-service.scm | 1 +
gnu/build/utils.scm | 55 ++++++++++++++++++++++++++++++++++++++
gnu/local.mk | 1 +
gnu/services/telephony.scm | 9 ++++---
gnu/tests/messaging.scm | 8 +++---
gnu/tests/telephony.scm | 11 ++++----
7 files changed, 76 insertions(+), 48 deletions(-)
create mode 100644 gnu/build/utils.scm

Toggle diff (279 lines)
diff --git a/gnu/build/dbus-service.scm b/gnu/build/dbus-service.scm
index 688afe44c3d..9bbcd457512 100644
--- a/gnu/build/dbus-service.scm
+++ b/gnu/build/dbus-service.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021, 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,6 +25,7 @@
;;; Code:
(define-module (gnu build dbus-service)
+ #:use-module (gnu build utils)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-19)
@@ -54,45 +56,10 @@ (define-module (gnu build dbus-service)
call-dbus-method
dbus-available-services
- dbus-service-available?
-
- with-retries))
+ dbus-service-available?))
(define %dbus-query-timeout 2) ;in seconds
-;;; Use Fibers' sleep to enable cooperative scheduling in Shepherd >= 0.9.0,
-;;; which is required at least for the Jami service.
-(define sleep*
- (lambda () ;delay execution
- (if (resolve-module '(fibers) #f #:ensure #f)
- (module-ref (resolve-interface '(fibers)) 'sleep)
- (begin
- (format #t "Fibers not available -- blocking 'sleep' in use~%")
- sleep))))
-
-;;;
-;;; Utilities.
-;;;
-
-(define-syntax-rule (with-retries n delay body ...)
- "Retry the code in BODY up to N times until it doesn't raise an exception nor
-return #f, else raise an error. A delay of DELAY seconds is inserted before
-each retry."
- (let loop ((attempts 0))
- (catch #t
- (lambda ()
- (let ((result (begin body ...)))
- (if (not result)
- (error "failed attempt" attempts)
- result)))
- (lambda args
- (if (< attempts n)
- (begin
- ((sleep*) delay) ;else wait and retry
- (loop (+ 1 attempts)))
- (error "maximum number of retry attempts reached"
- (quote body ...) args))))))
-
;;;
;;; Low level wrappers above AC/D-Bus.
diff --git a/gnu/build/jami-service.scm b/gnu/build/jami-service.scm
index a00785f699b..7c2c48d821a 100644
--- a/gnu/build/jami-service.scm
+++ b/gnu/build/jami-service.scm
@@ -25,6 +25,7 @@
(define-module (gnu build jami-service)
#:use-module (gnu build dbus-service)
+ #:use-module (gnu build utils)
#:use-module (ice-9 format)
#:use-module (ice-9 match)
#:use-module (ice-9 rdelim)
diff --git a/gnu/build/utils.scm b/gnu/build/utils.scm
new file mode 100644
index 00000000000..1aa72358bd8
--- /dev/null
+++ b/gnu/build/utils.scm
@@ -0,0 +1,55 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;;
+;;; This module contains helpers that could useful to any service.
+;;;
+;;; Code:
+
+(define-module (gnu build utils)
+ #:export (with-retries))
+
+;;; Use Fibers' sleep to enable cooperative scheduling in Shepherd >= 0.9.0,
+;;; which is required at least for the Jami service.
+(define sleep*
+ (lambda () ;delay execution
+ (if (resolve-module '(fibers) #f #:ensure #f)
+ (module-ref (resolve-interface '(fibers)) 'sleep)
+ (begin
+ (format #t "Fibers not available -- blocking 'sleep' in use~%")
+ sleep))))
+
+(define-syntax-rule (with-retries n delay body ...)
+ "Retry the code in BODY up to N times until it doesn't raise an exception nor
+return #f, else raise an error. A delay of DELAY seconds is inserted before
+each retry."
+ (let loop ((attempts 0))
+ (catch #t
+ (lambda ()
+ (let ((result (begin body ...)))
+ (if (not result)
+ (error "failed attempt" attempts)
+ result)))
+ (lambda args
+ (if (< attempts n)
+ (begin
+ ((sleep*) delay) ;else wait and retry
+ (loop (+ 1 attempts)))
+ (error "maximum number of retry attempts reached"
+ (quote body ...) args))))))
diff --git a/gnu/local.mk b/gnu/local.mk
index ee861f51ee2..da902d07df2 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -842,6 +842,7 @@ GNU_SYSTEM_MODULES = \
%D%/build/linux-modules.scm \
%D%/build/marionette.scm \
%D%/build/secret-service.scm \
+ %D%/build/utils.scm \
\
%D%/tests.scm \
%D%/tests/audio.scm \
diff --git a/gnu/services/telephony.scm b/gnu/services/telephony.scm
index 9926f4107de..ad6959e161b 100644
--- a/gnu/services/telephony.scm
+++ b/gnu/services/telephony.scm
@@ -210,7 +210,7 @@ (define (jami-account->alist jami-account-object)
(tfilter-maybe-value jami-account-object)
(tmap (lambda (field)
(let* ((name (field-name->account-detail
- (configuration-field-name field)))
+ (configuration-field-name field)))
(value ((configuration-field-serializer field)
name ((configuration-field-getter field)
jami-account-object))))
@@ -360,7 +360,8 @@ (define (jami-shepherd-services config)
;; variant of the 'sleep' procedure.
guile-fibers)
(with-imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
+ (gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)))
@@ -541,7 +542,8 @@ (define (jami-shepherd-services config)
(list (shepherd-service
(documentation "Run a D-Bus session for the Jami daemon.")
(provision '(jami-dbus-session))
- (modules `((gnu build dbus-service)
+ (modules `((gnu build utils)
+ (gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)
,@%default-modules))
@@ -587,6 +589,7 @@ (define (jami-shepherd-services config)
(ice-9 receive)
(srfi srfi-1)
(srfi srfi-26)
+ (gnu build utils)
(gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)
diff --git a/gnu/tests/messaging.scm b/gnu/tests/messaging.scm
index 8df67433a7f..2eb99331b52 100644
--- a/gnu/tests/messaging.scm
+++ b/gnu/tests/messaging.scm
@@ -269,7 +269,7 @@ (define (run-ngircd-test)
(marionette-operating-system
%ngircd-os
#:imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
(guix build utils)
(gnu services herd)))))))
@@ -298,7 +298,7 @@ (define (run-ngircd-test)
(test-assert "basic irc operations function as expected"
(marionette-eval
'(begin
- (use-modules ((gnu build dbus-service) #:select (with-retries))
+ (use-modules (gnu build utils)
(ice-9 textual-ports))
(define (write-command command)
@@ -437,7 +437,7 @@ (define (run-pounce-test)
(marionette-operating-system
%pounce-os
#:imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
(guix build utils)
(gnu services herd)))))
(memory-size 1024)))
@@ -470,7 +470,7 @@ (define (run-pounce-test)
(test-assert "pounce functions as an irc bouncer"
(marionette-eval
'(begin
- (use-modules ((gnu build dbus-service) #:select (with-retries))
+ (use-modules (gnu build utils)
(guix build utils)
(ice-9 textual-ports))
diff --git a/gnu/tests/telephony.scm b/gnu/tests/telephony.scm
index f03ea963f7e..3a085762323 100644
--- a/gnu/tests/telephony.scm
+++ b/gnu/tests/telephony.scm
@@ -143,7 +143,8 @@ (define* (run-jami-test #:key provisioning? partial?)
#:imported-modules '((gnu services herd)
(guix combinators)
(gnu build jami-service)
- (gnu build dbus-service))))
+ (gnu build dbus-service)
+ (gnu build utils))))
(define vm (virtual-machine
(operating-system os)
(memory-size 512)))
@@ -209,7 +210,7 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "service can be stopped"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu build jami-service)
(gnu services herd)
(rnrs base))
@@ -223,10 +224,10 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "service can be restarted"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu build jami-service)
(gnu services herd)
- (rnrs base) )
+ (rnrs base))
;; Start the service.
(start-service 'jami)
(with-retries 40 1 (jami-service-available?))
@@ -239,7 +240,7 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "jami accounts provisioning, account present"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu services herd)
(rnrs base))
;; Accounts take some time to appear after being added.
--
2.49.0
G
G
Giacomo Leidi wrote on 13 Jun 11:10 -0700
[PATCH v12 4/5] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
bff90864d95e7b5b570d8a5b0bf4fd5a9389d31f.1749838208.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
gnu/tests/containers.scm | 84 +++++++++++++++----------------
gnu/tests/docker.scm | 104 ++++++++++++++++++++++-----------------
2 files changed, 101 insertions(+), 87 deletions(-)

Toggle diff (283 lines)
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 051a4c740bf..0db22e3a07f 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -46,6 +46,9 @@ (define-module (gnu tests containers)
%test-oci-service-rootless-podman
%test-oci-service-docker))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %rootless-podman-os
(simple-operating-system
@@ -69,13 +72,48 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +292,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +345,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-network
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..1f264d52486 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -48,6 +49,9 @@ (define-module (gnu tests docker)
%test-docker-system
%test-oci-container))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %docker-os
(simple-operating-system
(service dhcp-client-service-type)
@@ -57,6 +61,41 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +212,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +221,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +353,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.49.0
G
G
Giacomo Leidi wrote on 13 Jun 11:10 -0700
[PATCH v12 5/5] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
202119dce53c6206cabb86890e4aad6de9fcfd71.1749838208.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 49 +++++++++++++
gnu/local.mk | 1 +
3 files changed, 164 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (194 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index fbffead4f9f..77c896cf0c7 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -52363,6 +52363,120 @@ Miscellaneous Home Services
documentation of the system service (@pxref{Miscellaneous Services,
@code{readymedia-service-type}}).
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 00000000000..1ccdb3b2464
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,49 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type
+ (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli))))
+ (service-extension home-shepherd-service-type
+ oci-configuration->shepherd-services)))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index ec557633708..a5676745f58 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -104,6 +104,7 @@ GNU_SYSTEM_MODULES = \
%D%/home/services.scm \
%D%/home/services/admin.scm \
%D%/home/services/backup.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.49.0
G
G
Giacomo Leidi wrote on 13 Jun 11:10 -0700
[PATCH v12 1/5] tests: oci-container: Set explicit timeouts.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
a8a082e87366e51b6ccba642d9ffa9a217dc6823.1749838208.git.goodoldpaul@autistici.org
* gnu/tests/docker.scm: Simplify %test-oci-container test case and add
explicit timeouts to tests outcomes.

Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
gnu/tests/docker.scm | 99 ++++++++++++++++++--------------------------
1 file changed, 41 insertions(+), 58 deletions(-)

Toggle diff (129 lines)
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 90c8d0f8508..5dcf05a17e3 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -1,7 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 Danny Milosavljevic <dannym@scratchpost.org>
;;; Copyright © 2019-2023 Ludovic Courtès <ludo@gnu.org>
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -414,71 +414,54 @@ (define (run-oci-container-test)
(test-runner-current (system-test-runner #$output))
(test-begin "oci-container")
- (test-assert "containerd service running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'containerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (test-assert "containerd PID file present"
- (wait-for-file "/run/containerd/containerd.pid" marionette))
-
- (test-assert "dockerd running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'dockerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (sleep 10) ; let service start
+ (wait-for-file "/run/containerd/containerd.pid" marionette)
(test-assert "docker-guile running"
(marionette-eval
'(begin
(use-modules (gnu services herd))
- (match (start-service 'docker-guile)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
+ (wait-for-service 'docker-guile #:timeout 120)
+ #t)
marionette))
- (test-equal "passing host environment variables and volumes"
- '("value" "hello")
- (marionette-eval
- `(begin
- (use-modules (ice-9 popen)
- (ice-9 rdelim))
-
- (define slurp
- (lambda args
- (let* ((port (apply open-pipe* OPEN_READ args))
- (output (let ((line (read-line port)))
- (if (eof-object? line)
- ""
- line)))
- (status (close-pipe port)))
- output)))
- (let* ((response1 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
- (response2 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
+ (test-assert "passing host environment variables and volumes"
+ (begin
+ (define (run-test)
+ (marionette-eval
+ `(begin
+ (use-modules (ice-9 popen)
+ (ice-9 rdelim))
+
+ (define slurp
+ (lambda args
+ (let* ((port (apply open-pipe* OPEN_READ args))
+ (output (let ((line (read-line port)))
+ (if (eof-object? line)
+ ""
+ line)))
+ (status (close-pipe port)))
+ output)))
+ (let* ((response1 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
+ (response2 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
(display (call-with-input-file \"/shared.txt\" read-line)))")))
- (list response1 response2)))
- marionette))
+ (list response1 response2)))
+ marionette))
+ ;; Allow services to come up on slower machines
+ (let loop ((attempts 0))
+ (if (= attempts 60)
+ (error "Service didn't come up after more than 60 seconds")
+ (if (equal? '("value" "hello")
+ (run-test))
+ #t
+ (begin
+ (sleep 1)
+ (loop (+ 1 attempts))))))))
(test-end))))

base-commit: b7fc28345c8eae2fe68d0c2c740a809926c69b87
--
2.49.0
P
Re: [bug#76081] [PATCH v10 7/7] home: Add home-oci-service-type.
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)
61cd246e-f2a1-4301-af23-8716800497d0@autistici.org
Hi all, I'm sending a minor improvement (revision 13). I separated the
command-line action of OCI backed Shepherd services into two different
command-line and entrypoint actions. The change I did compared to v12 is
similar to [0].

Apologies for the back and forth, I really hope you can share your
feedback . Thank you for your help and your time,

cheers,

giacomo




[0]:
P
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)
f5fbe62f-106c-420d-9c27-2d4d1f437d48@autistici.org
On 6/16/25 00:37, paul wrote:
Toggle quote (5 lines)
> Hi all, I'm sending a minor improvement (revision 13). I separated the
> command-line action of OCI backed Shepherd services into two different
> command-line and entrypoint actions. The change I did compared to v12
> is similar to [0].
>
These changes were tested with:

 guix shell -CPWN --writable-root nss-certs -m manifest.scm -- make
check-system TESTS="pounce jami ngircd oci-service-rootless-podman
oci-service-docker oci-container rootless-podman docker docker-system"
G
G
Giacomo Leidi wrote on 15 Jun 15:38 -0700
[PATCH v13 1/5] tests: oci-container: Set explicit timeouts.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
77c139050b15307c9bd4d337bf744743a1c29fdb.1750027137.git.goodoldpaul@autistici.org
* gnu/tests/docker.scm: Simplify %test-oci-container test case and add
explicit timeouts to tests outcomes.

Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
gnu/tests/docker.scm | 99 ++++++++++++++++++--------------------------
1 file changed, 41 insertions(+), 58 deletions(-)

Toggle diff (129 lines)
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 90c8d0f8508..5dcf05a17e3 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -1,7 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 Danny Milosavljevic <dannym@scratchpost.org>
;;; Copyright © 2019-2023 Ludovic Courtès <ludo@gnu.org>
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -414,71 +414,54 @@ (define (run-oci-container-test)
(test-runner-current (system-test-runner #$output))
(test-begin "oci-container")
- (test-assert "containerd service running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'containerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (test-assert "containerd PID file present"
- (wait-for-file "/run/containerd/containerd.pid" marionette))
-
- (test-assert "dockerd running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'dockerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (sleep 10) ; let service start
+ (wait-for-file "/run/containerd/containerd.pid" marionette)
(test-assert "docker-guile running"
(marionette-eval
'(begin
(use-modules (gnu services herd))
- (match (start-service 'docker-guile)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
+ (wait-for-service 'docker-guile #:timeout 120)
+ #t)
marionette))
- (test-equal "passing host environment variables and volumes"
- '("value" "hello")
- (marionette-eval
- `(begin
- (use-modules (ice-9 popen)
- (ice-9 rdelim))
-
- (define slurp
- (lambda args
- (let* ((port (apply open-pipe* OPEN_READ args))
- (output (let ((line (read-line port)))
- (if (eof-object? line)
- ""
- line)))
- (status (close-pipe port)))
- output)))
- (let* ((response1 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
- (response2 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
+ (test-assert "passing host environment variables and volumes"
+ (begin
+ (define (run-test)
+ (marionette-eval
+ `(begin
+ (use-modules (ice-9 popen)
+ (ice-9 rdelim))
+
+ (define slurp
+ (lambda args
+ (let* ((port (apply open-pipe* OPEN_READ args))
+ (output (let ((line (read-line port)))
+ (if (eof-object? line)
+ ""
+ line)))
+ (status (close-pipe port)))
+ output)))
+ (let* ((response1 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
+ (response2 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
(display (call-with-input-file \"/shared.txt\" read-line)))")))
- (list response1 response2)))
- marionette))
+ (list response1 response2)))
+ marionette))
+ ;; Allow services to come up on slower machines
+ (let loop ((attempts 0))
+ (if (= attempts 60)
+ (error "Service didn't come up after more than 60 seconds")
+ (if (equal? '("value" "hello")
+ (run-test))
+ #t
+ (begin
+ (sleep 1)
+ (loop (+ 1 attempts))))))))
(test-end))))

base-commit: 035bacebc21c263541b160bcdc27dc5ed36af551
--
2.49.0
G
G
Giacomo Leidi wrote on 15 Jun 15:38 -0700
[PATCH v13 3/5] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
787cf8d7e042608d6720ff58614f62e5d10920f2.1750027137.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/build/oci-containers.scm: New file containg OCI runtime business
logic used in OCI backed Shepherd services.
oci-read-lines
(oci-system*,oci-object-exists?,oci-object-service-available?
oci-image-load,oci-log-verbose,oci-container-execlp,oci-object-create):
New procedures.
* gnu/local.mk: Add it.
* gnu/services/containers.scm (list-of-oci-containers?,
list-of-oci-networks?,list-of-oci-volumes?,%oci-supported-runtimes,
oci-runtime?,oci-runtime-system-environment,oci-runtime-system-extra-arguments,
oci-runtime-system-requirement,oci-runtime-cli,oci-runtime-system-cli,
oci-runtime-home-cli,oci-runtime-name,oci-runtime-group,
oci-container-shepherd-name,oci-networks-shepherd-name,
oci-networks-home-shepherd-name,oci-volumes-shepherd-name,
oci-volumes-home-shepherd-name,oci-container-configuration->options,
oci-network-configuration->options,oci-volume-configuration->options,
oci-container-shepherd-service,oci-objects-merge-lst,oci-extension-merge,
oci-service-accounts,oci-service-profile,oci-service-subids,
oci-configuration->shepherd-services,oci-configuration-extend): New
procedures.
(image-reference): Implement unambiguous naming convention, that paired
with the new implementation for listing caches images with docker ls or
podman ls, allows for more efficient image caching.
(oci-container-configuration)[user,group]: Change default-type to
maybe-string, since by default containers will run under the user and
group declared in oci-configuration records. When unset the
oci-service-type will derive their value from the OCI runtime state.
[runtime,host-environment,environment,shepherd-actions,ports,extra-arguments]:
define a predicate and use it as a type in the configuration. This
way errors are reported with source location information.
(lower-manifest): Defer to caller the logic of setting up an image tag.
(lower-oci-image): Rename to load-oci-image-state.
(oci-runtime-state): Intermediate representation of the OCI runtime
details. It is supposed to be an internal API.
(oci-state): Intermediate representation of the OCI provisioning state,
such as containers and networks. It is supposed to be an internal API.
(oci-container-invocation): Intermediate representation of the OCI
runtime run command to start a container. It is supposed to be an
internal API.
(%oci-image-loader): Rename to oci-image-loader and use
oci-runtime-state and (gnu build oci-containers).
(oci-container-shepherd-service): Use oci-state and oci-runtime-state,
add command-line action.
(oci-network-configuration,oci-volume-configuration,oci-configuration,
oci-extension): New record types.
(oci-service-type): New service-type.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
doc/guix.texi | 306 ++++++--
gnu/build/oci-containers.scm | 210 ++++++
gnu/local.mk | 1 +
gnu/services/containers.scm | 1378 +++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 38 +-
gnu/tests/containers.scm | 561 +++++++++++++-
6 files changed, 2221 insertions(+), 273 deletions(-)
create mode 100644 gnu/build/oci-containers.scm

Toggle diff (387 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index eb55d337f57..fbffead4f9f 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -44108,59 +44108,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@samp{docker run} or @samp{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources, it can be either a package or a string representing
+an absolute file name to the runtime binary entrypoint. When unset it will default
+to @code{docker-cli} package for the @code{'docker} runtime or to @code{podman}
+package for the @code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -44180,16 +44283,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @samp{docker run}
+or @samp{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -44197,22 +44300,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -44240,7 +44345,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -44251,10 +44356,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -44265,25 +44371,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker run} or @samp{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker network create} or @samp{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker volume create} or @
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 15 Jun 15:38 -0700
[PATCH v13 2/5] gnu: Move with-retries outside dbus-service.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
40b4f09bbb9be45078a772287b2744ab9f020043.1750027137.git.goodoldpaul@autistici.org
This patch moves with-retries outside of (gnu build dbus-service) into a
more general (gnu build utils) which can be imported without
unnecessarily importing dbus related symbols.

* gnu/build/dbus-service.scm (sleep,with-retries): Move to...
* gnu/build/utils.scm: ...here.
* gnu/local.mk: Add gnu/build/utils.scm.
* gnu/build/jami-service.scm: Import (gnu build utils).
* gnu/services/telephony.scm (jami-account->alist): Format.
(jami-shepherd-services): Import (gnu build utils).
* gnu/test/messaging.scm (run-ngircd-test): Import (gnu build utils).
(run-pounce-test): Import (gnu build utils).
* gnu/test/telephony.scm (run-jami-test): Import (gnu build utils) and
format.

Change-Id: I3c1768f884ca46d0820a801bd0310c2ec8f3da54
---
gnu/build/dbus-service.scm | 39 +++------------------------
gnu/build/jami-service.scm | 1 +
gnu/build/utils.scm | 55 ++++++++++++++++++++++++++++++++++++++
gnu/local.mk | 1 +
gnu/services/telephony.scm | 9 ++++---
gnu/tests/messaging.scm | 8 +++---
gnu/tests/telephony.scm | 11 ++++----
7 files changed, 76 insertions(+), 48 deletions(-)
create mode 100644 gnu/build/utils.scm

Toggle diff (279 lines)
diff --git a/gnu/build/dbus-service.scm b/gnu/build/dbus-service.scm
index 688afe44c3d..9bbcd457512 100644
--- a/gnu/build/dbus-service.scm
+++ b/gnu/build/dbus-service.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021, 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,6 +25,7 @@
;;; Code:
(define-module (gnu build dbus-service)
+ #:use-module (gnu build utils)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-19)
@@ -54,45 +56,10 @@ (define-module (gnu build dbus-service)
call-dbus-method
dbus-available-services
- dbus-service-available?
-
- with-retries))
+ dbus-service-available?))
(define %dbus-query-timeout 2) ;in seconds
-;;; Use Fibers' sleep to enable cooperative scheduling in Shepherd >= 0.9.0,
-;;; which is required at least for the Jami service.
-(define sleep*
- (lambda () ;delay execution
- (if (resolve-module '(fibers) #f #:ensure #f)
- (module-ref (resolve-interface '(fibers)) 'sleep)
- (begin
- (format #t "Fibers not available -- blocking 'sleep' in use~%")
- sleep))))
-
-;;;
-;;; Utilities.
-;;;
-
-(define-syntax-rule (with-retries n delay body ...)
- "Retry the code in BODY up to N times until it doesn't raise an exception nor
-return #f, else raise an error. A delay of DELAY seconds is inserted before
-each retry."
- (let loop ((attempts 0))
- (catch #t
- (lambda ()
- (let ((result (begin body ...)))
- (if (not result)
- (error "failed attempt" attempts)
- result)))
- (lambda args
- (if (< attempts n)
- (begin
- ((sleep*) delay) ;else wait and retry
- (loop (+ 1 attempts)))
- (error "maximum number of retry attempts reached"
- (quote body ...) args))))))
-
;;;
;;; Low level wrappers above AC/D-Bus.
diff --git a/gnu/build/jami-service.scm b/gnu/build/jami-service.scm
index a00785f699b..7c2c48d821a 100644
--- a/gnu/build/jami-service.scm
+++ b/gnu/build/jami-service.scm
@@ -25,6 +25,7 @@
(define-module (gnu build jami-service)
#:use-module (gnu build dbus-service)
+ #:use-module (gnu build utils)
#:use-module (ice-9 format)
#:use-module (ice-9 match)
#:use-module (ice-9 rdelim)
diff --git a/gnu/build/utils.scm b/gnu/build/utils.scm
new file mode 100644
index 00000000000..1aa72358bd8
--- /dev/null
+++ b/gnu/build/utils.scm
@@ -0,0 +1,55 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;;
+;;; This module contains helpers that could useful to any service.
+;;;
+;;; Code:
+
+(define-module (gnu build utils)
+ #:export (with-retries))
+
+;;; Use Fibers' sleep to enable cooperative scheduling in Shepherd >= 0.9.0,
+;;; which is required at least for the Jami service.
+(define sleep*
+ (lambda () ;delay execution
+ (if (resolve-module '(fibers) #f #:ensure #f)
+ (module-ref (resolve-interface '(fibers)) 'sleep)
+ (begin
+ (format #t "Fibers not available -- blocking 'sleep' in use~%")
+ sleep))))
+
+(define-syntax-rule (with-retries n delay body ...)
+ "Retry the code in BODY up to N times until it doesn't raise an exception nor
+return #f, else raise an error. A delay of DELAY seconds is inserted before
+each retry."
+ (let loop ((attempts 0))
+ (catch #t
+ (lambda ()
+ (let ((result (begin body ...)))
+ (if (not result)
+ (error "failed attempt" attempts)
+ result)))
+ (lambda args
+ (if (< attempts n)
+ (begin
+ ((sleep*) delay) ;else wait and retry
+ (loop (+ 1 attempts)))
+ (error "maximum number of retry attempts reached"
+ (quote body ...) args))))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 1565828f889..063164eea11 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -843,6 +843,7 @@ GNU_SYSTEM_MODULES = \
%D%/build/linux-modules.scm \
%D%/build/marionette.scm \
%D%/build/secret-service.scm \
+ %D%/build/utils.scm \
\
%D%/tests.scm \
%D%/tests/audio.scm \
diff --git a/gnu/services/telephony.scm b/gnu/services/telephony.scm
index 9926f4107de..ad6959e161b 100644
--- a/gnu/services/telephony.scm
+++ b/gnu/services/telephony.scm
@@ -210,7 +210,7 @@ (define (jami-account->alist jami-account-object)
(tfilter-maybe-value jami-account-object)
(tmap (lambda (field)
(let* ((name (field-name->account-detail
- (configuration-field-name field)))
+ (configuration-field-name field)))
(value ((configuration-field-serializer field)
name ((configuration-field-getter field)
jami-account-object))))
@@ -360,7 +360,8 @@ (define (jami-shepherd-services config)
;; variant of the 'sleep' procedure.
guile-fibers)
(with-imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
+ (gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)))
@@ -541,7 +542,8 @@ (define (jami-shepherd-services config)
(list (shepherd-service
(documentation "Run a D-Bus session for the Jami daemon.")
(provision '(jami-dbus-session))
- (modules `((gnu build dbus-service)
+ (modules `((gnu build utils)
+ (gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)
,@%default-modules))
@@ -587,6 +589,7 @@ (define (jami-shepherd-services config)
(ice-9 receive)
(srfi srfi-1)
(srfi srfi-26)
+ (gnu build utils)
(gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)
diff --git a/gnu/tests/messaging.scm b/gnu/tests/messaging.scm
index 8df67433a7f..2eb99331b52 100644
--- a/gnu/tests/messaging.scm
+++ b/gnu/tests/messaging.scm
@@ -269,7 +269,7 @@ (define (run-ngircd-test)
(marionette-operating-system
%ngircd-os
#:imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
(guix build utils)
(gnu services herd)))))))
@@ -298,7 +298,7 @@ (define (run-ngircd-test)
(test-assert "basic irc operations function as expected"
(marionette-eval
'(begin
- (use-modules ((gnu build dbus-service) #:select (with-retries))
+ (use-modules (gnu build utils)
(ice-9 textual-ports))
(define (write-command command)
@@ -437,7 +437,7 @@ (define (run-pounce-test)
(marionette-operating-system
%pounce-os
#:imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
(guix build utils)
(gnu services herd)))))
(memory-size 1024)))
@@ -470,7 +470,7 @@ (define (run-pounce-test)
(test-assert "pounce functions as an irc bouncer"
(marionette-eval
'(begin
- (use-modules ((gnu build dbus-service) #:select (with-retries))
+ (use-modules (gnu build utils)
(guix build utils)
(ice-9 textual-ports))
diff --git a/gnu/tests/telephony.scm b/gnu/tests/telephony.scm
index f03ea963f7e..3a085762323 100644
--- a/gnu/tests/telephony.scm
+++ b/gnu/tests/telephony.scm
@@ -143,7 +143,8 @@ (define* (run-jami-test #:key provisioning? partial?)
#:imported-modules '((gnu services herd)
(guix combinators)
(gnu build jami-service)
- (gnu build dbus-service))))
+ (gnu build dbus-service)
+ (gnu build utils))))
(define vm (virtual-machine
(operating-system os)
(memory-size 512)))
@@ -209,7 +210,7 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "service can be stopped"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu build jami-service)
(gnu services herd)
(rnrs base))
@@ -223,10 +224,10 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "service can be restarted"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu build jami-service)
(gnu services herd)
- (rnrs base) )
+ (rnrs base))
;; Start the service.
(start-service 'jami)
(with-retries 40 1 (jami-service-available?))
@@ -239,7 +240,7 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "jami accounts provisioning, account present"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu services herd)
(rnrs base))
;; Accounts take some time to appear after being added.
--
2.49.0
G
G
Giacomo Leidi wrote on 15 Jun 15:38 -0700
[PATCH v13 4/5] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
3fde3db3b7be21c6a9cfa9a609cecb810ad2ae1e.1750027137.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
gnu/tests/containers.scm | 84 +++++++++++++++----------------
gnu/tests/docker.scm | 104 ++++++++++++++++++++++-----------------
2 files changed, 101 insertions(+), 87 deletions(-)

Toggle diff (283 lines)
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 051a4c740bf..0db22e3a07f 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -46,6 +46,9 @@ (define-module (gnu tests containers)
%test-oci-service-rootless-podman
%test-oci-service-docker))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %rootless-podman-os
(simple-operating-system
@@ -69,13 +72,48 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +292,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +345,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-network
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 5dcf05a17e3..1f264d52486 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -48,6 +49,9 @@ (define-module (gnu tests docker)
%test-docker-system
%test-oci-container))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %docker-os
(simple-operating-system
(service dhcp-client-service-type)
@@ -57,6 +61,41 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +212,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +221,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +353,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.49.0
G
G
Giacomo Leidi wrote on 15 Jun 15:38 -0700
[PATCH v13 5/5] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
d50b4c4fd75a24ef079a5608dd13e2a139c023d8.1750027137.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 49 +++++++++++++
gnu/local.mk | 1 +
3 files changed, 164 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (194 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index fbffead4f9f..77c896cf0c7 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -52363,6 +52363,120 @@ Miscellaneous Home Services
documentation of the system service (@pxref{Miscellaneous Services,
@code{readymedia-service-type}}).
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 00000000000..1ccdb3b2464
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,49 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type
+ (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli))))
+ (service-extension home-shepherd-service-type
+ oci-configuration->shepherd-services)))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 248220f96eb..3932e1f6eb1 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -104,6 +104,7 @@ GNU_SYSTEM_MODULES = \
%D%/home/services.scm \
%D%/home/services/admin.scm \
%D%/home/services/backup.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.49.0
M
M
Maxim Cournoyer wrote on 18 Jun 19:11 -0700
Re: [bug#76081] [PATCH v13 1/5] tests: oci-container: Set explicit timeouts.
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)(address . 76081@debbugs.gnu.org)
87plf0pjj0.fsf@terra.mail-host-address-is-not-set
Hi,

Giacomo Leidi <goodoldpaul@autistici.org> writes:

Toggle quote (5 lines)
> * gnu/tests/docker.scm: Simplify %test-oci-container test case and add
> explicit timeouts to tests outcomes.
>
> Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>

Perhaps surprisingly, in Guix we only used 'Signed-off-by' when
committing the patches someone else authored, unlike in the Linux kernel
project and elsewhere, where it means "I certify this work is mine and I
have the rights to distribute it" or similar.

--
Thanks,
Maxim
P
(name . Maxim Cournoyer)(address . maxim@guixotic.coop)(address . 76081@debbugs.gnu.org)
a201078e-b738-450c-ab41-1a0f0b61fd2e@autistici.org
Hi Maxim, I dropped all the Sign-offs even in other patches. I'll send
them in the next revision once you are done with your comments on other
patches. Thank you for spotting these,


cheers,

giacomo
G
G
Giacomo Leidi wrote on 23 Aug 10:37 -0700
(name . Maxim Cournoyer)(address . maxim@guixotic.coop)(address . 76081@debbugs.gnu.org)
8224f18c-a824-4c55-bc82-ea2a72baeeca@autistici.org
Hi Maxim,

I dropped all the Signed-off s from the commit messages. I think I
addressed all your other comments, I'm sending a rev 14 rebased on
current master. Do you think we are at a point where the
oci-service-type can be merged into Guix mainline?


Thank you for your huge help,


Giacomo
G
G
Giacomo Leidi wrote on 23 Aug 10:38 -0700
[PATCH v14 1/5] tests: oci-container: Set explicit timeouts.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
07d557d2b06cd9c00ec767857da0575a0adace3e.1755970702.git.goodoldpaul@autistici.org
* gnu/tests/docker.scm: Simplify %test-oci-container test case and add
explicit timeouts to tests outcomes.

Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
gnu/tests/docker.scm | 99 ++++++++++++++++++--------------------------
1 file changed, 41 insertions(+), 58 deletions(-)

Toggle diff (129 lines)
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 8952daab2f..9fee3905f0 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -1,7 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 Danny Milosavljevic <dannym@scratchpost.org>
;;; Copyright © 2019-2023 Ludovic Courtès <ludo@gnu.org>
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -414,71 +414,54 @@ (define (run-oci-container-test)
(test-runner-current (system-test-runner #$output))
(test-begin "oci-container")
- (test-assert "containerd service running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'containerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (test-assert "containerd PID file present"
- (wait-for-file "/run/containerd/containerd.pid" marionette))
-
- (test-assert "dockerd running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'dockerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (sleep 10) ; let service start
+ (wait-for-file "/run/containerd/containerd.pid" marionette)
(test-assert "docker-guile running"
(marionette-eval
'(begin
(use-modules (gnu services herd))
- (match (start-service 'docker-guile)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
+ (wait-for-service 'docker-guile #:timeout 120)
+ #t)
marionette))
- (test-equal "passing host environment variables and volumes"
- '("value" "hello")
- (marionette-eval
- `(begin
- (use-modules (ice-9 popen)
- (ice-9 rdelim))
-
- (define slurp
- (lambda args
- (let* ((port (apply open-pipe* OPEN_READ args))
- (output (let ((line (read-line port)))
- (if (eof-object? line)
- ""
- line)))
- (status (close-pipe port)))
- output)))
- (let* ((response1 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
- (response2 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
+ (test-assert "passing host environment variables and volumes"
+ (begin
+ (define (run-test)
+ (marionette-eval
+ `(begin
+ (use-modules (ice-9 popen)
+ (ice-9 rdelim))
+
+ (define slurp
+ (lambda args
+ (let* ((port (apply open-pipe* OPEN_READ args))
+ (output (let ((line (read-line port)))
+ (if (eof-object? line)
+ ""
+ line)))
+ (status (close-pipe port)))
+ output)))
+ (let* ((response1 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
+ (response2 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
(display (call-with-input-file \"/shared.txt\" read-line)))")))
- (list response1 response2)))
- marionette))
+ (list response1 response2)))
+ marionette))
+ ;; Allow services to come up on slower machines
+ (let loop ((attempts 0))
+ (if (= attempts 60)
+ (error "Service didn't come up after more than 60 seconds")
+ (if (equal? '("value" "hello")
+ (run-test))
+ #t
+ (begin
+ (sleep 1)
+ (loop (+ 1 attempts))))))))
(test-end))))

base-commit: 73f943fd9f4cc69bd0463e6c64abeb65e060e1da
--
2.51.0
G
G
Giacomo Leidi wrote on 23 Aug 10:38 -0700
[PATCH v14 3/5] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
feecd51898417439178adbba0014633da41f8b87.1755970702.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/build/oci-containers.scm: New file containg OCI runtime business
logic used in OCI backed Shepherd services.
oci-read-lines
(oci-system*,oci-object-exists?,oci-object-service-available?
oci-image-load,oci-log-verbose,oci-container-execlp,oci-object-create):
New procedures.
* gnu/local.mk: Add it.
* gnu/services/containers.scm (list-of-oci-containers?,
list-of-oci-networks?,list-of-oci-volumes?,%oci-supported-runtimes,
oci-runtime?,oci-runtime-system-environment,oci-runtime-system-extra-arguments,
oci-runtime-system-requirement,oci-runtime-cli,oci-runtime-system-cli,
oci-runtime-home-cli,oci-runtime-name,oci-runtime-group,
oci-container-shepherd-name,oci-networks-shepherd-name,
oci-networks-home-shepherd-name,oci-volumes-shepherd-name,
oci-volumes-home-shepherd-name,oci-container-configuration->options,
oci-network-configuration->options,oci-volume-configuration->options,
oci-container-shepherd-service,oci-objects-merge-lst,oci-extension-merge,
oci-service-accounts,oci-service-profile,oci-service-subids,
oci-configuration->shepherd-services,oci-configuration-extend): New
procedures.
(image-reference): Implement unambiguous naming convention, that paired
with the new implementation for listing caches images with docker ls or
podman ls, allows for more efficient image caching.
(oci-container-configuration)[user,group]: Change default-type to
maybe-string, since by default containers will run under the user and
group declared in oci-configuration records. When unset the
oci-service-type will derive their value from the OCI runtime state.
[runtime,host-environment,environment,shepherd-actions,ports,extra-arguments]:
define a predicate and use it as a type in the configuration. This
way errors are reported with source location information.
(lower-manifest): Defer to caller the logic of setting up an image tag.
(lower-oci-image): Rename to load-oci-image-state.
(oci-runtime-state): Intermediate representation of the OCI runtime
details. It is supposed to be an internal API.
(oci-state): Intermediate representation of the OCI provisioning state,
such as containers and networks. It is supposed to be an internal API.
(oci-container-invocation): Intermediate representation of the OCI
runtime run command to start a container. It is supposed to be an
internal API.
(%oci-image-loader): Rename to oci-image-loader and use
oci-runtime-state and (gnu build oci-containers).
(oci-container-shepherd-service): Use oci-state and oci-runtime-state,
add command-line action.
(oci-network-configuration,oci-volume-configuration,oci-configuration,
oci-extension): New record types.
(oci-service-type): New service-type.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
doc/guix.texi | 306 ++++++--
gnu/build/oci-containers.scm | 210 ++++++
gnu/local.mk | 1 +
gnu/services/containers.scm | 1363 +++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 38 +-
gnu/tests/containers.scm | 561 +++++++++++++-
6 files changed, 2206 insertions(+), 273 deletions(-)
create mode 100644 gnu/build/oci-containers.scm

Toggle diff (387 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index e2d546be0a..cbbbdae9a4 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -44746,59 +44746,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@samp{docker run} or @samp{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources, it can be either a package or a string representing
+an absolute file name to the runtime binary entrypoint. When unset it will default
+to @code{docker-cli} package for the @code{'docker} runtime or to @code{podman}
+package for the @code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -44818,16 +44921,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @samp{docker run}
+or @samp{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -44835,22 +44938,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -44878,7 +44983,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -44889,10 +44994,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -44903,25 +45009,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker run} or @samp{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker network create} or @samp{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker volume create} or @sa
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 23 Aug 10:38 -0700
[PATCH v14 4/5] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
c05e3cf6799d8ff2c825ee10f241e2743f251bb3.1755970702.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
gnu/tests/containers.scm | 84 +++++++++++++++----------------
gnu/tests/docker.scm | 104 ++++++++++++++++++++++-----------------
2 files changed, 101 insertions(+), 87 deletions(-)

Toggle diff (283 lines)
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index b38b67284e..496ddabbbd 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -46,6 +46,9 @@ (define-module (gnu tests containers)
%test-oci-service-rootless-podman
%test-oci-service-docker))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %rootless-podman-os
(simple-operating-system
@@ -69,13 +72,48 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +292,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +345,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-network
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 9fee3905f0..4fc50a99a7 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -48,6 +49,9 @@ (define-module (gnu tests docker)
%test-docker-system
%test-oci-container))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %docker-os
(simple-operating-system
(service dhcpcd-service-type)
@@ -57,6 +61,41 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +212,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +221,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +353,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.51.0
G
G
Giacomo Leidi wrote on 23 Aug 10:38 -0700
[PATCH v14 2/5] gnu: Move with-retries outside dbus-service.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
25e6a6fb6693f70f55763f1de46fc7246eb728e4.1755970702.git.goodoldpaul@autistici.org
This patch moves with-retries outside of (gnu build dbus-service) into a
more general (gnu build utils) which can be imported without
unnecessarily importing dbus related symbols.

* gnu/build/dbus-service.scm (sleep,with-retries): Move to...
* gnu/build/utils.scm: ...here.
* gnu/local.mk: Add gnu/build/utils.scm.
* gnu/build/jami-service.scm: Import (gnu build utils).
* gnu/services/telephony.scm (jami-account->alist): Format.
(jami-shepherd-services): Import (gnu build utils).
* gnu/test/messaging.scm (run-ngircd-test): Import (gnu build utils).
(run-pounce-test): Import (gnu build utils).
* gnu/test/telephony.scm (run-jami-test): Import (gnu build utils) and
format.

Change-Id: I3c1768f884ca46d0820a801bd0310c2ec8f3da54
---
gnu/build/dbus-service.scm | 39 +++------------------------
gnu/build/jami-service.scm | 1 +
gnu/build/utils.scm | 55 ++++++++++++++++++++++++++++++++++++++
gnu/local.mk | 1 +
gnu/services/telephony.scm | 9 ++++---
gnu/tests/messaging.scm | 8 +++---
gnu/tests/telephony.scm | 11 ++++----
7 files changed, 76 insertions(+), 48 deletions(-)
create mode 100644 gnu/build/utils.scm

Toggle diff (279 lines)
diff --git a/gnu/build/dbus-service.scm b/gnu/build/dbus-service.scm
index 688afe44c3..9bbcd45751 100644
--- a/gnu/build/dbus-service.scm
+++ b/gnu/build/dbus-service.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021, 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,6 +25,7 @@
;;; Code:
(define-module (gnu build dbus-service)
+ #:use-module (gnu build utils)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-19)
@@ -54,45 +56,10 @@ (define-module (gnu build dbus-service)
call-dbus-method
dbus-available-services
- dbus-service-available?
-
- with-retries))
+ dbus-service-available?))
(define %dbus-query-timeout 2) ;in seconds
-;;; Use Fibers' sleep to enable cooperative scheduling in Shepherd >= 0.9.0,
-;;; which is required at least for the Jami service.
-(define sleep*
- (lambda () ;delay execution
- (if (resolve-module '(fibers) #f #:ensure #f)
- (module-ref (resolve-interface '(fibers)) 'sleep)
- (begin
- (format #t "Fibers not available -- blocking 'sleep' in use~%")
- sleep))))
-
-;;;
-;;; Utilities.
-;;;
-
-(define-syntax-rule (with-retries n delay body ...)
- "Retry the code in BODY up to N times until it doesn't raise an exception nor
-return #f, else raise an error. A delay of DELAY seconds is inserted before
-each retry."
- (let loop ((attempts 0))
- (catch #t
- (lambda ()
- (let ((result (begin body ...)))
- (if (not result)
- (error "failed attempt" attempts)
- result)))
- (lambda args
- (if (< attempts n)
- (begin
- ((sleep*) delay) ;else wait and retry
- (loop (+ 1 attempts)))
- (error "maximum number of retry attempts reached"
- (quote body ...) args))))))
-
;;;
;;; Low level wrappers above AC/D-Bus.
diff --git a/gnu/build/jami-service.scm b/gnu/build/jami-service.scm
index a00785f699..7c2c48d821 100644
--- a/gnu/build/jami-service.scm
+++ b/gnu/build/jami-service.scm
@@ -25,6 +25,7 @@
(define-module (gnu build jami-service)
#:use-module (gnu build dbus-service)
+ #:use-module (gnu build utils)
#:use-module (ice-9 format)
#:use-module (ice-9 match)
#:use-module (ice-9 rdelim)
diff --git a/gnu/build/utils.scm b/gnu/build/utils.scm
new file mode 100644
index 0000000000..1aa72358bd
--- /dev/null
+++ b/gnu/build/utils.scm
@@ -0,0 +1,55 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;;
+;;; This module contains helpers that could useful to any service.
+;;;
+;;; Code:
+
+(define-module (gnu build utils)
+ #:export (with-retries))
+
+;;; Use Fibers' sleep to enable cooperative scheduling in Shepherd >= 0.9.0,
+;;; which is required at least for the Jami service.
+(define sleep*
+ (lambda () ;delay execution
+ (if (resolve-module '(fibers) #f #:ensure #f)
+ (module-ref (resolve-interface '(fibers)) 'sleep)
+ (begin
+ (format #t "Fibers not available -- blocking 'sleep' in use~%")
+ sleep))))
+
+(define-syntax-rule (with-retries n delay body ...)
+ "Retry the code in BODY up to N times until it doesn't raise an exception nor
+return #f, else raise an error. A delay of DELAY seconds is inserted before
+each retry."
+ (let loop ((attempts 0))
+ (catch #t
+ (lambda ()
+ (let ((result (begin body ...)))
+ (if (not result)
+ (error "failed attempt" attempts)
+ result)))
+ (lambda args
+ (if (< attempts n)
+ (begin
+ ((sleep*) delay) ;else wait and retry
+ (loop (+ 1 attempts)))
+ (error "maximum number of retry attempts reached"
+ (quote body ...) args))))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 1f26871a13..475479cf28 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -838,6 +838,7 @@ GNU_SYSTEM_MODULES = \
%D%/build/linux-modules.scm \
%D%/build/marionette.scm \
%D%/build/secret-service.scm \
+ %D%/build/utils.scm \
\
%D%/tests.scm \
%D%/tests/audio.scm \
diff --git a/gnu/services/telephony.scm b/gnu/services/telephony.scm
index 423e007871..aeb79034ee 100644
--- a/gnu/services/telephony.scm
+++ b/gnu/services/telephony.scm
@@ -210,7 +210,7 @@ (define (jami-account->alist jami-account-object)
(tfilter-maybe-value jami-account-object)
(tmap (lambda (field)
(let* ((name (field-name->account-detail
- (configuration-field-name field)))
+ (configuration-field-name field)))
(value ((configuration-field-serializer field)
name ((configuration-field-getter field)
jami-account-object))))
@@ -360,7 +360,8 @@ (define (jami-shepherd-services config)
;; variant of the 'sleep' procedure.
guile-fibers)
(with-imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
+ (gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)))
@@ -541,7 +542,8 @@ (define (jami-shepherd-services config)
(list (shepherd-service
(documentation "Run a D-Bus session for the Jami daemon.")
(provision '(jami-dbus-session))
- (modules `((gnu build dbus-service)
+ (modules `((gnu build utils)
+ (gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)
,@%default-modules))
@@ -587,6 +589,7 @@ (define (jami-shepherd-services config)
(ice-9 receive)
(srfi srfi-1)
(srfi srfi-26)
+ (gnu build utils)
(gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)
diff --git a/gnu/tests/messaging.scm b/gnu/tests/messaging.scm
index 0601ff6f69..f39e729655 100644
--- a/gnu/tests/messaging.scm
+++ b/gnu/tests/messaging.scm
@@ -271,7 +271,7 @@ (define (run-ngircd-test)
(marionette-operating-system
%ngircd-os
#:imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
(guix build utils)
(gnu services herd)))))))
@@ -300,7 +300,7 @@ (define (run-ngircd-test)
(test-assert "basic irc operations function as expected"
(marionette-eval
'(begin
- (use-modules ((gnu build dbus-service) #:select (with-retries))
+ (use-modules (gnu build utils)
(ice-9 textual-ports))
(define (write-command command)
@@ -439,7 +439,7 @@ (define (run-pounce-test)
(marionette-operating-system
%pounce-os
#:imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
(guix build utils)
(gnu services herd)))))
(memory-size 1024)))
@@ -472,7 +472,7 @@ (define (run-pounce-test)
(test-assert "pounce functions as an irc bouncer"
(marionette-eval
'(begin
- (use-modules ((gnu build dbus-service) #:select (with-retries))
+ (use-modules (gnu build utils)
(guix build utils)
(ice-9 textual-ports))
diff --git a/gnu/tests/telephony.scm b/gnu/tests/telephony.scm
index ef5474bef0..a9b8d100f8 100644
--- a/gnu/tests/telephony.scm
+++ b/gnu/tests/telephony.scm
@@ -143,7 +143,8 @@ (define* (run-jami-test #:key provisioning? partial?)
#:imported-modules '((gnu services herd)
(guix combinators)
(gnu build jami-service)
- (gnu build dbus-service))))
+ (gnu build dbus-service)
+ (gnu build utils))))
(define vm (virtual-machine
(operating-system os)
(memory-size 512)))
@@ -209,7 +210,7 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "service can be stopped"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu build jami-service)
(gnu services herd)
(rnrs base))
@@ -223,10 +224,10 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "service can be restarted"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu build jami-service)
(gnu services herd)
- (rnrs base) )
+ (rnrs base))
;; Start the service.
(start-service 'jami)
(with-retries 40 1 (jami-service-available?))
@@ -239,7 +240,7 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "jami accounts provisioning, account present"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu services herd)
(rnrs base))
;; Accounts take some time to appear after being added.
--
2.51.0
G
G
Giacomo Leidi wrote on 23 Aug 10:38 -0700
[PATCH v14 5/5] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
8c91573f5ba8272c185a0639787a9e7204b0ad75.1755970702.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
Signed-off-by: Giacomo Leidi <goodoldpaul@autistici.org>
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 49 +++++++++++++
gnu/local.mk | 1 +
3 files changed, 164 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (194 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index cbbbdae9a4..0c7f0729cb 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -53001,6 +53001,120 @@ Miscellaneous Home Services
documentation of the system service (@pxref{Miscellaneous Services,
@code{readymedia-service-type}}).
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 0000000000..1ccdb3b246
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,49 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type
+ (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli))))
+ (service-extension home-shepherd-service-type
+ oci-configuration->shepherd-services)))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 8a7c23e8b6..4c77f5e7e1 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -105,6 +105,7 @@ GNU_SYSTEM_MODULES = \
%D%/home/services.scm \
%D%/home/services/admin.scm \
%D%/home/services/backup.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.51.0
G
G
Giacomo Leidi wrote on 23 Aug 10:43 -0700
Re: [bug#76081] [PATCH v13 1/5] tests: oci-container: Set explicit timeouts.
(name . Maxim Cournoyer)(address . maxim@guixotic.coop)(address . 76081@debbugs.gnu.org)
f6547981-8187-4efd-8597-4fbdf68a504e@autistici.org
Of course I sent the wrong branch :) Rev 15 is the correct one, sorry
for the noise
G
G
Giacomo Leidi wrote on 23 Aug 10:46 -0700
[PATCH v15 2/5] gnu: Move with-retries outside dbus-service.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
00b4cf11e0d739ad6553e0fab0715b9dd76a5152.1755971216.git.goodoldpaul@autistici.org
This patch moves with-retries outside of (gnu build dbus-service) into a
more general (gnu build utils) which can be imported without
unnecessarily importing dbus related symbols.

* gnu/build/dbus-service.scm (sleep,with-retries): Move to...
* gnu/build/utils.scm: ...here.
* gnu/local.mk: Add gnu/build/utils.scm.
* gnu/build/jami-service.scm: Import (gnu build utils).
* gnu/services/telephony.scm (jami-account->alist): Format.
(jami-shepherd-services): Import (gnu build utils).
* gnu/test/messaging.scm (run-ngircd-test): Import (gnu build utils).
(run-pounce-test): Import (gnu build utils).
* gnu/test/telephony.scm (run-jami-test): Import (gnu build utils) and
format.

Change-Id: I3c1768f884ca46d0820a801bd0310c2ec8f3da54
---
gnu/build/dbus-service.scm | 39 +++------------------------
gnu/build/jami-service.scm | 1 +
gnu/build/utils.scm | 55 ++++++++++++++++++++++++++++++++++++++
gnu/local.mk | 1 +
gnu/services/telephony.scm | 9 ++++---
gnu/tests/messaging.scm | 8 +++---
gnu/tests/telephony.scm | 11 ++++----
7 files changed, 76 insertions(+), 48 deletions(-)
create mode 100644 gnu/build/utils.scm

Toggle diff (279 lines)
diff --git a/gnu/build/dbus-service.scm b/gnu/build/dbus-service.scm
index 688afe44c3..9bbcd45751 100644
--- a/gnu/build/dbus-service.scm
+++ b/gnu/build/dbus-service.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2021, 2022 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -24,6 +25,7 @@
;;; Code:
(define-module (gnu build dbus-service)
+ #:use-module (gnu build utils)
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-19)
@@ -54,45 +56,10 @@ (define-module (gnu build dbus-service)
call-dbus-method
dbus-available-services
- dbus-service-available?
-
- with-retries))
+ dbus-service-available?))
(define %dbus-query-timeout 2) ;in seconds
-;;; Use Fibers' sleep to enable cooperative scheduling in Shepherd >= 0.9.0,
-;;; which is required at least for the Jami service.
-(define sleep*
- (lambda () ;delay execution
- (if (resolve-module '(fibers) #f #:ensure #f)
- (module-ref (resolve-interface '(fibers)) 'sleep)
- (begin
- (format #t "Fibers not available -- blocking 'sleep' in use~%")
- sleep))))
-
-;;;
-;;; Utilities.
-;;;
-
-(define-syntax-rule (with-retries n delay body ...)
- "Retry the code in BODY up to N times until it doesn't raise an exception nor
-return #f, else raise an error. A delay of DELAY seconds is inserted before
-each retry."
- (let loop ((attempts 0))
- (catch #t
- (lambda ()
- (let ((result (begin body ...)))
- (if (not result)
- (error "failed attempt" attempts)
- result)))
- (lambda args
- (if (< attempts n)
- (begin
- ((sleep*) delay) ;else wait and retry
- (loop (+ 1 attempts)))
- (error "maximum number of retry attempts reached"
- (quote body ...) args))))))
-
;;;
;;; Low level wrappers above AC/D-Bus.
diff --git a/gnu/build/jami-service.scm b/gnu/build/jami-service.scm
index a00785f699..7c2c48d821 100644
--- a/gnu/build/jami-service.scm
+++ b/gnu/build/jami-service.scm
@@ -25,6 +25,7 @@
(define-module (gnu build jami-service)
#:use-module (gnu build dbus-service)
+ #:use-module (gnu build utils)
#:use-module (ice-9 format)
#:use-module (ice-9 match)
#:use-module (ice-9 rdelim)
diff --git a/gnu/build/utils.scm b/gnu/build/utils.scm
new file mode 100644
index 0000000000..1aa72358bd
--- /dev/null
+++ b/gnu/build/utils.scm
@@ -0,0 +1,55 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;;
+;;; This module contains helpers that could useful to any service.
+;;;
+;;; Code:
+
+(define-module (gnu build utils)
+ #:export (with-retries))
+
+;;; Use Fibers' sleep to enable cooperative scheduling in Shepherd >= 0.9.0,
+;;; which is required at least for the Jami service.
+(define sleep*
+ (lambda () ;delay execution
+ (if (resolve-module '(fibers) #f #:ensure #f)
+ (module-ref (resolve-interface '(fibers)) 'sleep)
+ (begin
+ (format #t "Fibers not available -- blocking 'sleep' in use~%")
+ sleep))))
+
+(define-syntax-rule (with-retries n delay body ...)
+ "Retry the code in BODY up to N times until it doesn't raise an exception nor
+return #f, else raise an error. A delay of DELAY seconds is inserted before
+each retry."
+ (let loop ((attempts 0))
+ (catch #t
+ (lambda ()
+ (let ((result (begin body ...)))
+ (if (not result)
+ (error "failed attempt" attempts)
+ result)))
+ (lambda args
+ (if (< attempts n)
+ (begin
+ ((sleep*) delay) ;else wait and retry
+ (loop (+ 1 attempts)))
+ (error "maximum number of retry attempts reached"
+ (quote body ...) args))))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 1f26871a13..475479cf28 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -838,6 +838,7 @@ GNU_SYSTEM_MODULES = \
%D%/build/linux-modules.scm \
%D%/build/marionette.scm \
%D%/build/secret-service.scm \
+ %D%/build/utils.scm \
\
%D%/tests.scm \
%D%/tests/audio.scm \
diff --git a/gnu/services/telephony.scm b/gnu/services/telephony.scm
index 423e007871..aeb79034ee 100644
--- a/gnu/services/telephony.scm
+++ b/gnu/services/telephony.scm
@@ -210,7 +210,7 @@ (define (jami-account->alist jami-account-object)
(tfilter-maybe-value jami-account-object)
(tmap (lambda (field)
(let* ((name (field-name->account-detail
- (configuration-field-name field)))
+ (configuration-field-name field)))
(value ((configuration-field-serializer field)
name ((configuration-field-getter field)
jami-account-object))))
@@ -360,7 +360,8 @@ (define (jami-shepherd-services config)
;; variant of the 'sleep' procedure.
guile-fibers)
(with-imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
+ (gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)))
@@ -541,7 +542,8 @@ (define (jami-shepherd-services config)
(list (shepherd-service
(documentation "Run a D-Bus session for the Jami daemon.")
(provision '(jami-dbus-session))
- (modules `((gnu build dbus-service)
+ (modules `((gnu build utils)
+ (gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)
,@%default-modules))
@@ -587,6 +589,7 @@ (define (jami-shepherd-services config)
(ice-9 receive)
(srfi srfi-1)
(srfi srfi-26)
+ (gnu build utils)
(gnu build dbus-service)
(gnu build jami-service)
(gnu system file-systems)
diff --git a/gnu/tests/messaging.scm b/gnu/tests/messaging.scm
index 0601ff6f69..f39e729655 100644
--- a/gnu/tests/messaging.scm
+++ b/gnu/tests/messaging.scm
@@ -271,7 +271,7 @@ (define (run-ngircd-test)
(marionette-operating-system
%ngircd-os
#:imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
(guix build utils)
(gnu services herd)))))))
@@ -300,7 +300,7 @@ (define (run-ngircd-test)
(test-assert "basic irc operations function as expected"
(marionette-eval
'(begin
- (use-modules ((gnu build dbus-service) #:select (with-retries))
+ (use-modules (gnu build utils)
(ice-9 textual-ports))
(define (write-command command)
@@ -439,7 +439,7 @@ (define (run-pounce-test)
(marionette-operating-system
%pounce-os
#:imported-modules (source-module-closure
- '((gnu build dbus-service)
+ '((gnu build utils)
(guix build utils)
(gnu services herd)))))
(memory-size 1024)))
@@ -472,7 +472,7 @@ (define (run-pounce-test)
(test-assert "pounce functions as an irc bouncer"
(marionette-eval
'(begin
- (use-modules ((gnu build dbus-service) #:select (with-retries))
+ (use-modules (gnu build utils)
(guix build utils)
(ice-9 textual-ports))
diff --git a/gnu/tests/telephony.scm b/gnu/tests/telephony.scm
index ef5474bef0..a9b8d100f8 100644
--- a/gnu/tests/telephony.scm
+++ b/gnu/tests/telephony.scm
@@ -143,7 +143,8 @@ (define* (run-jami-test #:key provisioning? partial?)
#:imported-modules '((gnu services herd)
(guix combinators)
(gnu build jami-service)
- (gnu build dbus-service))))
+ (gnu build dbus-service)
+ (gnu build utils))))
(define vm (virtual-machine
(operating-system os)
(memory-size 512)))
@@ -209,7 +210,7 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "service can be stopped"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu build jami-service)
(gnu services herd)
(rnrs base))
@@ -223,10 +224,10 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "service can be restarted"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu build jami-service)
(gnu services herd)
- (rnrs base) )
+ (rnrs base))
;; Start the service.
(start-service 'jami)
(with-retries 40 1 (jami-service-available?))
@@ -239,7 +240,7 @@ (define* (run-jami-test #:key provisioning? partial?)
(test-assert "jami accounts provisioning, account present"
(marionette-eval
'(begin
- (use-modules (gnu build dbus-service)
+ (use-modules (gnu build utils)
(gnu services herd)
(rnrs base))
;; Accounts take some time to appear after being added.
--
2.51.0
G
G
Giacomo Leidi wrote on 23 Aug 10:46 -0700
[PATCH v15 1/5] tests: oci-container: Set explicit timeouts.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
5ee28e6ebd5050f3e7711f710e6bab579ae6824d.1755971216.git.goodoldpaul@autistici.org
* gnu/tests/docker.scm: Simplify %test-oci-container test case and add
explicit timeouts to tests outcomes.

Change-Id: I75868479dcce031f3edc98064b1ebcf975f598f5
---
gnu/tests/docker.scm | 99 ++++++++++++++++++--------------------------
1 file changed, 41 insertions(+), 58 deletions(-)

Toggle diff (129 lines)
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 8952daab2f..9fee3905f0 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -1,7 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 Danny Milosavljevic <dannym@scratchpost.org>
;;; Copyright © 2019-2023 Ludovic Courtès <ludo@gnu.org>
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -414,71 +414,54 @@ (define (run-oci-container-test)
(test-runner-current (system-test-runner #$output))
(test-begin "oci-container")
- (test-assert "containerd service running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'containerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (test-assert "containerd PID file present"
- (wait-for-file "/run/containerd/containerd.pid" marionette))
-
- (test-assert "dockerd running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'dockerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (sleep 10) ; let service start
+ (wait-for-file "/run/containerd/containerd.pid" marionette)
(test-assert "docker-guile running"
(marionette-eval
'(begin
(use-modules (gnu services herd))
- (match (start-service 'docker-guile)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
+ (wait-for-service 'docker-guile #:timeout 120)
+ #t)
marionette))
- (test-equal "passing host environment variables and volumes"
- '("value" "hello")
- (marionette-eval
- `(begin
- (use-modules (ice-9 popen)
- (ice-9 rdelim))
-
- (define slurp
- (lambda args
- (let* ((port (apply open-pipe* OPEN_READ args))
- (output (let ((line (read-line port)))
- (if (eof-object? line)
- ""
- line)))
- (status (close-pipe port)))
- output)))
- (let* ((response1 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
- (response2 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
+ (test-assert "passing host environment variables and volumes"
+ (begin
+ (define (run-test)
+ (marionette-eval
+ `(begin
+ (use-modules (ice-9 popen)
+ (ice-9 rdelim))
+
+ (define slurp
+ (lambda args
+ (let* ((port (apply open-pipe* OPEN_READ args))
+ (output (let ((line (read-line port)))
+ (if (eof-object? line)
+ ""
+ line)))
+ (status (close-pipe port)))
+ output)))
+ (let* ((response1 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
+ (response2 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
(display (call-with-input-file \"/shared.txt\" read-line)))")))
- (list response1 response2)))
- marionette))
+ (list response1 response2)))
+ marionette))
+ ;; Allow services to come up on slower machines
+ (let loop ((attempts 0))
+ (if (= attempts 60)
+ (error "Service didn't come up after more than 60 seconds")
+ (if (equal? '("value" "hello")
+ (run-test))
+ #t
+ (begin
+ (sleep 1)
+ (loop (+ 1 attempts))))))))
(test-end))))

base-commit: 73f943fd9f4cc69bd0463e6c64abeb65e060e1da
--
2.51.0
G
G
Giacomo Leidi wrote on 23 Aug 10:46 -0700
[PATCH v15 3/5] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
8376b06f17d6c66c1fd4913c86a583b38809f462.1755971216.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/build/oci-containers.scm: New file containg OCI runtime business
logic used in OCI backed Shepherd services.
oci-read-lines
(oci-system*,oci-object-exists?,oci-object-service-available?
oci-image-load,oci-log-verbose,oci-container-execlp,oci-object-create):
New procedures.
* gnu/local.mk: Add it.
* gnu/services/containers.scm (list-of-oci-containers?,
list-of-oci-networks?,list-of-oci-volumes?,%oci-supported-runtimes,
oci-runtime?,oci-runtime-system-environment,oci-runtime-system-extra-arguments,
oci-runtime-system-requirement,oci-runtime-cli,oci-runtime-system-cli,
oci-runtime-home-cli,oci-runtime-name,oci-runtime-group,
oci-container-shepherd-name,oci-networks-shepherd-name,
oci-networks-home-shepherd-name,oci-volumes-shepherd-name,
oci-volumes-home-shepherd-name,oci-container-configuration->options,
oci-network-configuration->options,oci-volume-configuration->options,
oci-container-shepherd-service,oci-objects-merge-lst,oci-extension-merge,
oci-service-accounts,oci-service-profile,oci-service-subids,
oci-configuration->shepherd-services,oci-configuration-extend): New
procedures.
(image-reference): Implement unambiguous naming convention, that paired
with the new implementation for listing caches images with docker ls or
podman ls, allows for more efficient image caching.
(oci-container-configuration)[user,group]: Change default-type to
maybe-string, since by default containers will run under the user and
group declared in oci-configuration records. When unset the
oci-service-type will derive their value from the OCI runtime state.
[runtime,host-environment,environment,shepherd-actions,ports,extra-arguments]:
define a predicate and use it as a type in the configuration. This
way errors are reported with source location information.
(lower-manifest): Defer to caller the logic of setting up an image tag.
(lower-oci-image): Rename to load-oci-image-state.
(oci-runtime-state): Intermediate representation of the OCI runtime
details. It is supposed to be an internal API.
(oci-state): Intermediate representation of the OCI provisioning state,
such as containers and networks. It is supposed to be an internal API.
(oci-container-invocation): Intermediate representation of the OCI
runtime run command to start a container. It is supposed to be an
internal API.
(%oci-image-loader): Rename to oci-image-loader and use
oci-runtime-state and (gnu build oci-containers).
(oci-container-shepherd-service): Use oci-state and oci-runtime-state,
add command-line action.
(oci-network-configuration,oci-volume-configuration,oci-configuration,
oci-extension): New record types.
(oci-service-type): New service-type.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 306 ++++++--
gnu/build/oci-containers.scm | 210 ++++++
gnu/local.mk | 1 +
gnu/services/containers.scm | 1363 +++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 38 +-
gnu/tests/containers.scm | 561 +++++++++++++-
6 files changed, 2206 insertions(+), 273 deletions(-)
create mode 100644 gnu/build/oci-containers.scm

Toggle diff (392 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index e2d546be0a..cbbbdae9a4 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -44746,59 +44746,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@samp{docker run} or @samp{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources, it can be either a package or a string representing
+an absolute file name to the runtime binary entrypoint. When unset it will default
+to @code{docker-cli} package for the @code{'docker} runtime or to @code{podman}
+package for the @code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -44818,16 +44921,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @samp{docker run}
+or @samp{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -44835,22 +44938,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -44878,7 +44983,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -44889,10 +44994,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -44903,25 +45009,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker run} or @samp{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker network create} or @samp{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker volume create} or @samp{podman volume create}
+invokation.
@end table
dif
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 23 Aug 10:46 -0700
[PATCH v15 4/5] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
865430d431a35320bd34b15d1f064d56970b8fef.1755971216.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/tests/containers.scm | 84 +++++++++++++++----------------
gnu/tests/docker.scm | 104 ++++++++++++++++++++++-----------------
2 files changed, 101 insertions(+), 87 deletions(-)

Toggle diff (283 lines)
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 1cf215b06c..f16697e4eb 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -46,6 +46,9 @@ (define-module (gnu tests containers)
%test-oci-service-rootless-podman
%test-oci-service-docker))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %rootless-podman-os
(simple-operating-system
@@ -69,13 +72,48 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +292,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +345,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-network
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 9fee3905f0..4fc50a99a7 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -48,6 +49,9 @@ (define-module (gnu tests docker)
%test-docker-system
%test-oci-container))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %docker-os
(simple-operating-system
(service dhcpcd-service-type)
@@ -57,6 +61,41 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +212,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +221,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +353,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.51.0
G
G
Giacomo Leidi wrote on 23 Aug 10:46 -0700
[PATCH v15 5/5] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
dd8bdfb0e32a027996c6a424bf3fa513dce9b058.1755971216.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 49 +++++++++++++
gnu/local.mk | 1 +
3 files changed, 164 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (194 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index cbbbdae9a4..0c7f0729cb 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -53001,6 +53001,120 @@ Miscellaneous Home Services
documentation of the system service (@pxref{Miscellaneous Services,
@code{readymedia-service-type}}).
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 0000000000..1ccdb3b246
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,49 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type
+ (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli))))
+ (service-extension home-shepherd-service-type
+ oci-configuration->shepherd-services)))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 8a7c23e8b6..4c77f5e7e1 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -105,6 +105,7 @@ GNU_SYSTEM_MODULES = \
%D%/home/services.scm \
%D%/home/services/admin.scm \
%D%/home/services/backup.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.51.0
M
M
Maxim Cournoyer wrote on 23 Aug 22:27 -0700
Re: [bug#76081] [PATCH v15 2/5] gnu: Move with-retries outside dbus-service.
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)(address . 76081@debbugs.gnu.org)
87wm6tl266.fsf@guixotic.coop
Hi Giacomo,

Giacomo Leidi <goodoldpaul@autistici.org> writes:

Toggle quote (4 lines)
> This patch moves with-retries outside of (gnu build dbus-service) into a
> more general (gnu build utils) which can be imported without
> unnecessarily importing dbus related symbols.

Ah, I meant moving it outside of the test gexps, and ungexping the
definition(s) in place, something like

Toggle snippet (6 lines)
(define common-defs
#~(begin
(define (with-retries ...)
...)))

And then inside the test gexp:

Toggle snippet (5 lines)
#~(begin
#$common-defs)
(with-retries ...)

Moving it to (gnu build utils) is problematic as it causes a world
rebuild, and I'm not confident the API of with-retries is useful enough
to be placed there (maybe?).

Sorry for the back and forth. If you can adjust that last bit I think
we'd be good to go.

--
Thanks,
Maxim
G
G
Giacomo Leidi wrote on 24 Aug 07:51 -0700
(name . Maxim Cournoyer)(address . maxim@guixotic.coop)(address . 76081@debbugs.gnu.org)
d0d66495-dd73-40b4-9951-20fbf874d40b@autistici.org
Hi I dropped the patch and I'll send it to the core-team branch (or the
current equivalent of what used to be the core-updates branch for world
rebuild changes, I'm a little lost on the current convention) so that
this can be discussed separately. I think the API being used in so many
different places (unrelated to dbus) makes the point of it being enough
general but I'm definitely open to feedback or opinions. I'm sending a
rev16 which hopefully is the last one.


Thank you for your feedback and help :)

cheers

giacomo
G
G
Giacomo Leidi wrote on 24 Aug 07:53 -0700
(name . Maxim Cournoyer)(address . maxim@guixotic.coop)(address . 76081@debbugs.gnu.org)
9c521ab1-3cab-4e20-b5cd-d3a664a26160@autistici.org
Hi I dropped the patch and I'll send it to the core-team branch (or the
current equivalent of what used to be the core-updates branch for world
rebuild changes, I'm a little lost on the current convention) so that
this can be discussed separately. I think the API being used in so many
different places (unrelated to dbus) makes the point of it being enough
general but I'm definitely open to feedback or opinions. I'm sending a
rev16 which hopefully is the last one.


Thank you for your feedback and help :)

cheers

giacomo
G
G
Giacomo Leidi wrote on 24 Aug 07:59 -0700
[PATCH v16 1/4] tests: oci-container: Set explicit timeouts.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
7b65fe396add9036e5f8b18406c366b6c5137a69.1756047587.git.goodoldpaul@autistici.org
* gnu/tests/docker.scm: Simplify %test-oci-container test case and add
explicit timeouts to tests outcomes.

Change-Id: I75868479dcce031f3edc98064b1ebcf975f598f5
---
gnu/tests/docker.scm | 99 ++++++++++++++++++--------------------------
1 file changed, 41 insertions(+), 58 deletions(-)

Toggle diff (129 lines)
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 8952daab2f..9fee3905f0 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -1,7 +1,7 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 Danny Milosavljevic <dannym@scratchpost.org>
;;; Copyright © 2019-2023 Ludovic Courtès <ludo@gnu.org>
-;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;; Copyright © 2024, 2025 Giacomo Leidi <goodoldpaul@autistici.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -414,71 +414,54 @@ (define (run-oci-container-test)
(test-runner-current (system-test-runner #$output))
(test-begin "oci-container")
- (test-assert "containerd service running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'containerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (test-assert "containerd PID file present"
- (wait-for-file "/run/containerd/containerd.pid" marionette))
-
- (test-assert "dockerd running"
- (marionette-eval
- '(begin
- (use-modules (gnu services herd))
- (match (start-service 'dockerd)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
- marionette))
-
- (sleep 10) ; let service start
+ (wait-for-file "/run/containerd/containerd.pid" marionette)
(test-assert "docker-guile running"
(marionette-eval
'(begin
(use-modules (gnu services herd))
- (match (start-service 'docker-guile)
- (#f #f)
- (('service response-parts ...)
- (match (assq-ref response-parts 'running)
- ((pid) pid)))))
+ (wait-for-service 'docker-guile #:timeout 120)
+ #t)
marionette))
- (test-equal "passing host environment variables and volumes"
- '("value" "hello")
- (marionette-eval
- `(begin
- (use-modules (ice-9 popen)
- (ice-9 rdelim))
-
- (define slurp
- (lambda args
- (let* ((port (apply open-pipe* OPEN_READ args))
- (output (let ((line (read-line port)))
- (if (eof-object? line)
- ""
- line)))
- (status (close-pipe port)))
- output)))
- (let* ((response1 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
- (response2 (slurp
- ,(string-append #$docker-cli "/bin/docker")
- "exec" "docker-guile"
- "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
+ (test-assert "passing host environment variables and volumes"
+ (begin
+ (define (run-test)
+ (marionette-eval
+ `(begin
+ (use-modules (ice-9 popen)
+ (ice-9 rdelim))
+
+ (define slurp
+ (lambda args
+ (let* ((port (apply open-pipe* OPEN_READ args))
+ (output (let ((line (read-line port)))
+ (if (eof-object? line)
+ ""
+ line)))
+ (status (close-pipe port)))
+ output)))
+ (let* ((response1 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(display (getenv \"VARIABLE\"))"))
+ (response2 (slurp
+ ,(string-append #$docker-cli "/bin/docker")
+ "exec" "docker-guile"
+ "/bin/guile" "-c" "(begin (use-modules (ice-9 popen) (ice-9 rdelim))
(display (call-with-input-file \"/shared.txt\" read-line)))")))
- (list response1 response2)))
- marionette))
+ (list response1 response2)))
+ marionette))
+ ;; Allow services to come up on slower machines
+ (let loop ((attempts 0))
+ (if (= attempts 60)
+ (error "Service didn't come up after more than 60 seconds")
+ (if (equal? '("value" "hello")
+ (run-test))
+ #t
+ (begin
+ (sleep 1)
+ (loop (+ 1 attempts))))))))
(test-end))))

base-commit: 8408e0e511157c6c875481b69137b2545ec82966
--
2.51.0
G
G
Giacomo Leidi wrote on 24 Aug 07:59 -0700
[PATCH v16 2/4] services: Add oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
220a5d612a4884290b11e2e04b629a70bfa45227.1756047587.git.goodoldpaul@autistici.org
This patch implements a generalization of the
oci-container-service-type, which consequently is made deprecated. The
oci-service-type, in addition to all the features from the
oci-container-service-type, can now provision OCI networks and volumes.
It only handles OCI objects creation, the user is supposed to handle
state once the objects are provsioned.

It currently supports two different OCI runtimes: Docker and rootless
Podman. Both runtimes are tested to make sure provisioned containers
can connect to each other through provisioned networks and can
read/write data with provisioned volumes.

At last the Scheme API is thought to facilitate the implementation of a
Guix Home service in the future.

* gnu/build/oci-containers.scm: New file containg OCI runtime business
logic used in OCI backed Shepherd services.
oci-read-lines
(oci-system*,oci-object-exists?,oci-object-service-available?
oci-image-load,oci-log-verbose,oci-container-execlp,oci-object-create):
New procedures.
* gnu/local.mk: Add it.
* gnu/services/containers.scm (list-of-oci-containers?,
list-of-oci-networks?,list-of-oci-volumes?,%oci-supported-runtimes,
oci-runtime?,oci-runtime-system-environment,oci-runtime-system-extra-arguments,
oci-runtime-system-requirement,oci-runtime-cli,oci-runtime-system-cli,
oci-runtime-home-cli,oci-runtime-name,oci-runtime-group,
oci-container-shepherd-name,oci-networks-shepherd-name,
oci-networks-home-shepherd-name,oci-volumes-shepherd-name,
oci-volumes-home-shepherd-name,oci-container-configuration->options,
oci-network-configuration->options,oci-volume-configuration->options,
oci-container-shepherd-service,oci-objects-merge-lst,oci-extension-merge,
oci-service-accounts,oci-service-profile,oci-service-subids,
oci-configuration->shepherd-services,oci-configuration-extend): New
procedures.
(image-reference): Implement unambiguous naming convention, that paired
with the new implementation for listing caches images with docker ls or
podman ls, allows for more efficient image caching.
(oci-container-configuration)[user,group]: Change default-type to
maybe-string, since by default containers will run under the user and
group declared in oci-configuration records. When unset the
oci-service-type will derive their value from the OCI runtime state.
[runtime,host-environment,environment,shepherd-actions,ports,extra-arguments]:
define a predicate and use it as a type in the configuration. This
way errors are reported with source location information.
(lower-manifest): Defer to caller the logic of setting up an image tag.
(lower-oci-image): Rename to load-oci-image-state.
(oci-runtime-state): Intermediate representation of the OCI runtime
details. It is supposed to be an internal API.
(oci-state): Intermediate representation of the OCI provisioning state,
such as containers and networks. It is supposed to be an internal API.
(oci-container-invocation): Intermediate representation of the OCI
runtime run command to start a container. It is supposed to be an
internal API.
(%oci-image-loader): Rename to oci-image-loader and use
oci-runtime-state and (gnu build oci-containers).
(oci-container-shepherd-service): Use oci-state and oci-runtime-state,
add command-line action.
(oci-network-configuration,oci-volume-configuration,oci-configuration,
oci-extension): New record types.
(oci-service-type): New service-type.
* doc/guix.texi: Document it.
* gnu/tests/containers.scm: Test it.
* gnu/services/docker.scm: Deprecate the oci-container-service-type.

Change-Id: I656b3db85832e42d53072fcbfb91d1226f39ef38
---
doc/guix.texi | 306 ++++++--
gnu/build/oci-containers.scm | 210 ++++++
gnu/local.mk | 1 +
gnu/services/containers.scm | 1363 +++++++++++++++++++++++++++++-----
gnu/services/docker.scm | 38 +-
gnu/tests/containers.scm | 561 +++++++++++++-
6 files changed, 2206 insertions(+), 273 deletions(-)
create mode 100644 gnu/build/oci-containers.scm

Toggle diff (392 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 037ed371d1..aead56fb7c 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -44746,59 +44746,162 @@ Miscellaneous Services
@cindex OCI-backed, Shepherd services
@subsubheading OCI backed services
-Should you wish to manage your Docker containers with the same consistent
-interface you use for your other Shepherd services,
-@var{oci-container-service-type} is the tool to use: given an
-@acronym{Open Container Initiative, OCI} container image, it will run it in a
+Should you wish to manage your @acronym{Open Container Initiative, OCI} containers
+with the same consistent interface you use for your other Shepherd services,
+@var{oci-service-type} is the tool to use: given an
+OCI container image, it will run it in a
Shepherd service. One example where this is useful: it lets you run services
-that are available as Docker/OCI images but not yet packaged for Guix.
+that are available as OCI images but not yet packaged for Guix.
-@defvar oci-container-service-type
+@defvar oci-service-type
-This is a thin wrapper around Docker's CLI that executes OCI images backed
+This is a thin wrapper around Docker's or Podman's CLI that executes OCI images backed
processes as Shepherd Services.
@lisp
-(service oci-container-service-type
- (list
- (oci-container-configuration
- (network "host")
- (image
- (oci-image
- (repository "guile")
- (tag "3")
- (value (specifications->manifest '("guile")))
- (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
- #:max-layers 2))))
- (entrypoint "/bin/guile")
- (command
- '("-c" "(display \"hello!\n\")")))
- (oci-container-configuration
- (image "prom/prometheus")
- (ports
- '(("9000" . "9000")
- ("9090" . "9090"))))
- (oci-container-configuration
- (image "grafana/grafana:10.0.1")
- (network "host")
- (volumes
- '("/var/lib/grafana:/var/lib/grafana")))))
+(simple-service 'oci-provisioning
+ oci-service-type
+ (oci-extension
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "host")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090"))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "host")
+ (volumes
+ '("/var/lib/grafana:/var/lib/grafana")))))))
@end lisp
In this example three different Shepherd services are going to be added to the
system. Each @code{oci-container-configuration} record translates to a
-@code{docker run} invocation and its fields directly map to options. You can
-refer to the
-@url{https://docs.docker.com/engine/reference/commandline/run,upstream}
-documentation for the semantics of each value. If the images are not found,
-they will be
-@url{https://docs.docker.com/engine/reference/commandline/pull/,pulled}. The
+@samp{docker run} or @samp{podman run} invocation and its fields directly
+map to options. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html,Podman}
+upstream documentation for semantics of each value. If the images are not found,
+they will be pulled. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/pull/,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-pull.1.html,Podman}
+upstream documentation for semantics. The
services with @code{(network "host")} are going to be attached to the
host network and are supposed to behave like native processes with regard to
networking.
@end defvar
+@c %start of fragment
+
+@deftp {Data Type} oci-configuration
+Available @code{oci-configuration} fields are:
+
+@table @asis
+@item @code{runtime} (default: @code{'docker}) (type: symbol)
+The OCI runtime to use to run commands. It can be either @code{'docker} or
+@code{'podman}.
+
+@item @code{runtime-cli} (type: maybe-package-or-string)
+The OCI runtime command line to be installed in the system profile and used
+to provision OCI resources, it can be either a package or a string representing
+an absolute file name to the runtime binary entrypoint. When unset it will default
+to @code{docker-cli} package for the @code{'docker} runtime or to @code{podman}
+package for the @code{'podman} runtime.
+
+@item @code{runtime-extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be placed
+after each @command{docker} or @command{podman} invokation.
+
+@item @code{user} (type: maybe-string)
+The user name under whose authority OCI commands will be run. This field will
+override the @code{user} field of @code{oci-configuration}.
+
+@item @code{group} (type: maybe-string)
+The group name under whose authority OCI commands will be run. When
+using the @code{'podman} OCI runtime, this field will be ignored and the
+default group of the user configured in the @code{user} field will be used.
+This field will override the @code{group} field of @code{oci-configuration}.
+
+@item @code{subuids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subuids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{subgids-range} (type: maybe-subid-range)
+An optional @code{subid-range} record allocating subgids for the user from
+the @code{user} field. When unset, with the rootless Podman OCI runtime, it
+defaults to @code{(subid-range (name "oci-container"))}.
+
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision. The use of the @code{oci-extension} record should
+be preferred for most cases.
+
+@item @code{verbose?} (default: @code{#f}) (type: boolean)
+When true, additional output will be printed, allowing to better follow the
+flow of execution.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-extension
+Available @code{oci-extension} fields are:
+
+@table @asis
+@item @code{containers} (default: @code{'()}) (type: list-of-oci-containers)
+The list of @code{oci-container-configuration} records representing the
+containers to provision.
+
+@item @code{networks} (default: @code{'()}) (type: list-of-oci-networks)
+The list of @code{oci-network-configuration} records representing the
+containers to provision.
+
+@item @code{volumes} (default: @code{'()}) (type: list-of-oci-volumes)
+The list of @code{oci-volumes-configuration} records representing the
+containers to provision.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+
@c %start of fragment
@deftp {Data Type} oci-container-configuration
@@ -44818,16 +44921,16 @@ Miscellaneous Services
Overwrite the default entrypoint (@code{ENTRYPOINT}) of the image.
@item @code{host-environment} (default: @code{'()}) (type: list)
-Set environment variables in the host environment where @command{docker
-run} is invoked. This is especially useful to pass secrets from the
-host to the container without having them on the @command{docker run}'s
-command line: by setting the @code{MYSQL_PASSWORD} on the host and by passing
+Set environment variables in the host environment where @samp{docker run}
+or @samp{podman run} are invoked. This is especially useful to pass secrets
+from the host to the container without having them on the OCI runtime command line,
+for example: by setting the @code{MYSQL_PASSWORD} on the host and by passing
@code{--env MYSQL_PASSWORD} through the @code{extra-arguments} field, it is
possible to securely set values in the container environment. This field's
value can be a list of pairs or strings, even mixed:
@lisp
-(list '("LANGUAGE\" . "eo:ca:eu")
+(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
@@ -44835,22 +44938,24 @@ Miscellaneous Services
directly to @code{make-forkexec-constructor}.
@item @code{environment} (default: @code{'()}) (type: list)
-Set environment variables. This can be a list of pairs or strings, even mixed:
+Set environment variables inside the container. This can be a list of pairs
+or strings, even mixed:
@lisp
(list '("LANGUAGE" . "eo:ca:eu")
"JAVA_HOME=/opt/java")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#env,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#env,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#env-e-env,Podman}
+upstream documentation for semantics.
@item @code{image} (type: string-or-oci-image)
The image used to build the container. It can be a string or an
-@code{oci-image} record. Strings are resolved by the Docker Engine, and
-follow the usual format
+@code{oci-image} record. Strings are resolved by the OCI runtime,
+and follow the usual format
@code{myregistry.local:5000/testing/test-image:tag}.
@item @code{provision} (default: @code{""}) (type: string)
@@ -44878,7 +44983,7 @@ Miscellaneous Services
by the service.
@item @code{network} (default: @code{""}) (type: string)
-Set a Docker network for the spawned container.
+Set an OCI network for the spawned container.
@item @code{ports} (default: @code{'()}) (type: list)
Set the port or port ranges to expose from the spawned container. This can be a
@@ -44889,10 +44994,11 @@ Miscellaneous Services
"10443:443")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#publish,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#publish,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#publish-p-ip-hostport-containerport-protocol,Podman}
+upstream documentation for semantics.
@item @code{volumes} (default: @code{'()}) (type: list)
Set volume mappings for the spawned container. This can be a
@@ -44903,25 +45009,97 @@ Miscellaneous Services
"/gnu/store:/gnu/store")
@end lisp
-Pair members can be strings, gexps or file-like objects.
-Strings are passed directly to the Docker CLI. You can refer to the
-@uref{https://docs.docker.com/engine/reference/commandline/run/#volume,upstream}
-documentation for semantics.
+Pair members can be strings, gexps or file-like objects. Strings are passed
+directly to the OCI runtime CLI. You can refer to the
+@url{https://docs.docker.com/engine/reference/commandline/run/#volume,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options,Podman}
+upstream documentation for semantics.
@item @code{container-user} (default: @code{""}) (type: string)
Set the current user inside the spawned container. You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#user,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#user,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#user-u-user-group,Podman}
+upstream documentation for semantics.
@item @code{workdir} (default: @code{""}) (type: string)
Set the current working directory for the spawned Shepherd service.
You can refer to the
-@url{https://docs.docker.com/engine/reference/run/#workdir,upstream}
-documentation for semantics.
+@url{https://docs.docker.com/engine/reference/run/#workdir,Docker}
+or @url{https://docs.podman.io/en/stable/markdown/podman-run.1.html#workdir-w-dir,Podman}
+upstream documentation for semantics.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker run} or @samp{podman run} invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-network-configuration
+Available @code{oci-network-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI network to provision.
+
+@item @code{driver} (type: maybe-string)
+The driver to manage the network.
+
+@item @code{gateway} (type: maybe-string)
+IPv4 or IPv6 gateway for the subnet.
+
+@item @code{internal?} (default: @code{#f}) (type: boolean)
+Restrict external access to the network
+
+@item @code{ip-range} (type: maybe-string)
+Allocate container ip from a sub-range in CIDR format.
+
+@item @code{ipam-driver} (type: maybe-string)
+IP Address Management Driver.
+
+@item @code{ipv6?} (default: @code{#f}) (type: boolean)
+Enable IPv6 networking.
+
+@item @code{subnet} (type: maybe-string)
+Subnet in CIDR format that represents a network segment.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
+
+@item @code{extra-arguments} (default: @code{'()}) (type: list)
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker network create} or @samp{podman network create}
+invokation.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
+
+@c %start of fragment
+
+@deftp {Data Type} oci-volume-configuration
+Available @code{oci-volume-configuration} fields are:
+
+@table @asis
+@item @code{name} (type: string)
+The name of the OCI volume to provision.
+
+@item @code{labels} (default: @code{'()}) (type: list)
+The list of labels that will be used to tag the current volume.
@item @code{extra-arguments} (default: @code{'()}) (type: list)
-A list of strings, gexps or file-like objects that will be directly
-passed to the @command{docker run} invocation.
+A list of strings, gexps or file-like objects that will be directly passed
+to the @samp{docker volume create} or @samp{podman volume create}
+invokation.
@end table
dif
This message was truncated. Download the full message here.
G
G
Giacomo Leidi wrote on 24 Aug 07:59 -0700
[PATCH v16 3/4] tests: Use lower-oci-image-state in container tests.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
a26aec8da36934f34e7d1e935c94dd8f4232dc84.1756047587.git.goodoldpaul@autistici.org
This patch replaces boilerplate in container related tests with
oci-image plumbing from (gnu services containers).

* gnu/tests/containers.scm (%oci-tarball): New variable;
(run-rootless-podman-test): use %oci-tarball;
(build-tarball&run-rootless-podman-test): drop procedure.
* gnu/tests/docker.scm (%docker-tarball): New variable;
(build-tarball&run-docker-test): use %docker-tarball;
(%docker-system-tarball): New variable;
(build-tarball&run-docker-system-test): new procedure.

Change-Id: Iad6f0704aee188d89464c83722dea0bb7adb084a
---
gnu/tests/containers.scm | 84 +++++++++++++++----------------
gnu/tests/docker.scm | 104 ++++++++++++++++++++++-----------------
2 files changed, 101 insertions(+), 87 deletions(-)

Toggle diff (283 lines)
diff --git a/gnu/tests/containers.scm b/gnu/tests/containers.scm
index 1a442cddc6..089303643c 100644
--- a/gnu/tests/containers.scm
+++ b/gnu/tests/containers.scm
@@ -46,6 +46,9 @@ (define-module (gnu tests containers)
%test-oci-service-rootless-podman
%test-oci-service-docker))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %rootless-podman-os
(simple-operating-system
@@ -69,13 +72,48 @@ (define %rootless-podman-os
(supplementary-groups '("wheel" "netdev" "cgroup"
"audio" "video")))))))
-(define (run-rootless-podman-test oci-tarball)
+(define %oci-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
+(define (run-rootless-podman-test)
(define os
(marionette-operating-system
(operating-system-with-gc-roots
%rootless-podman-os
- (list oci-tarball))
+ (list %oci-tarball))
#:imported-modules '((gnu services herd)
(guix combinators))))
@@ -254,7 +292,7 @@ (define (run-rootless-podman-test oci-tarball)
(let* ((loaded (slurp ,(string-append #$podman
"/bin/podman")
"load" "-i"
- ,#$oci-tarball))
+ ,#$%oci-tarball))
(repository&tag "localhost/guile-guest:latest")
(response1 (slurp
,(string-append #$podman "/bin/podman")
@@ -307,49 +345,11 @@ (define (run-rootless-podman-test oci-tarball)
(gexp->derivation "rootless-podman-test" test))
-(define (build-tarball&run-rootless-podman-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:extra-options
- '(#:image-tag "guile-guest")
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-rootless-podman-test tarball)))
-
(define %test-rootless-podman
(system-test
(name "rootless-podman")
(description "Test rootless Podman service.")
- (value (build-tarball&run-rootless-podman-test))))
+ (value (run-rootless-podman-test))))
(define %oci-network
diff --git a/gnu/tests/docker.scm b/gnu/tests/docker.scm
index 9fee3905f0..4fc50a99a7 100644
--- a/gnu/tests/docker.scm
+++ b/gnu/tests/docker.scm
@@ -26,6 +26,7 @@ (define-module (gnu tests docker)
#:use-module (gnu system image)
#:use-module (gnu system vm)
#:use-module (gnu services)
+ #:use-module (gnu services containers)
#:use-module (gnu services dbus)
#:use-module (gnu services networking)
#:use-module (gnu services docker)
@@ -48,6 +49,9 @@ (define-module (gnu tests docker)
%test-docker-system
%test-oci-container))
+(define lower-oci-image-state
+ (@@ (gnu services containers) lower-oci-image-state))
+
(define %docker-os
(simple-operating-system
(service dhcpcd-service-type)
@@ -57,6 +61,41 @@ (define %docker-os
(service containerd-service-type)
(service docker-service-type)))
+(define %docker-tarball
+ (lower-oci-image-state
+ "guile-guest"
+ (packages->manifest
+ (list
+ guile-3.0 guile-json-3
+ (package
+ (name "guest-script")
+ (version "0")
+ (source #f)
+ (build-system trivial-build-system)
+ (arguments
+ (list
+ #:guile guile-3.0
+ #:builder
+ #~(let ((out #$output))
+ (mkdir out)
+ (call-with-output-file (string-append out "/a.scm")
+ (lambda (port)
+ (display "(display \"hello world\n\")" port))))))
+ (synopsis "Display hello world using Guile")
+ (description "This package displays the text \"hello world\" on the
+standard output device and then enters a new line.")
+ (home-page #f)
+ (license license:public-domain))))
+ '(#:entry-point "bin/guile"
+ #:localstatedir? #t
+ #:extra-options (#:image-tag "guile-guest")
+ #:symlinks (("/bin/Guile" -> "bin/guile")
+ ("aa.scm" -> "a.scm")))
+ "guile-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-test docker-tarball)
"Load DOCKER-TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
@@ -173,40 +212,7 @@ (define (run-docker-test docker-tarball)
(gexp->derivation "docker-test" test))
(define (build-tarball&run-docker-test)
- (mlet* %store-monad
- ((_ (set-grafting #f))
- (guile (set-guile-for-build (default-guile)))
- (guest-script-package ->
- (package
- (name "guest-script")
- (version "0")
- (source #f)
- (build-system trivial-build-system)
- (arguments `(#:guile ,guile-3.0
- #:builder
- (let ((out (assoc-ref %outputs "out")))
- (mkdir out)
- (call-with-output-file (string-append out "/a.scm")
- (lambda (port)
- (display "(display \"hello world\n\")" port)))
- #t)))
- (synopsis "Display hello world using Guile")
- (description "This package displays the text \"hello world\" on the
-standard output device and then enters a new line.")
- (home-page #f)
- (license license:public-domain)))
- (profile (profile-derivation (packages->manifest
- (list guile-3.0 guile-json-3
- guest-script-package))
- #:hooks '()
- #:locales? #f))
- (tarball (pack:docker-image
- "docker-pack" profile
- #:symlinks '(("/bin/Guile" -> "bin/guile")
- ("aa.scm" -> "a.scm"))
- #:entry-point "bin/guile"
- #:localstatedir? #t)))
- (run-docker-test tarball)))
+ (run-docker-test %docker-tarball))
(define %test-docker
(system-test
@@ -215,8 +221,22 @@ (define %test-docker
(value (build-tarball&run-docker-test))))
+(define %docker-system-tarball
+ (lower-oci-image-state
+ "guix-system-guest"
+ (operating-system
+ (inherit (simple-operating-system))
+ ;; Use locales for a single libc to
+ ;; reduce space requirements.
+ (locale-libcs (list glibc)))
+ '()
+ "guix-system-guest"
+ (%current-target-system)
+ (%current-system)
+ #f))
+
(define (run-docker-system-test tarball)
- "Load DOCKER-TARBALL as Docker image and run it in a Docker container,
+ "Load TARBALL as Docker image and run it in a Docker container,
inside %DOCKER-OS."
(define os
(marionette-operating-system
@@ -333,21 +353,15 @@ (define (run-docker-system-test tarball)
(gexp->derivation "docker-system-test" test))
+(define (build-tarball&run-docker-system-test)
+ (run-docker-system-test %docker-system-tarball))
+
(define %test-docker-system
(system-test
(name "docker-system")
(description "Run a system image as produced by @command{guix system
docker-image} inside Docker.")
- (value (with-monad %store-monad
- (>>= (lower-object
- (system-image (os->image
- (operating-system
- (inherit (simple-operating-system))
- ;; Use locales for a single libc to
- ;; reduce space requirements.
- (locale-libcs (list glibc)))
- #:type docker-image-type)))
- run-docker-system-test)))))
+ (value (build-tarball&run-docker-system-test))))
(define %oci-os
--
2.51.0
G
G
Giacomo Leidi wrote on 24 Aug 07:59 -0700
[PATCH v16 4/4] home: Add home-oci-service-type.
(address . 76081@debbugs.gnu.org)(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
dded59392afd6e851d2d3a7ce094ead89da0d012.1756047587.git.goodoldpaul@autistici.org
* gnu/home/service/containers.scm: New file;
* gnu/local.mk (GNU_SYSTEM_MODULES): Add it.
* doc/guix.texi (OCI backed services): Document it.

Change-Id: I8ce5b301e8032d0a7b2a9ca46752738cdee1f030
---
doc/guix.texi | 114 +++++++++++++++++++++++++++++++
gnu/home/services/containers.scm | 49 +++++++++++++
gnu/local.mk | 1 +
3 files changed, 164 insertions(+)
create mode 100644 gnu/home/services/containers.scm

Toggle diff (194 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index aead56fb7c..f3f3fe2129 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -53002,6 +53002,120 @@ Miscellaneous Home Services
documentation of the system service (@pxref{Miscellaneous Services,
@code{readymedia-service-type}}).
+@subsubheading OCI backed services
+
+@cindex OCI-backed, for Home
+The @code{(gnu home services containers)} module provides the following service:
+
+@defvar home-oci-service-type
+This is the type of the service that allows to manage your OCI containers with
+the same consistent interface you use for your other Home Shepherd services.
+@end defvar
+
+This service is a direct mapping of the @code{oci-service-type} system
+service (@pxref{Miscellaneous Services, OCI backed services}). You can
+use it like this:
+
+@lisp
+(use-modules (gnu services containers)
+ (gnu home services containers))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+
+@end lisp
+
+You may specify a custom configuration by providing a
+@code{oci-configuration} record, exactly like for
+@code{oci-service-type}, but wrapping it in @code{for-home}:
+
+@lisp
+(use-modules (gnu services)
+ (gnu services containers)
+ (gnu home services containers))
+
+(service home-oci-service-type
+ (for-home
+ (oci-configuration
+ (runtime 'podman)
+ (verbose? #t))))
+
+(simple-service 'home-oci-provisioning
+ home-oci-service-type
+ (oci-extension
+ (volumes
+ (list
+ (oci-volume-configuration (name "prometheus"))
+ (oci-volume-configuration (name "grafana"))))
+ (networks
+ (list
+ (oci-network-configuration (name "monitoring"))))
+ (containers
+ (list
+ (oci-container-configuration
+ (network "monitoring")
+ (image
+ (oci-image
+ (repository "guile")
+ (tag "3")
+ (value (specifications->manifest '("guile")))
+ (pack-options '(#:symlinks (("/bin/guile" -> "bin/guile"))
+ #:max-layers 2))))
+ (entrypoint "/bin/guile")
+ (command
+ '("-c" "(display \"hello!\n\")")))
+ (oci-container-configuration
+ (image "prom/prometheus")
+ (network "monitoring")
+ (ports
+ '(("9000" . "9000")
+ ("9090" . "9090")))
+ (volumes
+ (list
+ '(("prometheus" . "/var/lib/prometheus")))))
+ (oci-container-configuration
+ (image "grafana/grafana:10.0.1")
+ (network "monitoring")
+ (volumes
+ '(("grafana:/var/lib/grafana"))))))))
+@end lisp
+
@node Invoking guix home
@section Invoking @command{guix home}
diff --git a/gnu/home/services/containers.scm b/gnu/home/services/containers.scm
new file mode 100644
index 0000000000..1ccdb3b246
--- /dev/null
+++ b/gnu/home/services/containers.scm
@@ -0,0 +1,49 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services containers)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu services)
+ #:use-module (gnu services configuration)
+ #:use-module (gnu services containers)
+ #:use-module (guix gexp)
+ #:use-module (guix packages)
+ #:use-module (srfi srfi-1)
+ #:export (home-oci-service-type))
+
+(define home-oci-service-type
+ (service-type
+ (inherit (system->home-service-type oci-service-type))
+ (extensions
+ (list
+ (service-extension home-profile-service-type
+ (lambda (config)
+ (let ((runtime-cli
+ (oci-configuration-runtime-cli config))
+ (runtime
+ (oci-configuration-runtime config)))
+ (oci-service-profile runtime runtime-cli))))
+ (service-extension home-shepherd-service-type
+ oci-configuration->shepherd-services)))
+ (extend
+ (lambda (config extension)
+ (for-home
+ (oci-configuration
+ (inherit (oci-configuration-extend config extension))))))
+ (default-value (for-home (oci-configuration)))))
diff --git a/gnu/local.mk b/gnu/local.mk
index 74a85cad36..7f05f1b8de 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -105,6 +105,7 @@ GNU_SYSTEM_MODULES = \
%D%/home/services.scm \
%D%/home/services/admin.scm \
%D%/home/services/backup.scm \
+ %D%/home/services/containers.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
%D%/home/services/dotfiles.scm \
--
2.51.0
M
M
Maxim Cournoyer wrote on 24 Aug 16:25 -0700
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)(address . 76081@debbugs.gnu.org)
87jz2sl2tw.fsf@guixotic.coop
Hi Giacomo,

Not sure if anything can be done with your provider, but I'm still
receiving all your email in the Gmail SPAM, flagged because:

Toggle quote (4 lines)
> This message isn't authenticated and the sender can't be verified. Use
> caution when clicking links, downloading attachments, or replying with
> personal information.

--
Thanks,
Maxim
M
M
Maxim Cournoyer wrote on 24 Aug 21:07 -0700
(name . Giacomo Leidi)(address . goodoldpaul@autistici.org)
87v7mcjb89.fsf@guixotic.coop
Hi,

Merged at least, with commit 57386498b61.

Thanks for your patience.

--
Thanks,
Maxim
Closed
L
L
Ludovic Courtès wrote on 25 Aug 01:33 -0700
(name . Maxim Cournoyer)(address . maxim.cournoyer@gmail.com)
871pozn6ma.fsf@gnu.org
Hello,

Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

Toggle quote (2 lines)
> Merged at least, with commit 57386498b61.

It seems that this (probably cc07ecd7ccc52540113414eaebafc0fb218ef9ff)
broke ‘guix pull’:


Presumably something is opening a connection to the store from the
top-level.

(Commit cc07ecd7ccc52540113414eaebafc0fb218ef9ff also uses @@, which
should really be avoided, because it breaks encapsulation and is not
even guaranteed to work, due to inlining.)

Ludo’.
Closed
M
M
Maxim Cournoyer wrote on 25 Aug 17:34 -0700
(name . Ludovic Courtès)(address . ludo@gnu.org)
874itukjin.fsf@guixotic.coop
Hi,

Ludovic Courtès <ludo@gnu.org> writes:

Toggle quote (15 lines)
> Hello,
>
> Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:
>
>> Merged at least, with commit 57386498b61.
>
> It seems that this (probably cc07ecd7ccc52540113414eaebafc0fb218ef9ff)
> broke ‘guix pull’:
>
> https://mail.gnu.org/archive/html/help-guix/2025-08/msg00127.html
> https://ci.guix.gnu.org/eval/2081166
>
> Presumably something is opening a connection to the store from the
> top-level.

Thanks for sharing this idea. That seems likely as for example
%oci-tarball is defined at the top level in terms of
lower-oci-image-state, which calls run-with-store.

Toggle quote (4 lines)
> (Commit cc07ecd7ccc52540113414eaebafc0fb218ef9ff also uses @@, which
> should really be avoided, because it breaks encapsulation and is not
> even guaranteed to work, due to inlining.)

We use this trick in a few tests for "white box testing" (reaching to
internals that shouldn't be part of the public API). Sometimes with the
(set! some-symbol some-symbol) trick to avoid inlining in the source
module. Maybe that was the idea here?

--
Thanks,
Maxim
Closed
?
Your comment

This issue is archived.

To comment on this conversation send an email to 76081@patchwise.org

To respond to this issue using the mumi CLI, first switch to it
mumi current 76081
Then, you may apply the latest patchset in this issue (with sign off)
mumi am -- -s
Or, compose a reply to this issue
mumi compose
Or, send patches to this issue
mumi send-email *.patch