Commit | Line | Data |
---|---|---|
9a622827 MB |
1 | ;;; GNU Guix --- Functional package management for GNU |
2 | ;;; Copyright © 2020 Marius Bakke <marius@gnu.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 | (define-module (gnu tests ganeti) | |
20 | #:use-module (gnu) | |
21 | #:use-module (gnu tests) | |
22 | #:use-module (gnu system vm) | |
23 | #:use-module (gnu services) | |
24 | #:use-module (gnu services ganeti) | |
25 | #:use-module (gnu services networking) | |
26 | #:use-module (gnu services ssh) | |
27 | #:use-module (gnu packages virtualization) | |
28 | #:use-module (guix gexp) | |
29 | #:use-module (ice-9 format) | |
30 | #:export (%test-ganeti-kvm %test-ganeti-lxc)) | |
31 | ||
32 | (define %ganeti-os | |
33 | (operating-system | |
34 | (host-name "gnt1") | |
35 | (timezone "Etc/UTC") | |
36 | (locale "en_US.UTF-8") | |
37 | ||
38 | (bootloader (bootloader-configuration | |
39 | (bootloader grub-bootloader) | |
40 | (target "/dev/vda"))) | |
41 | (file-systems (cons (file-system | |
42 | (device (file-system-label "my-root")) | |
43 | (mount-point "/") | |
44 | (type "ext4")) | |
45 | %base-file-systems)) | |
46 | (firmware '()) | |
47 | ||
48 | ;; The hosts file must contain a nonlocal IP for host-name. | |
49 | ;; In addition, the cluster name must resolve to an IP address that | |
50 | ;; is not currently provisioned. | |
51 | (hosts-file (plain-file "hosts" (format #f " | |
52 | 127.0.0.1 localhost | |
53 | ::1 localhost | |
54 | 10.0.2.2 gnt1.example.com gnt1 | |
55 | 192.168.254.254 ganeti.example.com | |
56 | "))) | |
57 | ||
58 | (packages (append (list ganeti-instance-debootstrap ganeti-instance-guix) | |
59 | %base-packages)) | |
60 | (services | |
61 | (append (list (static-networking-service "eth0" "10.0.2.2" | |
62 | #:netmask "255.255.255.0" | |
63 | #:gateway "10.0.2.1" | |
64 | #:name-servers '("10.0.2.1")) | |
65 | ||
66 | (service openssh-service-type | |
67 | (openssh-configuration | |
68 | (permit-root-login 'without-password))) | |
69 | ||
70 | (service ganeti-service-type | |
71 | (ganeti-configuration | |
72 | (file-storage-paths '("/srv/ganeti/file-storage")) | |
41daf128 MB |
73 | (rapi-configuration |
74 | (ganeti-rapi-configuration | |
75 | ;; Disable TLS so we can test the RAPI without | |
76 | ;; pulling in GnuTLS. | |
77 | (ssl? #f))) | |
9a622827 MB |
78 | (os %default-ganeti-os)))) |
79 | %base-services)))) | |
80 | ||
81 | (define* (run-ganeti-test hypervisor #:key | |
82 | (master-netdev "eth0") | |
83 | (hvparams '()) | |
84 | (extra-packages '()) | |
85 | (rapi-port 5080) | |
86 | (noded-port 1811)) | |
87 | "Run tests in %GANETI-OS." | |
88 | (define os | |
89 | (marionette-operating-system | |
90 | (operating-system | |
91 | (inherit %ganeti-os) | |
92 | (packages (append extra-packages | |
93 | (operating-system-packages %ganeti-os)))) | |
94 | #:imported-modules '((gnu services herd) | |
95 | (guix combinators)))) | |
96 | ||
97 | (define %forwarded-rapi-port 5080) | |
98 | (define %forwarded-noded-port 1811) | |
99 | ||
100 | (define vm | |
101 | (virtual-machine | |
102 | (operating-system os) | |
103 | ;; Some of the daemons are fairly memory-hungry. | |
104 | (memory-size 512) | |
105 | ;; Forward HTTP ports so we can access them from the "outside". | |
106 | (port-forwardings `((,%forwarded-rapi-port . ,rapi-port) | |
107 | (,%forwarded-noded-port . ,noded-port))))) | |
108 | ||
109 | (define test | |
110 | (with-imported-modules '((gnu build marionette)) | |
111 | #~(begin | |
112 | (use-modules (srfi srfi-11) (srfi srfi-64) | |
113 | (web uri) (web client) (web response) | |
114 | (gnu build marionette)) | |
115 | ||
116 | (define marionette | |
117 | (make-marionette (list #$vm))) | |
118 | ||
119 | (mkdir #$output) | |
120 | (chdir #$output) | |
121 | ||
122 | (test-begin "ganeti") | |
123 | ||
124 | ;; Ganeti uses the Shepherd to start/stop daemons, so make sure | |
125 | ;; it is ready before we begin. It takes a while because all | |
126 | ;; Ganeti daemons fail to start initially. | |
127 | (test-assert "shepherd is ready" | |
128 | (wait-for-unix-socket "/var/run/shepherd/socket" marionette)) | |
129 | ||
130 | (test-eq "gnt-cluster init" | |
131 | 0 | |
132 | (marionette-eval | |
133 | '(begin | |
134 | (setenv | |
135 | "PATH" | |
136 | ;; Init needs to run 'ssh-keygen', 'ip', etc. | |
137 | "/run/current-system/profile/sbin:/run/current-system/profile/bin") | |
138 | (system* #$(file-append ganeti "/sbin/gnt-cluster") "init" | |
139 | (string-append "--master-netdev=" #$master-netdev) | |
140 | ;; TODO: Enable more disk backends. | |
141 | "--enabled-disk-templates=file" | |
142 | (string-append "--enabled-hypervisors=" | |
143 | #$hypervisor) | |
144 | (string-append "--hypervisor-parameters=" | |
145 | #$hypervisor ":" | |
146 | (string-join '#$hvparams "\n")) | |
147 | ;; Set the default NIC mode to 'routed' to avoid having to | |
148 | ;; configure a full bridge to placate 'gnt-cluster verify'. | |
149 | "--nic-parameters=mode=routed,link=eth0" | |
150 | "ganeti.example.com")) | |
151 | marionette)) | |
152 | ||
153 | ;; Disable the watcher while doing daemon tests to prevent interference. | |
154 | (test-eq "watcher pause" | |
155 | 0 | |
156 | (marionette-eval | |
157 | '(begin | |
158 | (system* #$(file-append ganeti "/sbin/gnt-cluster") | |
159 | "watcher" "pause" "1h")) | |
160 | marionette)) | |
161 | ||
162 | (test-assert "force-start wconfd" | |
163 | ;; Check that the 'force-start' Shepherd action works, used in a | |
164 | ;; master-failover scenario. | |
165 | (marionette-eval | |
166 | '(begin | |
167 | (setenv "PATH" "/run/current-system/profile/bin") | |
168 | (invoke "herd" "stop" "ganeti-wconfd") | |
169 | (invoke "herd" "disable" "ganeti-wconfd") | |
170 | (invoke "herd" "force-start" "ganeti-wconfd")) | |
171 | marionette)) | |
172 | ||
173 | ;; Verify that the cluster is healthy. | |
174 | (test-eq "gnt-cluster verify 1" | |
175 | 0 | |
176 | (marionette-eval | |
177 | '(begin | |
178 | (system* #$(file-append ganeti "/sbin/gnt-cluster") "verify")) | |
179 | marionette)) | |
180 | ||
181 | ;; Try stopping and starting daemons with daemon-util like | |
182 | ;; 'gnt-node add', 'gnt-cluster init', etc. | |
183 | (test-eq "daemon-util stop-all" | |
184 | 0 | |
185 | (marionette-eval | |
186 | '(begin | |
187 | (system* #$(file-append ganeti "/lib/ganeti/daemon-util") | |
188 | "stop-all")) | |
189 | marionette)) | |
190 | ||
191 | (test-eq "daemon-util start-all" | |
192 | 0 | |
193 | (marionette-eval | |
194 | '(begin | |
195 | (system* #$(file-append ganeti "/lib/ganeti/daemon-util") | |
196 | "start-all")) | |
197 | marionette)) | |
198 | ||
199 | ;; Check that the cluster is still healthy after the daemon restarts. | |
200 | (test-eq "gnt-cluster verify 2" | |
201 | 0 | |
202 | (marionette-eval | |
203 | '(begin | |
204 | (system* #$(file-append ganeti "/sbin/gnt-cluster") "verify")) | |
205 | marionette)) | |
206 | ||
207 | (test-eq "watcher continue" | |
208 | 0 | |
209 | (marionette-eval | |
210 | '(begin | |
211 | (system* #$(file-append ganeti "/sbin/gnt-cluster") | |
212 | "watcher" "continue")) | |
213 | marionette)) | |
214 | ||
215 | ;; Try accessing the RAPI. This causes an expected failure: | |
216 | ;; https://github.com/ganeti/ganeti/issues/1502 | |
217 | ;; Run it anyway for easy testing of potential fixes. | |
218 | (test-equal "http-get RAPI version" | |
219 | '(200 "2") | |
220 | (let-values | |
221 | (((response text) | |
222 | (http-get #$(simple-format | |
223 | #f "http://localhost:~A/version" | |
224 | %forwarded-rapi-port) | |
225 | #:decode-body? #t))) | |
226 | (list (response-code response) text))) | |
227 | ||
228 | (test-equal "gnt-os list" | |
229 | "debootstrap+default\nguix+default\n" | |
230 | (marionette-eval | |
231 | '(begin | |
232 | (use-modules (ice-9 popen)) | |
233 | (let* ((port (open-pipe* | |
234 | OPEN_READ | |
235 | #$(file-append ganeti "/sbin/gnt-os") | |
236 | "list" "--no-headers")) | |
237 | (output (get-string-all port))) | |
238 | (close-pipe port) | |
239 | output)) | |
240 | marionette)) | |
241 | ||
242 | (test-eq "gnt-cluster destroy" | |
243 | 0 | |
244 | (marionette-eval | |
245 | '(begin | |
246 | (system* #$(file-append ganeti "/sbin/gnt-cluster") | |
247 | "destroy" "--yes-do-it")) | |
248 | marionette)) | |
249 | ||
250 | (test-end) | |
251 | (exit (= (test-runner-fail-count (test-runner-current)) 1))))) | |
252 | ||
253 | (gexp->derivation (string-append "ganeti-" hypervisor "-test") test)) | |
254 | ||
255 | (define %test-ganeti-kvm | |
256 | (system-test | |
257 | (name "ganeti-kvm") | |
258 | (description "Provision a Ganeti cluster using the KVM hypervisor.") | |
259 | (value (run-ganeti-test "kvm" | |
260 | ;; Set kernel_path to an empty string to prevent | |
261 | ;; 'gnt-cluster verify' from testing for its presence. | |
262 | #:hvparams '("kernel_path=") | |
263 | #:extra-packages (list qemu))))) | |
264 | ||
265 | (define %test-ganeti-lxc | |
266 | (system-test | |
267 | (name "ganeti-lxc") | |
268 | (description "Provision a Ganeti cluster using LXC as the hypervisor.") | |
269 | (value (run-ganeti-test "lxc" | |
270 | #:extra-packages (list lxc))))) |