From 4189437245ab5a1118b86b8e4e18b1808da051fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Ko=C5=BEar?= Date: Thu, 3 Jun 2021 14:16:31 +0200 Subject: [PATCH 1/3] fix linkcheck --- source/conf.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/conf.py b/source/conf.py index 9433455..3f69d2d 100644 --- a/source/conf.py +++ b/source/conf.py @@ -377,8 +377,13 @@ epub_exclude_files = ['search.html'] # If false, no index is generated. #epub_use_index = True -# it's an SPA -linkcheck_ignore = [r'https://app.terraform.io'] + +linkcheck_ignore = [ + # It's a SPA + r'https://app.terraform.io', + # Seems like README anchors aren't parsable? + r'https://github.com/cachix/install-nix-action', +] # Anchors are not present in HTML linkcheck_anchors_ignore = [ From dedcd7b6b3dc5d8fd19295289e3ee80442cb6b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Ko=C5=BEar?= Date: Thu, 3 Jun 2021 14:10:45 +0200 Subject: [PATCH 2/3] Add tutorial for integration testing using virtual machines --- .../continuous-integration-github-actions.rst | 2 + source/tutorials/index.rst | 1 + ...gration-testing-using-virtual-machines.rst | 231 ++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 source/tutorials/integration-testing-using-virtual-machines.rst diff --git a/source/tutorials/continuous-integration-github-actions.rst b/source/tutorials/continuous-integration-github-actions.rst index a376e8d..39f9f79 100644 --- a/source/tutorials/continuous-integration-github-actions.rst +++ b/source/tutorials/continuous-integration-github-actions.rst @@ -1,3 +1,5 @@ +.. _github-actions: + Continuous Integration with GitHub Actions ========================================== diff --git a/source/tutorials/index.rst b/source/tutorials/index.rst index 70aca46..dd19cae 100644 --- a/source/tutorials/index.rst +++ b/source/tutorials/index.rst @@ -13,4 +13,5 @@ Tutorials building-and-running-docker-images.rst deploying-nixos-using-terraform.rst installing-nixos-on-a-raspberry-pi.rst + integration-testing-using-virtual-machines.rst contributing.rst diff --git a/source/tutorials/integration-testing-using-virtual-machines.rst b/source/tutorials/integration-testing-using-virtual-machines.rst new file mode 100644 index 0000000..82d673f --- /dev/null +++ b/source/tutorials/integration-testing-using-virtual-machines.rst @@ -0,0 +1,231 @@ +Integration testing using Virtual Machines +========================================== + +One of the most powerful features in the Nix ecosystem is **the ability +to provide a set of declarative NixOS configurations and use a simple +Python interface** to interact with them using `QEMU `_ +as the backend. + +Those tests are widely used to ensure that NixOS works as intended, so in general they are called **NixOS tests**. +They can be written and launched outside of NixOS, on any Linux machine (with +`MacOS support coming soon `_). + +Integration tests are reproducible due to the design properties of Nix, +making them a valuable part of a Continuous Integration pipeline. + + +Testing a typical web application backed by PostgreSQL +------------------------------------------------------ + +This tutorial follows `PostgREST tutorial `_, +a generic `RESTful API `_ for PostgreSQL. + +If you skim over the official tutorial, you'll notice there's quite a bit of setup +in order to test if all the steps work. + +We are going to set up: + +- VM named ``server`` running postgreSQL and postgREST. + +- VM named ``client`` running HTTP client queries using ``curl``. + +- A ``testScript`` orchestrating testing logic between ``client`` and ``server``. + + +Writing the test +---------------- + +Create ``postgrest.nix``: + +.. TODO: highlight nix https://github.com/pygments/pygments/issues/1793 + + +.. code-block:: + :linenos: + + let + # Pin nixpkgs, see pinning tutorial for more details + nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/0f8f64b54ed07966b83db2f20c888d5e035012ef.tar.gz"; + pkgs = import nixpkgs {}; + + # Single source of truth for all tutorial constants + database = "postgres"; + schema = "api"; + table = "todos"; + username = "authenticator"; + password = "mysecretpassword"; + webRole = "web_anon"; + postgrestPort = 3000; + + # NixOS module shared between server and client + sharedModule = { + # Since it's common for CI not to have $DISPLAY available, we have to explicitly tell the tests "please don't expect any screen available" + virtualisation.graphics = false; + }; + + in pkgs.nixosTest ({ + # NixOS tests are run inside a virtual machine, and here we specify system of the machine. + system = "x86_64-linux"; + + nodes = { + server = { config, pkgs, ... }: { + imports = [ sharedModule ]; + + networking.firewall.allowedTCPPorts = [ postgrestPort ]; + + services.postgresql = { + enable = true; + + initialScript = pkgs.writeText "initialScript.sql" '' + create schema ${schema}; + + create table ${schema}.${table} ( + id serial primary key, + done boolean not null default false, + task text not null, + due timestamptz + ); + + insert into ${schema}.${table} (task) values ('finish tutorial 0'), ('pat self on back'); + + create role ${webRole} nologin; + grant usage on schema ${schema} to ${webRole}; + grant select on ${schema}.${table} to ${webRole}; + + create role ${username} inherit login password '${password}'; + grant ${webRole} to ${username}; + ''; + }; + + users = { + mutableUsers = false; + users = { + # For ease of debugging the VM as the `root` user + root.password = ""; + + # Create a system user that matches the database user so that we + # can use peer authentication. The tutorial defines a password, + # but it's not necessary. + "${username}".isSystemUser = true; + }; + }; + + systemd.services.postgrest = { + wantedBy = [ "multi-user.target" ]; + after = [ "postgresql.service" ]; + script = + let + configuration = pkgs.writeText "tutorial.conf" '' + db-uri = "postgres://${username}:${password}@localhost:${toString config.services.postgresql.port}/${database}" + db-schema = "${schema}" + db-anon-role = "${username}" + ''; + in "${pkgs.haskellPackages.postgrest}/bin/postgrest ${configuration}"; + serviceConfig.User = username; + }; + }; + + client = { + imports = [ sharedModule ]; + }; + }; + + # Disable linting for simpler debugging of the testScript + skipLint = true; + + testScript = '' + import json + import sys + + start_all() + + server.wait_for_open_port(${toString postgrestPort}) + + expected = [ + {"id": 1, "done": False, "task": "finish tutorial 0", "due": None}, + {"id": 2, "done": False, "task": "pat self on back", "due": None}, + ] + + actual = json.loads( + client.succeed( + "${pkgs.curl}/bin/curl http://server:${toString postgrestPort}/${table}" + ) + ) + + assert expected == actual, "table query returns expected content" + ''; + }) + +A few notes: + +- Between the machines defined inside the ``nodes`` attribute, hostnames + are resolved based on their attribute names. In this case we have ``client`` and ``server``. + +- The testing framework exposes a wide set of operations used inside the ``testScript``. + A full set of testing operations is part of + `VM testing operations API Reference `_. + + +Running tests +------------- + +To set up all machines and execute the test script: + +.. code:: shell-session + + $ nix-build postgrest.nix + +You'll notice an error message if something goes wrong. + +In case the tests succeed, you should see at the end: + +.. code:: shell-session + + ... + test script finished in 10.96s + cleaning up + killing client (pid 10) + killing server (pid 22) + (0.00 seconds) + /nix/store/bx7z3imvxxpwkkza10vb23czhw7873w2-vm-test-run-unnamed + + +Developing and debugging tests +------------------------------ + +When developing tests or when something breaks, it's useful to interactively fiddle +with the script or access a terminal for a machine. + +To interactively start a Python session with testing framework: + +.. code:: shell-session + + $ $(nix-build -A driver postgrest.nix)/bin/nixos-test-driver + ... + starting VDE switch for network 1 + >>> + +You can run `any of the testing operations `_. +``testScript`` attribute from our ``postgrest.nix`` definition can be executed with ``test_script()`` function. + +To start all machines and enter a telnet terminal to a specific machine: + +.. code:: shell-session + + >>> start_all() + ... + >>> server.shell_interact() + server: Terminal is ready (there is no prompt): + + uname -a + Linux server 5.10.37 #1-NixOS SMP Fri May 14 07:50:46 UTC 2021 x86_64 GNU/Linux + + +Next steps +---------- + +- Running integration tests on CI requires hardware acceleration, which many CIs do not support. + To run integration tests on :ref:`GitHub Actions ` see + `how to disable hardware acceleration `_. + +- NixOS comes with a large set of tests that serve also as educational examples, a good inspiration is `Matrix bridging with an IRC `_. From eb55acc61213f376b0223e8a53127b762fc1c122 Mon Sep 17 00:00:00 2001 From: Kim R2 <65985742+tzarsquared@users.noreply.github.com> Date: Mon, 7 Jun 2021 13:08:36 +0200 Subject: [PATCH 3/3] language edit --- ...ntegration-testing-using-virtual-machines.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/tutorials/integration-testing-using-virtual-machines.rst b/source/tutorials/integration-testing-using-virtual-machines.rst index 82d673f..9e6e732 100644 --- a/source/tutorials/integration-testing-using-virtual-machines.rst +++ b/source/tutorials/integration-testing-using-virtual-machines.rst @@ -1,5 +1,5 @@ -Integration testing using Virtual Machines -========================================== +Integration testing using virtual machines (VMs) +================================================ One of the most powerful features in the Nix ecosystem is **the ability to provide a set of declarative NixOS configurations and use a simple @@ -11,7 +11,7 @@ They can be written and launched outside of NixOS, on any Linux machine (with `MacOS support coming soon `_). Integration tests are reproducible due to the design properties of Nix, -making them a valuable part of a Continuous Integration pipeline. +making them a valuable part of a Continuous Integration (CI) pipeline. Testing a typical web application backed by PostgreSQL @@ -25,9 +25,9 @@ in order to test if all the steps work. We are going to set up: -- VM named ``server`` running postgreSQL and postgREST. +- A VM named ``server`` running postgreSQL and postgREST. -- VM named ``client`` running HTTP client queries using ``curl``. +- A VM named ``client`` running HTTP client queries using ``curl``. - A ``testScript`` orchestrating testing logic between ``client`` and ``server``. @@ -196,7 +196,7 @@ Developing and debugging tests When developing tests or when something breaks, it's useful to interactively fiddle with the script or access a terminal for a machine. -To interactively start a Python session with testing framework: +To interactively start a Python session with a testing framework: .. code:: shell-session @@ -206,7 +206,7 @@ To interactively start a Python session with testing framework: >>> You can run `any of the testing operations `_. -``testScript`` attribute from our ``postgrest.nix`` definition can be executed with ``test_script()`` function. +The ``testScript`` attribute from our ``postgrest.nix`` definition can be executed with ``test_script()`` function. To start all machines and enter a telnet terminal to a specific machine: @@ -228,4 +228,4 @@ Next steps To run integration tests on :ref:`GitHub Actions ` see `how to disable hardware acceleration `_. -- NixOS comes with a large set of tests that serve also as educational examples, a good inspiration is `Matrix bridging with an IRC `_. +- NixOS comes with a large set of tests that serve also as educational examples. A good inspiration is `Matrix bridging with an IRC `_.