Repository: freedomofpress/securedrop

Issue:
QA: Automate basic server testing
The default QA test plan includes a basic testing section that mostly checks server configuration. These tests are duplicated in the production testinfra tests, so with some work to get `testinfra` to use production settings where available (via `install_files/ansible-base/group_vars/all/site-specific`), it should be possible to reduce tester workload by removing Basic testing in favour of `testinfra`.


Discussion:

Resolving patch:
diff --git a/molecule/testinfra/conftest.py b/molecule/testinfra/conftest.py
--- a/molecule/testinfra/conftest.py
+++ b/molecule/testinfra/conftest.py
@@ -14,8 +14,7 @@
 import testutils
 
 
-# The config tests target staging by default. It's possible to override
-# for e.g. prod, but the associated vars files are not yet ported.
+# The config tests target staging by default.
 target_host = os.environ.get('SECUREDROP_TESTINFRA_TARGET_HOST', 'staging')
 
 
@@ -35,6 +34,34 @@ def securedrop_import_testinfra_vars(hostname, with_header=False):
     hostvars['python_version'] = "3.8"
     hostvars['apparmor_enforce_actual'] = hostvars['apparmor_enforce']['focal']
 
+    # If the tests are run against a production environment, check local config
+    # and override as necessary.
+    prod_filepath = os.path.join(os.path.dirname(__file__),
+                                 "../../install_files/ansible-base/group_vars/all/site-specific")
+    if os.path.isfile(prod_filepath):
+        with io.open(prod_filepath, 'r') as f:
+            prodvars = yaml.safe_load(f)
+
+        def _prod_override(vars_key, prod_key):
+            if prod_key in prodvars:
+                hostvars[vars_key] = prodvars[prod_key]
+
+        _prod_override('app_ip', 'app_ip')
+        _prod_override('mon_ip', 'monitor_ip')
+        _prod_override('sasl_domain', 'sasl_domain')
+        _prod_override('sasl_username', 'sasl_username')
+        _prod_override('sasl_password', 'sasl_password')
+        _prod_override('daily_reboot_time', 'daily_reboot_time')
+
+        # Check repo targeting, and update vars
+        repo_filepath = os.path.join(os.path.dirname(__file__),
+                                 "../../install_files/ansible-base/roles/install-fpf-repo/defaults/main.yml")  # noqa: E501
+        if os.path.isfile(repo_filepath):
+            with io.open(repo_filepath, 'r') as f:
+                repovars = yaml.safe_load(f)
+                if 'apt_repo_url' in repovars:
+                    hostvars['fpf_apt_repo_url'] = repovars['apt_repo_url']
+
     if with_header:
         hostvars = dict(securedrop_test_vars=hostvars)
 


Test patch:
diff --git a/molecule/testinfra/common/test_automatic_updates.py b/molecule/testinfra/common/test_automatic_updates.py
--- a/molecule/testinfra/common/test_automatic_updates.py
+++ b/molecule/testinfra/common/test_automatic_updates.py
@@ -6,6 +6,10 @@
 test_vars = testutils.securedrop_test_vars
 testinfra_hosts = [test_vars.app_hostname, test_vars.monitor_hostname]
 
+# Updates and upgrades are scheduled at set times before a scheduled reboot
+OFFSET_UPDATE = 2
+OFFSET_UPGRADE = 1
+
 
 def test_automatic_updates_dependencies(host):
     """
@@ -65,7 +69,7 @@ def test_sources_list(host, repo):
     "APT::Periodic::Enable": "1",
     "Unattended-Upgrade::AutoFixInterruptedDpkg": "true",
     "Unattended-Upgrade::Automatic-Reboot": "true",
-    "Unattended-Upgrade::Automatic-Reboot-Time": "4:00",
+    "Unattended-Upgrade::Automatic-Reboot-Time": "{}:00".format(test_vars.daily_reboot_time),
     "Unattended-Upgrade::Automatic-Reboot-WithUsers": "true",
     "Unattended-Upgrade::Origins-Pattern": [
         "origin=${distro_id},archive=${distro_codename}",
@@ -145,21 +149,23 @@ def test_apt_daily_services_and_timers_enabled(host, service):
 
 def test_apt_daily_timer_schedule(host):
     """
-    Timer for running apt-daily, i.e. 'apt-get update', should be 2h
+    Timer for running apt-daily, i.e. 'apt-get update', should be OFFSET_UPDATE hrs
     before the daily_reboot_time.
     """
+    t = (int(test_vars.daily_reboot_time) - OFFSET_UPDATE) % 24
     c = host.run("systemctl show apt-daily.timer")
-    assert "TimersCalendar={ OnCalendar=*-*-* 02:00:00 ;" in c.stdout
+    assert "TimersCalendar={ OnCalendar=*-*-* " + "{:02d}".format(t) + ":00:00 ;" in c.stdout
     assert "RandomizedDelayUSec=20m" in c.stdout
 
 
 def test_apt_daily_upgrade_timer_schedule(host):
     """
-    Timer for running apt-daily-upgrade, i.e. 'apt-get upgrade', should be 1h
+    Timer for running apt-daily-upgrade, i.e. 'apt-get upgrade', should be OFFSET_UPGRADE hrs
     before the daily_reboot_time, and 1h after the apt-daily time.
     """
+    t = (int(test_vars.daily_reboot_time) - OFFSET_UPGRADE) % 24
     c = host.run("systemctl show apt-daily-upgrade.timer")
-    assert "TimersCalendar={ OnCalendar=*-*-* 03:00:00 ;" in c.stdout
+    assert "TimersCalendar={ OnCalendar=*-*-* " + "{:02d}".format(t) + ":00:00 ;" in c.stdout
     assert "RandomizedDelayUSec=20m" in c.stdout