reconfigure.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2019 Jakob L. Kreuze <zerodaysfordays@sdf.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. (define-module (gnu tests reconfigure)
  19. #:use-module (gnu bootloader)
  20. #:use-module (gnu services shepherd)
  21. #:use-module (gnu system)
  22. #:use-module (gnu system accounts)
  23. #:use-module (gnu system shadow)
  24. #:use-module (gnu system vm)
  25. #:use-module (gnu tests)
  26. #:use-module (guix derivations)
  27. #:use-module (guix gexp)
  28. #:use-module (guix monads)
  29. #:use-module (guix scripts system reconfigure)
  30. #:use-module (guix store)
  31. #:export (%test-switch-to-system
  32. %test-upgrade-services
  33. %test-install-bootloader))
  34. ;;; Commentary:
  35. ;;;
  36. ;;; Test in-place system reconfiguration: advancing the system generation on a
  37. ;;; running instance of the Guix System.
  38. ;;;
  39. ;;; Code:
  40. (define* (run-switch-to-system-test)
  41. "Run a test of an OS running SWITCH-SYSTEM-PROGRAM, which creates a new
  42. generation of the system profile."
  43. (define os
  44. (marionette-operating-system
  45. (operating-system
  46. (inherit (simple-operating-system))
  47. (users (cons (user-account
  48. (name "jakob")
  49. (group "users")
  50. (home-directory "/home/jakob"))
  51. %base-user-accounts)))
  52. #:imported-modules '((gnu services herd)
  53. (guix combinators))))
  54. (define vm (virtual-machine os))
  55. (define (test script)
  56. (with-imported-modules '((gnu build marionette))
  57. #~(begin
  58. (use-modules (gnu build marionette)
  59. (srfi srfi-64))
  60. (define marionette
  61. (make-marionette (list #$vm)))
  62. ;; Return the names of the generation symlinks on MARIONETTE.
  63. (define (system-generations marionette)
  64. (marionette-eval
  65. '(begin
  66. (use-modules (ice-9 ftw)
  67. (srfi srfi-1))
  68. (let* ((profile-dir "/var/guix/profiles/")
  69. (entries (map first (cddr (file-system-tree profile-dir)))))
  70. (remove (lambda (entry)
  71. (member entry '("per-user" "system")))
  72. entries)))
  73. marionette))
  74. (mkdir #$output)
  75. (chdir #$output)
  76. (test-begin "switch-to-system")
  77. (let ((generations-prior (system-generations marionette)))
  78. (test-assert "script successfully evaluated"
  79. (marionette-eval
  80. '(primitive-load #$script)
  81. marionette))
  82. (test-equal "script created new generation"
  83. (length (system-generations marionette))
  84. (1+ (length generations-prior)))
  85. (test-assert "script activated the new generation"
  86. (and (eqv? 'symlink
  87. (marionette-eval
  88. '(stat:type (lstat "/run/current-system"))
  89. marionette))
  90. (string= #$os
  91. (marionette-eval
  92. '(readlink "/run/current-system")
  93. marionette))))
  94. (test-assert "script activated user accounts"
  95. (marionette-eval
  96. '(string-contains (call-with-input-file "/etc/passwd"
  97. (lambda (port)
  98. (get-string-all port)))
  99. "jakob")
  100. marionette)))
  101. (test-end)
  102. (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
  103. (gexp->derivation "switch-to-system" (test (switch-system-program os))))
  104. (define* (run-upgrade-services-test)
  105. "Run a test of an OS running UPGRADE-SERVICES-PROGRAM, which upgrades the
  106. Shepherd (PID 1) by unloading obsolete services and loading new services."
  107. (define os
  108. (marionette-operating-system
  109. (simple-operating-system)
  110. #:imported-modules '((gnu services herd)
  111. (guix combinators))))
  112. (define vm (virtual-machine os))
  113. (define dummy-service
  114. ;; Shepherd service that does nothing, for the sole purpose of ensuring
  115. ;; that it is properly installed and started by the script.
  116. (shepherd-service (provision '(dummy))
  117. (start #~(const #t))
  118. (stop #~(const #t))
  119. (respawn? #f)))
  120. (define (test enable-dummy disable-dummy)
  121. (with-imported-modules '((gnu build marionette))
  122. #~(begin
  123. (use-modules (gnu build marionette)
  124. (srfi srfi-64))
  125. (define marionette
  126. (make-marionette (list #$vm)))
  127. ;; Return the names of the running services on MARIONETTE.
  128. (define (running-services marionette)
  129. (marionette-eval
  130. '(begin
  131. (use-modules (gnu services herd))
  132. (map live-service-canonical-name (current-services)))
  133. marionette))
  134. (mkdir #$output)
  135. (chdir #$output)
  136. (test-begin "upgrade-services")
  137. (let ((services-prior (running-services marionette)))
  138. (test-assert "script successfully evaluated"
  139. (marionette-eval
  140. '(primitive-load #$enable-dummy)
  141. marionette))
  142. (test-assert "script started new service"
  143. (and (not (memq 'dummy services-prior))
  144. (memq 'dummy (running-services marionette))))
  145. (test-assert "script successfully evaluated"
  146. (marionette-eval
  147. '(primitive-load #$disable-dummy)
  148. marionette))
  149. (test-assert "script stopped obsolete service"
  150. (not (memq 'dummy (running-services marionette)))))
  151. (test-end)
  152. (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
  153. (gexp->derivation
  154. "upgrade-services"
  155. (let* ((file (shepherd-service-file dummy-service))
  156. (enable (upgrade-services-program (list file) '(dummy) '() '()))
  157. (disable (upgrade-services-program '() '() '(dummy) '())))
  158. (test enable disable))))
  159. (define* (run-install-bootloader-test)
  160. "Run a test of an OS running INSTALL-BOOTLOADER-PROGRAM, which installs a
  161. bootloader's configuration file."
  162. (define os
  163. (marionette-operating-system
  164. (simple-operating-system)
  165. #:imported-modules '((gnu services herd)
  166. (guix combinators))))
  167. (define vm (virtual-machine os))
  168. (define (test script)
  169. (with-imported-modules '((gnu build marionette))
  170. #~(begin
  171. (use-modules (gnu build marionette)
  172. (ice-9 regex)
  173. (srfi srfi-1)
  174. (srfi srfi-64))
  175. (define marionette
  176. (make-marionette (list #$vm)))
  177. ;; Return the system generation paths that have GRUB menu entries.
  178. (define (generations-in-grub-cfg marionette)
  179. (let ((grub-cfg (marionette-eval
  180. '(begin
  181. (call-with-input-file "/boot/grub/grub.cfg"
  182. (lambda (port)
  183. (get-string-all port))))
  184. marionette)))
  185. (map (lambda (parameter)
  186. (second (string-split (match:substring parameter) #\=)))
  187. (list-matches "system=[^ ]*" grub-cfg))))
  188. (mkdir #$output)
  189. (chdir #$output)
  190. (test-begin "install-bootloader")
  191. (test-assert "no prior menu entry for system generation"
  192. (not (member #$os (generations-in-grub-cfg marionette))))
  193. (test-assert "script successfully evaluated"
  194. (marionette-eval
  195. '(primitive-load #$script)
  196. marionette))
  197. (test-assert "menu entry created for system generation"
  198. (member #$os (generations-in-grub-cfg marionette)))
  199. (test-end)
  200. (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
  201. (let* ((bootloader ((compose bootloader-configuration-bootloader
  202. operating-system-bootloader)
  203. os))
  204. ;; The typical use-case for 'install-bootloader-program' is to read
  205. ;; the boot parameters for the existing menu entries on the system,
  206. ;; parse them with 'boot-parameters->menu-entry', and pass the
  207. ;; results to 'operating-system-bootcfg'. However, to obtain boot
  208. ;; parameters, we would need to start the marionette, which we should
  209. ;; ideally avoid doing outside of the 'test' G-Expression. Thus, we
  210. ;; generate a bootloader configuration for the script as if there
  211. ;; were no existing menu entries. In the grand scheme of things, this
  212. ;; matters little -- these tests should not make assertions about the
  213. ;; behavior of 'operating-system-bootcfg'.
  214. (bootcfg (operating-system-bootcfg os '()))
  215. (bootcfg-file (bootloader-configuration-file bootloader)))
  216. (gexp->derivation
  217. "install-bootloader"
  218. ;; Due to the read-only nature of the virtual machines used in the system
  219. ;; test suite, the bootloader installer script is omitted. 'grub-install'
  220. ;; would attempt to write directly to the virtual disk if the
  221. ;; installation script were run.
  222. (test (install-bootloader-program #f #f bootcfg bootcfg-file #f "/")))))
  223. (define %test-switch-to-system
  224. (system-test
  225. (name "switch-to-system")
  226. (description "Create a new generation of the system profile.")
  227. (value (run-switch-to-system-test))))
  228. (define %test-upgrade-services
  229. (system-test
  230. (name "upgrade-services")
  231. (description "Upgrade the Shepherd by unloading obsolete services and
  232. loading new services.")
  233. (value (run-upgrade-services-test))))
  234. (define %test-install-bootloader
  235. (system-test
  236. (name "install-bootloader")
  237. (description "Install a bootloader and its configuration file.")
  238. (value (run-install-bootloader-test))))