diff --git a/software/metabase/test/test.py b/software/metabase/test/test.py index 9bbed0cb8fbd9897326e78dd3a4b20acbcd8b681..3a349d282cc376983b6da1f033ebaeac08ba0318 100644 --- a/software/metabase/test/test.py +++ b/software/metabase/test/test.py @@ -25,23 +25,32 @@ # ############################################################################## +import contextlib import os import json from urllib import parse +import shutil +import subprocess +import time + import requests from slapos.testing.testcase import makeModuleSetUpAndTestCaseClass +from slapos.testing.utils import CrontabMixin + -setUpModule, MetabaseTestCase = makeModuleSetUpAndTestCaseClass( +setUpModule, BaseTestCase = makeModuleSetUpAndTestCaseClass( os.path.abspath( os.path.join(os.path.dirname(__file__), '..', 'software.cfg'))) -class TestMetabaseSetup(MetabaseTestCase): +class MetabaseTestCase(BaseTestCase): __partition_reference__ = 'S' # postgresql use a socket in data dir # instance can take time, /api/session/properties timeout at the beginning. instance_max_retry = 30 + +class TestMetabaseSetup(MetabaseTestCase): def test_setup(self): url = self.computer_partition.getConnectionParameterDict()['url'] resp = requests.get(parse.urljoin(url, '/setup'), verify=False) @@ -94,3 +103,62 @@ class TestMetabaseSetup(MetabaseTestCase): "password": password }).json() self.assertTrue(session.get('id')) + + +class TestMetabaseBackup(MetabaseTestCase, CrontabMixin): + def test_backup(self): + self._executeCrontabAtDate('postgresql-backup-crontab-entry', '2100-01-01') + with open( + os.path.join( + self.computer_partition_root_path, 'srv', 'backup', 'backup.pg_dump'), + 'rb') as f: + self.assertIn(b'CREATE DATABASE metabase_db', f.read()) + + def test_restore(self): + # restore a "known good" backup and check we can login with the + # user from the backup. + url = self.computer_partition.getConnectionParameterDict()['url'] + + shutil.copyfile( + os.path.join(os.path.dirname(__file__), 'testdata', 'backup.pg_dump'), + os.path.join( + self.computer_partition_root_path, 'srv', 'backup', 'backup.pg_dump') + ) + + with self.slap.instance_supervisor_rpc as supervisor: + # stop metabase, so that it does not interfere with restoring the backup + info, = [i for i in + supervisor.getAllProcessInfo() if 'metabase-instance' in i['name']] + metabase_process_name = f"{info['group']}:{info['name']}" + supervisor.stopProcess(metabase_process_name) + + # restart postgres, to terminate all current connections + info, = [i for i in + supervisor.getAllProcessInfo() if 'postgres' in i['name']] + postresql_process_name = f"{info['group']}:{info['name']}" + supervisor.stopProcess(postresql_process_name) + supervisor.startProcess(postresql_process_name) + + subprocess.check_output( + os.path.join( + self.computer_partition_root_path, 'bin', 'postgresql-restore-backup')) + + with self.slap.instance_supervisor_rpc as supervisor: + supervisor.startProcess(metabase_process_name) + + for _ in range(30): + with contextlib.suppress(requests.exceptions.RequestException): + time.sleep(1) + resp = requests.post( + parse.urljoin(url, '/api/session'), + verify=False, + json={ + "username": "youlooknicetoday@email.com", + "password": "passwordformbackup123" + }, timeout=5) + if resp.ok: + session = resp.json() + break + else: + resp.raise_for_status() + self.assertTrue(session.get('id')) diff --git a/software/metabase/test/testdata/backup.pg_dump b/software/metabase/test/testdata/backup.pg_dump new file mode 100644 index 0000000000000000000000000000000000000000..b9906dffe1ae8902a1e417f5761aa31dbffd490e Binary files /dev/null and b/software/metabase/test/testdata/backup.pg_dump differ