[bug#75010,7/7] WIP: gnu: tests: Add module for guix deploy tests.
Commit Message
* gnu/tests/deploy.scm: Add file.
Change-Id: I348c8bf2e518ec6c00af126993eaca3fcd453901
---
gnu/tests/deploy.scm | 203 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 203 insertions(+)
create mode 100644 gnu/tests/deploy.scm
Comments
Herman Rimm <herman@rimm.ee> skribis:
> * gnu/tests/deploy.scm: Add file.
>
> Change-Id: I348c8bf2e518ec6c00af126993eaca3fcd453901
Yay, nice!
Could you add it to ‘gnu/local.mk’?
> +(define (machines os)
> + (program-file "machines.scm"
> + #~(list (machine (configuration
This should be ‘scheme-file’ (with normal indentation).
> +(define* (deploy-program #:optional (os #~%simple-os))
> + (program-file "deploy.scm"
> + (with-extensions (list guile-gcrypt)
> + (with-imported-modules `(,@(source-module-closure
> + '((guix scripts deploy))
> + #:select? not-config?)
> + ((guix config) => ,(make-config.scm)))
> + #~(begin
> + (use-modules (guix scripts deploy))
> + (guix-deploy #$(machines os)))))))
We could use the ‘guix’ package here: it would be faster, but then we
would be testing an older snapshot and not the code at hand. Not great.
Still, maybe using ‘current-guix’ would be faster (fewer things to
build), as in:
#~(execl #$(file-append (current-guix) "/bin/guix")
"guix" "deploy“ #$(machines os))
> + (define (system-generations marionette)
> + (marionette-eval
> + '(begin
> + (use-modules (ice-9 ftw)
> + (srfi srfi-1))
> + (let* ((profile-dir "/var/guix/profiles/")
> + (entries (map first (cddr (file-system-tree profile-dir)))))
Please use ‘match’ rather than ‘first’ and ‘cddr’ (info "(guix) Data
Types and Pattern Matching").
Or maybe you could just as well use ‘scandir’?
> + (test-equal "script created new generation"
> + (length (system-generations marionette))
> + (1+ (length generations-prior)))
> +
> + (test-equal "script activated the new generation"
> + (string-append "/var/guix/profiles/system-"
> + (number->string (+ 1 (length generations-prior)))
> + "-link")
> + (marionette-eval '(readlink "/run/current-system")
> + marionette)))
We could also check other things, like the host name.
Ludo’.
new file mode 100644
@@ -0,0 +1,203 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2019 Jakob L. Kreuze <zerodaysfordays@sdf.org>
+;;; Copyright © 2024 Herman Rimm <herman@rimm.ee>
+;;;
+;;; 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 tests deploy)
+ #:use-module (gnu packages gnupg)
+ #:use-module ((guix self) #:select (make-config.scm))
+ #:use-module (gnu services)
+ #:use-module (gnu services base)
+ #:use-module (gnu services ssh)
+ #:use-module (gnu system)
+ #:use-module (gnu system vm)
+ #:use-module (gnu tests)
+ #:use-module (guix gexp)
+ #:use-module (guix modules)
+ #:use-module (ice-9 match)
+ #:export (%test-deploy
+ %test-rollback))
+
+;;; Commentary:
+;;;
+;;; Test in-place system deployment: advancing the system generation on
+;;; a running instance of the Guix System.
+;;;
+;;; Code:
+
+(define (machines os)
+ (program-file "machines.scm"
+ #~(list (machine (configuration
+ (machine-ssh-configuration
+ (host-name "localhost")
+ (system (%current-system))))
+ (environment managed-host-environment-type)
+ (operating-system #$os)))))
+
+(define not-config?
+ ;; Select (guix …) and (gnu …) modules, except (guix config).
+ (match-lambda
+ (('guix 'config) #f)
+ (('guix rest ...) #t)
+ (('gnu rest ...) #t)
+ (_ #f)))
+
+(define* (deploy-program #:optional (os #~%simple-os))
+ (program-file "deploy.scm"
+ (with-extensions (list guile-gcrypt)
+ (with-imported-modules `(,@(source-module-closure
+ '((guix scripts deploy))
+ #:select? not-config?)
+ ((guix config) => ,(make-config.scm)))
+ #~(begin
+ (use-modules (guix scripts deploy))
+ (guix-deploy #$(machines os)))))))
+
+(define os
+ (marionette-operating-system
+ (simple-operating-system
+ (service openssh-service-type
+ (openssh-configuration
+ (permit-root-login #t)
+ (allow-empty-passwords? #t)))
+ (service static-networking-service-type
+ (list (static-networking
+ (inherit %loopback-static-networking)
+ (provision '(networking))))))
+ #:imported-modules '((gnu services herd)
+ (guix combinators))))
+
+(define vm (virtual-machine os))
+
+(define* (run-deploy-test)
+ "Run a test of an OS running DEPLOY-PROGRAM, which creates a new
+generation of the system profile."
+ (define (test script)
+ (with-imported-modules '((gnu build marionette))
+ #~(begin
+ (use-modules (gnu build marionette)
+ (srfi srfi-64))
+
+ (define marionette
+ (make-marionette (list #$vm)))
+
+ ;; Return the names of the generation symlinks on MARIONETTE.
+ (define (system-generations marionette)
+ (marionette-eval
+ '(begin
+ (use-modules (ice-9 ftw)
+ (srfi srfi-1))
+ (let* ((profile-dir "/var/guix/profiles/")
+ (entries (map first (cddr (file-system-tree profile-dir)))))
+ (remove (lambda (entry)
+ (member entry '("per-user" "system")))
+ entries)))
+ marionette))
+
+ (test-runner-current (system-test-runner #$output))
+ (test-begin "deploy")
+
+ (let ((generations-prior (system-generations marionette)))
+ (test-assert "script successfully evaluated"
+ (marionette-eval
+ '(primitive-load #$script)
+ marionette))
+
+ (test-equal "script created new generation"
+ (length (system-generations marionette))
+ (1+ (length generations-prior)))
+
+ (test-equal "script activated the new generation"
+ (string-append "/var/guix/profiles/system-"
+ (number->string (+ 1 (length generations-prior)))
+ "-link")
+ (marionette-eval '(readlink "/run/current-system")
+ marionette)))
+
+ (test-end))))
+
+ (gexp->derivation "deploy" (test (deploy-program))))
+
+(define* (run-rollback-test)
+ "Run a test of an OS with a faulty bootloader running DEPLOY-PROGRAM,
+which causes a rollback."
+ (define os
+ #~(operating-system
+ (inherit %simple-os)
+ (bootloader
+ (bootloader-configuration
+ (inherit (operating-system-bootloader
+ %simple-os))
+ (targets '("/dev/null"))))))
+
+ (define (test script)
+ (with-imported-modules '((gnu build marionette))
+ #~(begin
+ (use-modules (gnu build marionette)
+ (srfi srfi-64))
+
+ (define marionette
+ (make-marionette (list #$vm)))
+
+ ;; Return the names of the generation symlinks on MARIONETTE.
+ (define (system-generations marionette)
+ (marionette-eval
+ '(begin
+ (use-modules (ice-9 ftw)
+ (srfi srfi-1))
+ (let* ((profile-dir "/var/guix/profiles/")
+ (entries (map first (cddr (file-system-tree profile-dir)))))
+ (remove (lambda (entry)
+ (member entry '("per-user" "system")))
+ entries)))
+ marionette))
+
+ (test-runner-current (system-test-runner #$output))
+ (test-begin "rollback")
+
+ (let ((generations-prior (system-generations marionette)))
+ (test-assert "script successfully evaluated"
+ (marionette-eval
+ '(primitive-load #$script)
+ marionette))
+
+ (test-equal "script created new generation"
+ (length (system-generations marionette))
+ (1+ (length generations-prior)))
+
+ (test-equal "script rolled back the new generation"
+ (string-append "/var/guix/profiles/system-"
+ (number->string (length generations-prior))
+ "-link")
+ (marionette-eval '(readlink "/run/current-system")
+ marionette)))
+
+ (test-end))))
+
+ (gexp->derivation "rollback" (test (deploy-program os))))
+
+(define %test-deploy
+ (system-test
+ (name "deploy")
+ (description "Deploy to the local machine.")
+ (value (run-deploy-test))))
+
+(define %test-rollback
+ (system-test
+ (name "rollback")
+ (description "Rollback the deployment of a faulty bootloader.")
+ (value (run-rollback-test))))