;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2015 Mathieu Lirzin ;;; Copyright © 2015 David Thompson ;;; Copyright © 2016, 2019 Efraim Flashner ;;; Copyright © 2017 Nikita ;;; Copyright © 2017–2021 Tobias Geerinckx-Rice ;;; Copyright © 2020, 2022 Marius Bakke ;;; Copyright © 2020 EuAndreh ;;; Copyright © 2021 Noisytoot ;;; Copyright © 2021 Zhu Zihao ;;; Copyright © 2021 Petr Hodina ;;; Copyright © 2022 jgart ;;; Copyright © 2022 Vinicius Monego ;;; ;;; 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)
aboutsummaryrefslogtreecommitdiff
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2018 Gábor Boskovits  <boskovits@gmail.com>
;;; Copyright © 2018, 2019 Oleg Pykhalov <go.wigust@gmail.com>
;;;
;;; 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 monitoring)
  #:use-module (gnu packages databases)
  #:use-module (gnu packages monitoring)
  #:use-module (gnu packages php)
  #:use-module (gnu services)
  #:use-module (gnu services monitoring)
  #:use-module (gnu services networking)
  #:use-module (gnu services databases)
  #:use-module (gnu services shepherd)
  #:use-module (gnu services web)
  #:use-module (gnu system vm)
  #:use-module (gnu system)
  #:use-module (gnu tests)
  #:use-module (guix gexp)
  #:export (%test-prometheus-node-exporter
            %test-zabbix))


;;;
;;; Prometheus Node Exporter
;;;

