From b3cb19512e4b186c06bdca612b06a57273e3ff7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Wed, 14 Sep 2022 18:52:11 +0200 Subject: [PATCH 1/2] tests: salt function call and grains in jinja template Regression test for saltstack/salt#62636 and saltstack/salt#61083 --- qubes/tests/integ/salt.py | 88 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/qubes/tests/integ/salt.py b/qubes/tests/integ/salt.py index b473e0741..7b84be149 100644 --- a/qubes/tests/integ/salt.py +++ b/qubes/tests/integ/salt.py @@ -24,6 +24,7 @@ import shutil import subprocess import sys +import unittest import qubes.tests @@ -369,16 +370,39 @@ def test_001_multi_state_highstate(self): f.write('base:\n') f.write(' {}:\n'.format(vmname)) f.write(' - test_salt.{}\n'.format(state)) + # test with some jinja templating + state = 'jinja' + with open(os.path.join(self.salt_testdir, state + '.sls'), 'w') as f: + f.write("{% set ex = salt['cmd.shell']('echo what') %}\n") + f.write("{% if ex.startswith('what') %}\n") + f.write("/home/user/{}:\n".format(state)) + f.write(" file.managed:\n") + f.write(" - contents: |\n") + f.write(" this is test\n") + f.write("{% endif %}\n") + f.write("/home/user/test2:\n") + f.write(" file.managed:\n") + f.write(" - contents: |\n") + f.write(" {{ grains['id'] }}\n") + with open(os.path.join(self.salt_testdir, state + '.top'), 'w') as f: + f.write('base:\n') + f.write(' {}:\n'.format(vmname)) + f.write(' - test_salt.{}\n'.format(state)) self.dom0_salt_call_json(['top.enable', 'test_salt.something']) self.dom0_salt_call_json(['top.enable', 'test_salt.something2']) + self.dom0_salt_call_json(['top.enable', 'test_salt.jinja']) state_output = self.salt_call( ['--skip-dom0', '--show-output', '--targets=' + vmname, 'state.highstate']) expected_output = vmname + ':\n' self.assertTrue(state_output.startswith(expected_output), 'Full output: ' + state_output) - state_output_json = json.loads(state_output[len(expected_output):]) + try: + state_output_json = json.loads(state_output[len(expected_output):]) + except json.decoder.JSONDecodeError as e: + self.fail('{}: {}'.format(e, state_output[len(expected_output):])) + states = states + ('jinja',) for state in states: state_id = \ 'file_|-/home/user/{0}_|-/home/user/{0}_|-managed'.format(state) @@ -394,6 +418,68 @@ def test_001_multi_state_highstate(self): self.assertEqual(stdout, b'this is test\n') self.assertEqual(stderr, b'') + stdout, stderr = self.loop.run_until_complete(self.vm.run_for_stdio( + 'cat /home/user/' + state)) + self.assertEqual(stdout, b'this is test\n') + self.assertEqual(stderr, b'') + + # and finally verify if grain id is correct + stdout, stderr = self.loop.run_until_complete(self.vm.run_for_stdio( + 'cat /home/user/test2')) + self.assertEqual(stdout, '{}\n'.format(self.vm.name).encode()) + self.assertEqual(stderr, b'') + + @unittest.expectedFailure + def test_002_grans_id(self): + vmname = self.make_vm_name('target') + tplname = self.make_vm_name('tpl') + self.test_template = self.app.add_new_vm('TemplateVM', + name=tplname, label='red') + self.test_template.features.update( + self.app.domains[self.template].features) + self.loop.run_until_complete( + self.test_template.clone_disk_files(self.app.domains[self.template])) + self.vm = self.app.add_new_vm('AppVM', name=vmname, label='red', + template=self.test_template) + self.loop.run_until_complete(self.vm.create_on_disk()) + # test with some jinja templating + with open(os.path.join(self.salt_testdir, 'jinja.sls'), 'w') as f: + f.write("/home/user/test2-{{ grains['id'] }}:\n") + f.write(" file.managed:\n") + f.write(" - contents: |\n") + f.write(" {{ grains['id'] }}\n") + with open(os.path.join(self.salt_testdir, 'jinja.top'), 'w') as f: + f.write('base:\n') + f.write(' {}:\n'.format(vmname)) + f.write(' - test_salt.jinja\n') + f.write(' {}:\n'.format(tplname)) + f.write(' - test_salt.jinja\n') + + self.dom0_salt_call_json(['top.enable', 'test_salt.jinja']) + state_output = self.salt_call( + ['--skip-dom0', '--show-output', + '--targets={},{}'.format(tplname, vmname), + 'state.highstate']) + self.assertIn(tplname + ':\n', state_output) + self.assertIn(vmname + ':\n', state_output) + tpl_output, _, appvm_output = state_output.partition(vmname + ':\n') + self.assertTrue(tpl_output.startswith(tplname + ':\n'), + 'Full output: ' + state_output) + tpl_output = tpl_output[len(tplname + ':\n'):] + + for name, output in ((tplname, tpl_output), (vmname, appvm_output)): + try: + state_output_json = json.loads(output) + except json.decoder.JSONDecodeError as e: + self.fail('{}: {}'.format(e, output)) + state_id = \ + 'file_|-/home/user/test2-{0}_|-/home/user/test2-{0}_|-managed'.format(name) + # drop the header + self.assertIn(state_id, state_output_json[name]) + state_output_single = state_output_json[name][state_id] + + self.assertTrue(state_output_single['result']) + self.assertNotEqual(state_output_single['changes'], {}) def create_testcases_for_templates(): return qubes.tests.create_testcases_for_templates('TC_10_VMSalt', From bef1cbe24aebbb177035cf213f4c988f554b7c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Thu, 15 Sep 2022 00:17:17 +0200 Subject: [PATCH 2/2] tests/salt: make sure to use correct dispvm for test default_dispvm is default management_dispvm, but the test system can have non-default set, so set both properties. --- qubes/tests/integ/salt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qubes/tests/integ/salt.py b/qubes/tests/integ/salt.py index 7b84be149..9d69ce854 100644 --- a/qubes/tests/integ/salt.py +++ b/qubes/tests/integ/salt.py @@ -278,6 +278,7 @@ def setUp(self): template_for_dispvms=True, name=dispvm_tpl_name) self.loop.run_until_complete(dispvm_tpl.create_on_disk()) self.app.default_dispvm = dispvm_tpl + self.app.management_dispvm = dispvm_tpl def tearDown(self): self.app.default_dispvm = None