summaryrefslogtreecommitdiff
path: root/gnu/build
diff options
context:
space:
mode:
authorGiacomo Leidi <goodoldpaul@autistici.org>2025-08-24 16:59:45 +0200
committerMaxim Cournoyer <maxim@guixotic.coop>2025-08-25 13:04:36 +0900
commit60f4d72590abf11885ea3e2ec2a7c277683417aa (patch)
treea224ee352a609ac50d59735cb666600cda42a960 /gnu/build
parentd6200cefcc18e55df07fc0e5dabbb4f5f7de7bb4 (diff)
services: Add oci-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. 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 Modified-by: Maxim Cournoyer <maxim@guixotic.coop> Signed-off-by: Maxim Cournoyer <maxim@guixotic.coop>
Diffstat (limited to 'gnu/build')
-rw-r--r--gnu/build/oci-containers.scm210
1 files changed, 210 insertions, 0 deletions
diff --git a/gnu/build/oci-containers.scm b/gnu/build/oci-containers.scm
new file mode 100644
index 00000000000..38704e9e4a4
--- /dev/null
+++ b/gnu/build/oci-containers.scm
@@ -0,0 +1,210 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2025 Giacomo Leidi <goodoldpaul@autistici.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19;;; Commentary:
20;;;
21;;; This module contains helpers used as part of the oci-service-type
22;;; definition.
23;;;
24;;; Code:
25
26(define-module (gnu build oci-containers)
27 #:use-module (ice-9 format)
28 #:use-module (ice-9 match)
29 #:use-module (ice-9 popen)
30 #:use-module (ice-9 rdelim)
31 #:use-module (ice-9 textual-ports)
32 #:use-module (srfi srfi-1)
33 #:export (oci-read-lines
34 oci-system*
35 oci-object-exists?
36 oci-object-service-available?
37 oci-image-load
38 oci-log-verbose
39 oci-container-execlp
40 oci-object-create))
41
42(define* (oci-read-lines invocation #:key verbose?)
43 (define (get-lines port)
44 (let ((lines-string (get-string-all port)))
45 (string-split lines-string #\newline)))
46
47 (define command
48 (string-join invocation " "))
49
50 (when verbose? (format #t "Running ~a~%" command))
51
52 (with-input-from-port (open-input-pipe command)
53 (lambda _
54 (get-lines (current-input-port)))))
55
56(define* (oci-log-verbose invocation)
57 (format #t "Running in verbose mode...
58Current user: ~a ~a
59Current group: ~a ~a
60Current directory: ~a~%"
61 (getuid) (passwd:name (getpwuid (getuid)))
62 (getgid) (group:name (getgrgid (getgid)))
63 (getcwd))
64
65 (format #t "Running~{ ~a~}~%" invocation))
66
67(define* (oci-system* invocation #:key verbose?)
68 (when verbose?
69 (format #t "Running~{ ~a~}~%" invocation))
70
71 (let* ((status (apply system* invocation))
72 (exit-code (status:exit-val status)))
73 (when verbose?
74 (format #t "Exit code: ~a~%" exit-code))
75 status))
76
77(define* (oci-object-member name objects
78 #:key verbose?)
79
80 (define member? (member name objects))
81
82 (when (and verbose? (> (length objects) 0))
83 (format #t "~a is ~apart of:~{ ~a~}~%"
84 name
85 (if member? "" "not ")
86 objects))
87 member?)
88
89(define* (oci-object-list runtime-cli object
90 #:key verbose?
91 (format-string "{{.Name}}"))
92
93 (define invocation
94 (list runtime-cli object "ls" "--format"
95 (string-append "\"" format-string "\"")))
96
97 (filter
98 (lambda (name)
99 (not (string=? (string-trim name) "")))
100 (oci-read-lines invocation #:verbose? verbose?)))
101
102(define* (docker-object-exist? runtime-cli object name
103 #:key verbose?
104 (format-string "{{.Name}}"))
105
106 (define objects
107 (oci-object-list runtime-cli object
108 #:verbose? verbose?
109 #:format-string format-string))
110
111 (oci-object-member name objects #:verbose? verbose?))
112
113(define* (podman-object-exist? runtime-cli object name #:key verbose?)
114 (let ((invocation (list runtime-cli object "exists" name)))
115 (define exit-code
116 (status:exit-val (oci-system* invocation #:verbose? verbose?)))
117 (equal? EXIT_SUCCESS exit-code)))
118
119(define* (oci-object-exists? runtime runtime-cli object name
120 #:key verbose?
121 (format-string "{{.Name}}"))
122 (if (eq? runtime 'podman)
123 (podman-object-exist? runtime-cli object name
124 #:verbose? verbose?)
125 (docker-object-exist? runtime-cli object name
126 #:verbose? verbose?
127 #:format-string format-string)))
128
129(define* (oci-object-service-available? runtime-cli object names
130 #:key verbose?
131 (format-string "{{.Name}}"))
132 "Whether NAMES are provisioned in the current OBJECT environment."
133 (define environment
134 (oci-object-list runtime-cli object
135 #:verbose? verbose?
136 #:format-string format-string))
137 (when verbose?
138 (format #t "~a environment:~{ ~a~}~%" object environment))
139
140 (define available?
141 (every
142 (lambda (name)
143 (oci-object-member name environment #:verbose? verbose?))
144 names))
145
146 (when verbose?
147 (format #t "~a service is~a available~%" object (if available? "" " not")))
148
149 available?)
150
151(define* (oci-image-load runtime runtime-cli tarball name tag
152 #:key verbose?
153 (format-string "{{.Repository}}:{{.Tag}}"))
154 (define load-invocation
155 (list runtime-cli "load" "-i" tarball))
156
157 (if (oci-object-exists? runtime runtime-cli "image" tag
158 #:verbose? verbose?
159 #:format-string format-string)
160 (format #t "~a image already exists, skipping.~%" tag)
161 (begin
162 (format #t "Loading image for ~a from ~a...~%" name tarball)
163
164 (let ((line (first
165 (oci-read-lines load-invocation #:verbose? verbose?))))
166 (unless (or (eof-object? line)
167 (string-null? line))
168
169 (format #t "~a~%" line)
170
171 (let* ((repository&tag
172 (string-drop line
173 (string-length
174 "Loaded image: ")))
175 (tag-invocation
176 (list runtime-cli "tag" repository&tag tag))
177 (drop-old-tag-invocation
178 (list runtime-cli "image" "rm" "-f" repository&tag)))
179
180 (unless (string=? repository&tag tag)
181 (let ((exit-code
182 (status:exit-val
183 (oci-system* tag-invocation #:verbose? verbose?))))
184 (format #t "Tagged ~a with ~a...~%" tarball tag)
185
186 (when (equal? EXIT_SUCCESS exit-code)
187 (oci-system* drop-old-tag-invocation #:verbose? verbose?))))))))))
188
189(define* (oci-container-execlp invocation #:key verbose? pre-script)
190 (when pre-script
191 (pre-script))
192 (when verbose?
193 (oci-log-verbose invocation))
194 (apply execlp (first invocation) invocation))
195
196(define* (oci-object-create runtime runtime-cli runtime-name
197 object
198 invocations
199 #:key verbose?
200 (format-string "{{.Name}}"))
201 (for-each
202 (lambda (invocation)
203 (define name (last invocation))
204 (if (oci-object-exists? runtime runtime-cli object name
205 #:format-string format-string
206 #:verbose? verbose?)
207 (format #t "~a ~a ~a already exists, skipping creation.~%"
208 runtime-name name object)
209 (oci-system* invocation #:verbose? verbose?)))
210 invocations))