(define* (run-prometheus-node-exporter-server-test name test-os)
  "Run tests in %PROMETHEUS-NODE-EXPORTER-OS, which has prometheus-node-exporter running."
  (define os
    (marionette-operating-system
     test-os
     #:imported-modules '((gnu services herd))))

  (define vm
    (virtual-machine
     (operating-system os)
     (port-forwardings '((8080 . 9100)))))

  (define test
    (with-imported-modules '((gnu build marionette))
      #~(begin
          (use-modules (srfi srfi-11)
                       (srfi srfi-64)
                       (gnu build marionette)
                       (web client)
                       (web response))

          (define marionette
            (make-marionette (list #$vm)))

          (test-runner-current (system-test-runner #$output))
          (test-begin #$name)

          (test-assert "prometheus-node-exporter running"
            (marionette-eval
             '(begin
                (use-modules (gnu services herd))
                (match (start-service 'prometheus-node-exporter)
                  (#f #f)
                  (('service response-parts ...)
                   (match (assq-ref response-parts 'running)
                     ((pid) pid)))))
             marionette))

          (test-equal "http-get"
            200
            (begin
              (wait-for-tcp-port 9100 marionette)
              (let-values (((response text)
                            (http-get "http://localhost:8080")))
                (response-code response))))

          (test-end))))

  (gexp->derivation (string-append name "-test") test))

(define %prometheus-node-exporter-os
  (simple-operating-system
   (service dhcp-client-service-type)
   (service prometheus-node-exporter-service-type
            (prometheus-node-exporter-configuration))))

(define %test-prometheus-node-exporter
  (system-test
   (name "prometheus-node-exporter")
   (description "Connect to a running prometheus-node-exporter server.")
   (value (run-prometheus-node-exporter-server-test
           name %prometheus-node-exporter-os))))


;;;
;;; Zabbix
;;;

(define %psql-user-create-zabbix
  "\
sudo -u postgres psql <<< \"create user zabbix password 'zabbix';\"
")

(define %psql-db-zabbix-create-script
  "\
sudo -u postgres psql --no-align <<< \\\\du
")

(define %psql-db-create-zabbix
  "\
sudo -u postgres createdb -O zabbix -E Unicode -T template0 zabbix
")

(define %psql-db-import-zabbix
  #~(format #f "\
cat ~a | sudo -u zabbix psql zabbix;
cat ~a | sudo -u zabbix psql zabbix;
cat ~a | sudo -u zabbix psql zabbix;
"
            (string-append #$zabbix-server:schema
                           "/database/postgresql/schema.sql")
            (string-append #$zabbix-server:schema
                           "/database/postgresql/images.sql")
            (string-append #$zabbix-server:schema
                           "/database/postgresql/data.sql")))

(define* (run-zabbix-server-test name test-os)
  "Run tests in %ZABBIX-OS, which has zabbix running."
  (define os
    (marionette-operating-system
     test-os
     #:imported-modules '((gnu services herd))))

  (define vm
    (virtual-machine
     (operating-system os)
     (port-forwardings '((8080 . 80)))
     (memory-size 1024)))

  (define test
    (with-imported-modules '((gnu build marionette))
      #~(begin
          (use-modules (srfi srfi-11)
                       (srfi srfi-64)
                       (gnu build marionette)
                       (web client)
                       (web response)
                       (ice-9 popen)
                       (ice-9 rdelim))

          (define marionette
            (make-marionette (list #$vm)))

          (test-runner-current (system-test-runner #$output))
          (test-begin #$name)

          ;; XXX: Shepherd reads the config file *before* binding its control
          ;; socket, so /var/run/shepherd/socket might not exist yet when the
          ;; 'marionette' service is started.
          (test-assert "shepherd socket ready"
            (marionette-eval
             `(begin
                (use-modules (gnu services herd))
                (let loop ((i 10))
                  (cond ((file-exists? (%shepherd-socket-file))
                         #t)
                        ((> i 0)
                         (sleep 1)
                         (loop (- i 1)))
                        (else
                         'failure))))
             marionette))

          (test-assert "postgres service running"
            (marionette-eval
             '(begin
                (use-modules (gnu services herd))
                (start-service 'postgres))
             marionette))

          ;; Add privileged programs to $PATH so that the scripts passed to
          ;; 'system' can find 'sudo'.
          (marionette-eval
           '(setenv "PATH"
                    "/run/privileged/bin:/run/current-system/profile/bin")
           marionette)

          (test-eq "postgres create zabbix user"
            0
            (marionette-eval '(begin (system #$%psql-user-create-zabbix))
                             marionette))

          (test-equal "postgres find zabbix user"
            "List of roles
Role name|Attributes|Member of
postgres|Superuser, Create role, Create DB, Replication, Bypass RLS|{}
zabbix||{}
"
            (marionette-eval
             '(begin
                (use-modules (ice-9 popen)
                             (ice-9 rdelim))

                (let* ((port (open-pipe #$%psql-db-zabbix-create-script
                                        OPEN_READ))
                       (output (read-string port))
                       (status (close-pipe port)))
                  output))
             marionette))

          (test-eq "postgres create zabbix db"
            0
            (marionette-eval '(begin (system #$%psql-db-create-zabbix))
                             marionette))

          (test-eq "postgres import zabbix db"
            0
            (marionette-eval '(begin (system #$%psql-db-import-zabbix))
                             marionette))

          ;; Wait for zabbix-server to be up and running.
          (test-assert "zabbix-server running"
            (marionette-eval
             '(begin
                (use-modules (gnu services herd))
                (start-service 'zabbix-server))
             marionette))

          ;; Make sure the PID file is created.
          (test-assert "zabbix-server PID file"
            (marionette-eval
             '(file-exists? "/var/run/zabbix/zabbix_server.pid")
             marionette))

          ;; Wait for zabbix-agent to be up and running.
          (test-assert "zabbix-agent running"
            (marionette-eval
             '(begin
                (use-modules (gnu services herd))
                (start-service 'zabbix-agent))
             marionette))

          ;; Make sure the PID file is created.
          (test-assert "zabbix-agent PID file"
            (marionette-eval
             '(file-exists? "/var/run/zabbix/zabbix_agent.pid")
             marionette))

          ;; Wait for php-fpm to be up and running.
          (test-assert "php-fpm running"
            (marionette-eval
             '(begin
                (use-modules (gnu services herd))
                (start-service 'php-fpm))
             marionette))

          ;; Wait for nginx to be up and running.
          (test-assert "nginx running"
            (marionette-eval
             '(begin
                (use-modules (gnu services herd))
                (start-service 'nginx))
             marionette))

          ;; Make sure the PID file is created.
          (test-assert "nginx PID file"
            (marionette-eval
             '(file-exists? "/var/run/nginx/pid")
             marionette))

          ;; Make sure we can access pages that correspond to our Zabbix instance.
          (letrec-syntax ((test-url
                           (syntax-rules ()
                             ((_ path code)
                              (test-equal (string-append "GET " path)
                                code
                                (let-values (((response body)
                                              (http-get (string-append
                                                         "http://localhost:8080"
                                                         path))))
                                  (response-code response))))
                             ((_ path)
                              (test-url path 200)))))
            (test-url "/")
            (test-url "/does-not-exist" 404))

          (test-end))))

  (gexp->derivation (string-append name "-test") test))

(define %zabbix-os
  ;; Return operating system under test.
  (let ((base-os
         (simple-operating-system
          (service dhcp-client-service-type)
          (service postgresql-service-type
                   (postgresql-configuration
                    (postgresql postgresql)))
          (service zabbix-front-end-service-type
                   (zabbix-front-end-configuration
                    (db-password "zabbix")))

          (service php-fpm-service-type
                   (php-fpm-configuration
                    (timezone "Europe/Paris")))

          (service zabbix-server-service-type
                   (zabbix-server-configuration
                    (db-password "zabbix")
                    (log-type "console")))

          (service zabbix-agent-service-type))))
    (operating-system
      (inherit base-os)
      (packages (cons* postgresql (operating-system-packages base-os))))))

(define %test-zabbix
  (system-test
   (name "zabbix")
   (description "Connect to a running Zabbix")
   (value (run-zabbix-server-test name %zabbix-os))))
;; licensed. The CommonMark specification is Creative Commons CC-BY-SA 4.0 ;; licensed. See 'COPYING' in the source distribution for more information. (license (list license:bsd-2 license:expat license:cc-by-sa4.0)))) (define-public perl-commonmark (package (name "perl-commonmark") (version "0.290000") (source (origin (method url-fetch) (uri (string-append "mirror://cpan/authors/id/N/NW/NWELLNHOF/CommonMark-" version ".tar.gz")) (sha256 (base32 "1pgaqa4f00i9r5z7l9xiya0q51ysq0nhpvgr0f3rza3cxz1v80d5")))) (build-system perl-build-system) (arguments `(#:make-maker-flags ;; MakeMaker ignores LIBRARY_PATH. (list (format #f "LIBS=-L~a/lib -lcmark" (assoc-ref %build-inputs "cmark"))))) (inputs (list cmark perl-test-leaktrace perl-devel-checklib perl-module-build)) (home-page "https://metacpan.org/release/CommonMark") (synopsis "Interface to the CommonMark C library") (description "This module is an XS wrapper around the official CommonMark C library libcmark. It closely follows the original API.") (license license:perl-license))) (define-public cmark-gfm (package (inherit cmark) (name "cmark-gfm") (version "0.29.0.gfm.2") (home-page "https://github.com/github/cmark-gfm") (source (origin (method git-fetch) (uri (git-reference (url home-page) (commit version))) (file-name (git-file-name name version)) (sha256 (base32 "0vz6zs3m22k7jzfj4782lahciwfjlbi4m3qz5crsmssip3rwdy7h")))) (arguments '(#:test-target "test" #:phases (modify-phases %standard-phases (add-after 'install 'install-config (lambda* (#:key outputs #:allow-other-keys) (let ((out (assoc-ref outputs "out"))) ;; XXX: cmark-gfm-core-extensions.h includes this file. (install-file "src/config.h" (string-append out "/include")))))))) (synopsis "GitHub flavored CommonMark") (description "This package is a fork of @code{cmark}, with GitHub-specific Markdown additions."))) (define-public smu (package (name "smu") (version "1.5") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/Gottox/smu") (commit (string-append "v" version)))) (file-name (git-file-name name version)) (sha256 (base32 "1jm7lhnzjx4q7gcwlkvsbffcy0zppywyh50d71ami6dnq182vvcc")))) (build-system gnu-build-system) (arguments `(#:make-flags (list "CC=gcc" (string-append "PREFIX=" (assoc-ref %outputs "out"))) #:tests? #f ; no tests included #:phases (modify-phases %standard-phases (delete 'configure)))) (home-page "https://github.com/Gottox/smu") (synopsis "Simple markup") (description "Smu is a very simple and minimal markup language. It is designed for using in wiki-like environments. Smu makes it very easy to write your documents on the fly and convert them into HTML. Smu is capable to parse very large documents. As long as you avoid an huge amount of indents it scales just great. Smu was started as a rewrite of Markdown but became something more lightweight and consistent. The biggest difference between Markdown and smu is that smu doesn't support reference style links.") (license license:x11))) (define-public md4c (package (name "md4c") (version "0.4.8") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/mity/md4c/") (commit (string-append "release-" version)))) (file-name (git-file-name name version)) (sha256 (base32 "12pdh4rfjc3b0cblj5nz3jksr2376lx8ay0vw5dwa1s97q09pczq")))) (build-system cmake-build-system) (arguments '(#:tests? #f)) (home-page "https://github.com/mity/md4c/") (synopsis "C Markdown parser compliant to CommonMark") (description "MD4C is a C Markdown parser with a SAX-like interface. It is compliant to the CommonMark specification, with a few extensions.") (license license:expat))) (define-public python-mistletoe (package (name "python-mistletoe") (version "0.8.1") (source (origin (method url-fetch) (uri (pypi-uri "mistletoe" version)) (sha256 (base32 "0h8ydzxlfzmspiz8lcm13qp720kfsxiky0qqnc2mxf4qzm16m326")))) (build-system python-build-system) (arguments `(#:phases (modify-phases %standard-phases (replace 'check (lambda* (#:key tests? #:allow-other-keys) (when tests? (invoke "python" "-m" "unittest" "discover" "test"))))))) (home-page "https://github.com/miyuchina/mistletoe") (synopsis "Extensible Markdown parser in pure Python") (description "The @code{mistletoe} Markdown parser is a CommonMark-compliant Markdown parser that supports definitions of custom tokens. Parsing Markdown into an abstract syntax tree also allows @code{mistletoe} to swap out renderers for different output formats, without touching any of the core components.") (license license:expat)))