web.scm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2017 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2017 Christopher Baines <mail@cbaines.net>
  4. ;;; Copyright © 2017, 2018 Clément Lassieur <clement@lassieur.org>
  5. ;;; Copyright © 2018 Pierre-Antoine Rouby <pierre-antoine.rouby@inria.fr>
  6. ;;; Copyright © 2018 Marius Bakke <mbakke@fastmail.com>
  7. ;;;
  8. ;;; This file is part of GNU Guix.
  9. ;;;
  10. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  11. ;;; under the terms of the GNU General Public License as published by
  12. ;;; the Free Software Foundation; either version 3 of the License, or (at
  13. ;;; your option) any later version.
  14. ;;;
  15. ;;; GNU Guix is distributed in the hope that it will be useful, but
  16. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. ;;; GNU General Public License for more details.
  19. ;;;
  20. ;;; You should have received a copy of the GNU General Public License
  21. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  22. (define-module (gnu tests web)
  23. #:use-module (gnu tests)
  24. #:use-module (gnu system)
  25. #:use-module (gnu system file-systems)
  26. #:use-module (gnu system shadow)
  27. #:use-module (gnu system vm)
  28. #:use-module (gnu services)
  29. #:use-module (gnu services web)
  30. #:use-module (gnu services networking)
  31. #:use-module (guix gexp)
  32. #:use-module (guix store)
  33. #:export (%test-httpd
  34. %test-nginx
  35. %test-varnish
  36. %test-php-fpm
  37. %test-hpcguix-web
  38. %test-tailon))
  39. (define %index.html-contents
  40. ;; Contents of the /index.html file.
  41. "Hello, guix!")
  42. (define %make-http-root
  43. ;; Create our server root in /srv.
  44. #~(begin
  45. (mkdir "/srv")
  46. (mkdir "/srv/http")
  47. (call-with-output-file "/srv/http/index.html"
  48. (lambda (port)
  49. (display #$%index.html-contents port)))))
  50. (define* (run-webserver-test name test-os #:key (log-file #f) (http-port 8080))
  51. "Run tests in %NGINX-OS, which has nginx running and listening on
  52. HTTP-PORT."
  53. (define os
  54. (marionette-operating-system
  55. test-os
  56. #:imported-modules '((gnu services herd)
  57. (guix combinators))))
  58. (define forwarded-port 8080)
  59. (define vm
  60. (virtual-machine
  61. (operating-system os)
  62. (port-forwardings `((,http-port . ,forwarded-port)))))
  63. (define test
  64. (with-imported-modules '((gnu build marionette))
  65. #~(begin
  66. (use-modules (srfi srfi-11) (srfi srfi-64)
  67. (gnu build marionette)
  68. (web uri)
  69. (web client)
  70. (web response))
  71. (define marionette
  72. (make-marionette (list #$vm)))
  73. (mkdir #$output)
  74. (chdir #$output)
  75. (test-begin #$name)
  76. (test-assert #$(string-append name " service running")
  77. (marionette-eval
  78. '(begin
  79. (use-modules (gnu services herd))
  80. (match (start-service '#$(string->symbol name))
  81. (#f #f)
  82. (('service response-parts ...)
  83. (match (assq-ref response-parts 'running)
  84. ((#t) #t)
  85. ((pid) (number? pid))))))
  86. marionette))
  87. ;; Retrieve the index.html file we put in /srv.
  88. (test-equal "http-get"
  89. '(200 #$%index.html-contents)
  90. (let-values
  91. (((response text)
  92. (http-get #$(simple-format
  93. #f "http://localhost:~A/index.html" forwarded-port)
  94. #:decode-body? #t)))
  95. (list (response-code response) text)))
  96. #$@(if log-file
  97. `((test-assert ,(string-append "log file exists " log-file)
  98. (marionette-eval
  99. '(file-exists? ,log-file)
  100. marionette)))
  101. '())
  102. (test-end)
  103. (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
  104. (gexp->derivation (string-append name "-test") test))
  105. ;;;
  106. ;;; HTTPD
  107. ;;;
  108. (define %httpd-os
  109. (simple-operating-system
  110. (service dhcp-client-service-type)
  111. (service httpd-service-type
  112. (httpd-configuration
  113. (config
  114. (httpd-config-file
  115. (listen '("8080"))))))
  116. (simple-service 'make-http-root activation-service-type
  117. %make-http-root)))
  118. (define %test-httpd
  119. (system-test
  120. (name "httpd")
  121. (description "Connect to a running HTTPD server.")
  122. (value (run-webserver-test name %httpd-os
  123. #:log-file "/var/log/httpd/error_log"))))
  124. ;;;
  125. ;;; NGINX
  126. ;;;
  127. (define %nginx-servers
  128. ;; Server blocks.
  129. (list (nginx-server-configuration
  130. (listen '("8080")))))
  131. (define %nginx-os
  132. ;; Operating system under test.
  133. (simple-operating-system
  134. (service dhcp-client-service-type)
  135. (service nginx-service-type
  136. (nginx-configuration
  137. (log-directory "/var/log/nginx")
  138. (server-blocks %nginx-servers)))
  139. (simple-service 'make-http-root activation-service-type
  140. %make-http-root)))
  141. (define %test-nginx
  142. (system-test
  143. (name "nginx")
  144. (description "Connect to a running NGINX server.")
  145. (value (run-webserver-test name %nginx-os
  146. #:log-file "/var/log/nginx/access.log"))))
  147. ;;;
  148. ;;; Varnish
  149. ;;;
  150. (define %varnish-vcl
  151. (mixed-text-file
  152. "varnish-test.vcl"
  153. "vcl 4.0;
  154. backend dummy { .host = \"127.1.1.1\"; }
  155. sub vcl_recv { return(synth(200, \"OK\")); }
  156. sub vcl_synth {
  157. synthetic(\"" %index.html-contents "\");
  158. set resp.http.Content-Type = \"text/plain\";
  159. return(deliver);
  160. }"))
  161. (define %varnish-os
  162. (simple-operating-system
  163. (service dhcp-client-service-type)
  164. ;; Pretend to be a web server that serves %index.html-contents.
  165. (service varnish-service-type
  166. (varnish-configuration
  167. (name "/tmp/server")
  168. ;; Use a small VSL buffer to fit in the test VM.
  169. (parameters '(("vsl_space" . "4M")))
  170. (vcl %varnish-vcl)))
  171. ;; Proxy the "server" using the builtin configuration.
  172. (service varnish-service-type
  173. (varnish-configuration
  174. (parameters '(("vsl_space" . "4M")))
  175. (backend "localhost:80")
  176. (listen '(":8080"))))))
  177. (define %test-varnish
  178. (system-test
  179. (name "varnish")
  180. (description "Test the Varnish Cache server.")
  181. (value (run-webserver-test "varnish-default" %varnish-os))))
  182. ;;;
  183. ;;; PHP-FPM
  184. ;;;
  185. (define %make-php-fpm-http-root
  186. ;; Create our server root in /srv.
  187. #~(begin
  188. (mkdir "/srv")
  189. (call-with-output-file "/srv/index.php"
  190. (lambda (port)
  191. (display "<?php
  192. phpinfo();
  193. echo(\"Computed by php:\".((string)(2+3)));
  194. ?>\n" port)))))
  195. (define %php-fpm-nginx-server-blocks
  196. (list (nginx-server-configuration
  197. (root "/srv")
  198. (locations
  199. (list (nginx-php-location)))
  200. (listen '("8042"))
  201. (ssl-certificate #f)
  202. (ssl-certificate-key #f))))
  203. (define %php-fpm-os
  204. ;; Operating system under test.
  205. (simple-operating-system
  206. (service dhcp-client-service-type)
  207. (service php-fpm-service-type)
  208. (service nginx-service-type
  209. (nginx-configuration
  210. (server-blocks %php-fpm-nginx-server-blocks)))
  211. (simple-service 'make-http-root activation-service-type
  212. %make-php-fpm-http-root)))
  213. (define* (run-php-fpm-test #:optional (http-port 8042))
  214. "Run tests in %PHP-FPM-OS, which has nginx running and listening on
  215. HTTP-PORT, along with php-fpm."
  216. (define os
  217. (marionette-operating-system
  218. %php-fpm-os
  219. #:imported-modules '((gnu services herd)
  220. (guix combinators))))
  221. (define vm
  222. (virtual-machine
  223. (operating-system os)
  224. (port-forwardings `((8080 . ,http-port)))))
  225. (define test
  226. (with-imported-modules '((gnu build marionette)
  227. (guix build utils))
  228. #~(begin
  229. (use-modules (srfi srfi-11) (srfi srfi-64)
  230. (gnu build marionette)
  231. (web uri)
  232. (web client)
  233. (web response))
  234. (define marionette
  235. (make-marionette (list #$vm)))
  236. (mkdir #$output)
  237. (chdir #$output)
  238. (test-begin "php-fpm")
  239. (test-assert "php-fpm running"
  240. (marionette-eval
  241. '(begin
  242. (use-modules (gnu services herd))
  243. (match (start-service 'php-fpm)
  244. (#f #f)
  245. (('service response-parts ...)
  246. (match (assq-ref response-parts 'running)
  247. ((pid) (number? pid))))))
  248. marionette))
  249. (test-assert "nginx running"
  250. (marionette-eval
  251. '(begin
  252. (use-modules (gnu services herd))
  253. (start-service 'nginx))
  254. marionette))
  255. (test-equal "http-get"
  256. 200
  257. (let-values (((response text)
  258. (http-get "http://localhost:8080/index.php"
  259. #:decode-body? #t)))
  260. (response-code response)))
  261. (test-equal "php computed result is sent"
  262. "Computed by php:5"
  263. (let-values (((response text)
  264. (http-get "http://localhost:8080/index.php"
  265. #:decode-body? #t)))
  266. (begin
  267. (use-modules (ice-9 regex))
  268. (let ((matches (string-match "Computed by php:5" text)))
  269. (and matches
  270. (match:substring matches 0))))))
  271. (test-end)
  272. (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
  273. (gexp->derivation "php-fpm-test" test))
  274. (define %test-php-fpm
  275. (system-test
  276. (name "php-fpm")
  277. (description "Test PHP-FPM through nginx.")
  278. (value (run-php-fpm-test))))
  279. ;;;
  280. ;;; hpcguix-web
  281. ;;;
  282. (define* (run-hpcguix-web-server-test name test-os)
  283. "Run tests in %HPCGUIX-WEB-OS, which has hpcguix-web running."
  284. (define os
  285. (marionette-operating-system
  286. test-os
  287. #:imported-modules '((gnu services herd)
  288. (guix combinators))))
  289. (define vm
  290. (virtual-machine
  291. (operating-system os)
  292. (port-forwardings '((8080 . 5000)))))
  293. (define test
  294. (with-imported-modules '((gnu build marionette))
  295. #~(begin
  296. (use-modules (srfi srfi-11) (srfi srfi-64)
  297. (gnu build marionette)
  298. (web uri)
  299. (web client)
  300. (web response))
  301. (define marionette
  302. (make-marionette (list #$vm)))
  303. (mkdir #$output)
  304. (chdir #$output)
  305. (test-begin #$name)
  306. (test-assert "hpcguix-web running"
  307. (marionette-eval
  308. '(begin
  309. (use-modules (gnu services herd))
  310. (match (start-service 'hpcguix-web)
  311. (#f #f)
  312. (('service response-parts ...)
  313. (match (assq-ref response-parts 'running)
  314. ((pid) (number? pid))))))
  315. marionette))
  316. (test-equal "http-get"
  317. 200
  318. (begin
  319. (wait-for-tcp-port 5000 marionette)
  320. (let-values (((response text)
  321. (http-get "http://localhost:8080")))
  322. (response-code response))))
  323. (test-end)
  324. (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
  325. (gexp->derivation (string-append name "-test") test))
  326. (define %hpcguix-web-specs
  327. ;; Server config gexp.
  328. #~(define site-config
  329. (hpcweb-configuration
  330. (title-prefix "[TEST] HPCGUIX-WEB"))))
  331. (define %hpcguix-web-os
  332. (simple-operating-system
  333. (service dhcp-client-service-type)
  334. (service hpcguix-web-service-type
  335. (hpcguix-web-configuration
  336. (specs %hpcguix-web-specs)))))
  337. (define %test-hpcguix-web
  338. (system-test
  339. (name "hpcguix-web")
  340. (description "Connect to a running hpcguix-web server.")
  341. (value (run-hpcguix-web-server-test name %hpcguix-web-os))))
  342. (define %tailon-os
  343. ;; Operating system under test.
  344. (simple-operating-system
  345. (service dhcp-client-service-type)
  346. (service tailon-service-type
  347. (tailon-configuration
  348. (config-file
  349. (tailon-configuration-file
  350. (bind "0.0.0.0:8080")))))))
  351. (define* (run-tailon-test #:optional (http-port 8081))
  352. "Run tests in %TAILON-OS, which has tailon running and listening on
  353. HTTP-PORT."
  354. (define os
  355. (marionette-operating-system
  356. %tailon-os
  357. #:imported-modules '((gnu services herd)
  358. (guix combinators))))
  359. (define vm
  360. (virtual-machine
  361. (operating-system os)
  362. (port-forwardings `((,http-port . 8080)))))
  363. (define test
  364. (with-imported-modules '((gnu build marionette))
  365. #~(begin
  366. (use-modules (srfi srfi-11) (srfi srfi-64)
  367. (ice-9 match)
  368. (gnu build marionette)
  369. (web uri)
  370. (web client)
  371. (web response))
  372. (define marionette
  373. ;; Forward the guest's HTTP-PORT, where tailon is listening, to
  374. ;; port 8080 in the host.
  375. (make-marionette (list #$vm)))
  376. (mkdir #$output)
  377. (chdir #$output)
  378. (test-begin "tailon")
  379. (test-assert "service running"
  380. (marionette-eval
  381. '(begin
  382. (use-modules (gnu services herd))
  383. (start-service 'tailon))
  384. marionette))
  385. (define* (retry-on-error f #:key times delay)
  386. (let loop ((attempt 1))
  387. (match (catch
  388. #t
  389. (lambda ()
  390. (cons #t
  391. (f)))
  392. (lambda args
  393. (cons #f
  394. args)))
  395. ((#t . return-value)
  396. return-value)
  397. ((#f . error-args)
  398. (if (>= attempt times)
  399. error-args
  400. (begin
  401. (sleep delay)
  402. (loop (+ 1 attempt))))))))
  403. (test-equal "http-get"
  404. 200
  405. (retry-on-error
  406. (lambda ()
  407. (let-values (((response text)
  408. (http-get #$(format
  409. #f
  410. "http://localhost:~A/"
  411. http-port)
  412. #:decode-body? #t)))
  413. (response-code response)))
  414. #:times 10
  415. #:delay 5))
  416. (test-end)
  417. (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
  418. (gexp->derivation "tailon-test" test))
  419. (define %test-tailon
  420. (system-test
  421. (name "tailon")
  422. (description "Connect to a running Tailon server.")
  423. (value (run-tailon-test))))