From 028956dd290a8c7c8f66cc692bb847882d17800e Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Wed, 18 Sep 2019 16:14:51 +0200 Subject: [PATCH 01/36] Add initial policyd initial test Add the initial, general, 'success' test for policyd support in charms. --- .../openstack/charm_tests/policyd/__init__.py | 17 ++++ zaza/openstack/charm_tests/policyd/tests.py | 97 +++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 zaza/openstack/charm_tests/policyd/__init__.py create mode 100644 zaza/openstack/charm_tests/policyd/tests.py diff --git a/zaza/openstack/charm_tests/policyd/__init__.py b/zaza/openstack/charm_tests/policyd/__init__.py new file mode 100644 index 0000000..89ec5f1 --- /dev/null +++ b/zaza/openstack/charm_tests/policyd/__init__.py @@ -0,0 +1,17 @@ +# Copyright 2019 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Collection of code for setting up and testing policyd overrides across a +collection of charms. +""" diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py new file mode 100644 index 0000000..17b4369 --- /dev/null +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -0,0 +1,97 @@ +# Copyright 2019 Canonical Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Encapsulate policyd testing.""" + +import logging +import os +import shutil +import tempfile +import zipfile + +import zaza.model as zaza_model + +import zaza.openstack.charm_tests.test_utils as test_utils + + +class PolicydTest(test_utils.OpenStackBaseTest): + """Charm operation tests. + + The policyd test needs some config from the tests.yaml in order to work + properly. A top level key of "tests_options". Under that key is + 'policyd', and then the k:v of 'service': . e.g. for keystone + + tests_options: + policyd: + service: keystone + """ + + def setUpClass(cls, application_name=None): + super().setUpClass(application_name) + cls._tmp_dir = tempfile.mkdtemp() + cls._service_name = \ + cls.test_config['tests_options']['policyd']['service'] + + def tearDownClass(cls): + super().tearDownClass() + try: + shutil.rmtree(cls._tmp_dir, ignore_errors=True) + except Exception as e: + logging.error("Removing the policyd tempdir/files failed: {}" + .format(str(e))) + + def tearDown(self): + """Ensure that the policyd config is switched off and the charm is + stable at the end of the test. + """ + self._set_config_and_wait(False) + + def _set_config_and_wait(self, state): + zaza_model.set_application_config(self.application_name, + {"use-policyd-override", state}) + zaza_model.block_until_all_units_idle() + + def _make_zip_file_from(self, name, files): + """Make a zip file from a dictionary of filename: string. + + :param name: the name of the zip file + :type name: PathLike + :param files: a dict of name: string to construct the files from. + :type files: Dict[str, str] + :returns: temp file that is the zip file. + :rtype: PathLike + """ + path = os.path.join(self._tmp_dir, name) + with zipfile.ZipFile(path, "w") as zfp: + for name, contents in files.items(): + zfp.writestr(name, contents) + + def test_policyd_good_yaml(self): + # Test that the policyd with a good zipped yaml file puts the yaml file + # in the right directory + good = { + 'file1.yaml': "{'rule1': '!'}" + } + good_zip_path = self._make_zip_file_from('good.zip', good) + zaza_model.attach_resource(self.application_name, + 'policyd-override', + good_zip_path) + zaza_model.block_until_all_units_idle() + self._set_config_and_wait(True) + # check that the file gets to the right location + path = os.path.join( + "/etc", self._service_name, "policy.d", 'file1.yaml') + zaza_model.block_until_file_has_contents(self.application_name, + path, + "'rule1': '!'") From 5e2fbf96399e395ed397305e560567142235a462 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Wed, 18 Sep 2019 16:49:18 +0200 Subject: [PATCH 02/36] Fix class super calls --- zaza/openstack/charm_tests/policyd/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 17b4369..0131b65 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -38,13 +38,13 @@ class PolicydTest(test_utils.OpenStackBaseTest): """ def setUpClass(cls, application_name=None): - super().setUpClass(application_name) + super(PolicydTest, cls).setUpClass(application_name) cls._tmp_dir = tempfile.mkdtemp() cls._service_name = \ cls.test_config['tests_options']['policyd']['service'] def tearDownClass(cls): - super().tearDownClass() + super(PolicydTest, cls).tearDownClass() try: shutil.rmtree(cls._tmp_dir, ignore_errors=True) except Exception as e: From 677de69b9a95bd323468df03718b0d41564e286f Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Wed, 18 Sep 2019 16:54:31 +0200 Subject: [PATCH 03/36] More fixes to super calls --- zaza/openstack/charm_tests/policyd/tests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 0131b65..bff1b5c 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -37,12 +37,14 @@ class PolicydTest(test_utils.OpenStackBaseTest): service: keystone """ + @classmethod def setUpClass(cls, application_name=None): super(PolicydTest, cls).setUpClass(application_name) cls._tmp_dir = tempfile.mkdtemp() cls._service_name = \ cls.test_config['tests_options']['policyd']['service'] + @classmethod def tearDownClass(cls): super(PolicydTest, cls).tearDownClass() try: From 99db1e3eab85d589dcec6bdb31ca5c98eababe0d Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Wed, 18 Sep 2019 16:59:22 +0200 Subject: [PATCH 04/36] Fix test with None path --- zaza/openstack/charm_tests/policyd/tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index bff1b5c..802453c 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -78,6 +78,7 @@ class PolicydTest(test_utils.OpenStackBaseTest): with zipfile.ZipFile(path, "w") as zfp: for name, contents in files.items(): zfp.writestr(name, contents) + return path def test_policyd_good_yaml(self): # Test that the policyd with a good zipped yaml file puts the yaml file From 8aa4102855754a0f90a234e0ee4fdcd20e5d1644 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Wed, 18 Sep 2019 17:01:44 +0200 Subject: [PATCH 05/36] Fix config setting code --- zaza/openstack/charm_tests/policyd/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 802453c..22c1977 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -61,7 +61,7 @@ class PolicydTest(test_utils.OpenStackBaseTest): def _set_config_and_wait(self, state): zaza_model.set_application_config(self.application_name, - {"use-policyd-override", state}) + {"use-policyd-override": state}) zaza_model.block_until_all_units_idle() def _make_zip_file_from(self, name, files): From 89980f1cc539c40ebcef2575affd5ab01e1d6709 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 09:11:53 +0200 Subject: [PATCH 06/36] Fix using bools with libjuju --- zaza/openstack/charm_tests/policyd/tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 22c1977..7fd0c9e 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -60,8 +60,9 @@ class PolicydTest(test_utils.OpenStackBaseTest): self._set_config_and_wait(False) def _set_config_and_wait(self, state): + s = "true" if state else "false" zaza_model.set_application_config(self.application_name, - {"use-policyd-override": state}) + {"use-policyd-override": s}) zaza_model.block_until_all_units_idle() def _make_zip_file_from(self, name, files): From 214660f43d7892495c487dab71c7094a216546d5 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 10:04:27 +0200 Subject: [PATCH 07/36] Added additional logging --- zaza/openstack/charm_tests/policyd/tests.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 7fd0c9e..5657c74 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -88,14 +88,19 @@ class PolicydTest(test_utils.OpenStackBaseTest): 'file1.yaml': "{'rule1': '!'}" } good_zip_path = self._make_zip_file_from('good.zip', good) + logging.info("About to attach the resource") zaza_model.attach_resource(self.application_name, 'policyd-override', good_zip_path) + logging.info("... waiting for idle") zaza_model.block_until_all_units_idle() + logging.info("Now setting config to true") self._set_config_and_wait(True) # check that the file gets to the right location path = os.path.join( "/etc", self._service_name, "policy.d", 'file1.yaml') + logging.info("Now checking for file contents: {}".format(path)) zaza_model.block_until_file_has_contents(self.application_name, path, "'rule1': '!'") + logging.info("...done") From 6306615a1777b59b1bd7b269c04a1b5a82319e77 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 11:55:40 +0200 Subject: [PATCH 08/36] More debugging in the test --- tox.ini | 4 ++-- zaza/openstack/charm_tests/policyd/tests.py | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/tox.ini b/tox.ini index 2447271..6a37cfe 100644 --- a/tox.ini +++ b/tox.ini @@ -25,7 +25,7 @@ deps = -r{toxinidir}/requirements.txt commands = /bin/true [flake8] -ignore = E402,E226 +ignore = E402,E226,W504 per-file-ignores = unit_tests/**: D @@ -34,4 +34,4 @@ basepython = python3 changedir = doc/source deps = -r{toxinidir}/requirements.txt -commands = sphinx-build -W -b html -d {toxinidir}/doc/build/doctrees . {toxinidir}/doc/build/html \ No newline at end of file +commands = sphinx-build -W -b html -d {toxinidir}/doc/build/doctrees . {toxinidir}/doc/build/html diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 5657c74..9db2611 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -23,6 +23,7 @@ import zipfile import zaza.model as zaza_model import zaza.openstack.charm_tests.test_utils as test_utils +import zaza.openstack.utilities.openstack as openstack_utils class PolicydTest(test_utils.OpenStackBaseTest): @@ -40,6 +41,9 @@ class PolicydTest(test_utils.OpenStackBaseTest): @classmethod def setUpClass(cls, application_name=None): super(PolicydTest, cls).setUpClass(application_name) + if (openstack_utils.get_os_release() < + openstack_utils.get_os_release('xenial_queens')): + cls.SkipTest("Test not valid before xenial_queens") cls._tmp_dir = tempfile.mkdtemp() cls._service_name = \ cls.test_config['tests_options']['policyd']['service'] @@ -60,9 +64,10 @@ class PolicydTest(test_utils.OpenStackBaseTest): self._set_config_and_wait(False) def _set_config_and_wait(self, state): - s = "true" if state else "false" - zaza_model.set_application_config(self.application_name, - {"use-policyd-override": s}) + s = "True" if state else "False" + config = {"user-policyd-override": s} + logging.info("Setting config to", config) + zaza_model.set_application_config(self.application_name, config) zaza_model.block_until_all_units_idle() def _make_zip_file_from(self, name, files): @@ -102,5 +107,13 @@ class PolicydTest(test_utils.OpenStackBaseTest): logging.info("Now checking for file contents: {}".format(path)) zaza_model.block_until_file_has_contents(self.application_name, path, - "'rule1': '!'") + "rule1: '!'") + logging.info("... waiting for idle") + zaza_model.block_until_all_units_idle() + # check that the status includes "PO:" at the beginning of the status + # line + + # disable the policy override + # verify that the file no longer exists + # check that the status no longer has "PO:" on it. logging.info("...done") From f5f3c9a966f1f65949b3f7f098a8c2fda9328d7d Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 12:07:23 +0200 Subject: [PATCH 09/36] Yet more fixes to the test --- zaza/openstack/charm_tests/policyd/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 9db2611..49d03a9 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -65,7 +65,7 @@ class PolicydTest(test_utils.OpenStackBaseTest): def _set_config_and_wait(self, state): s = "True" if state else "False" - config = {"user-policyd-override": s} + config = {"use-policyd-override": s} logging.info("Setting config to", config) zaza_model.set_application_config(self.application_name, config) zaza_model.block_until_all_units_idle() From 5ba0cf211cf87475ad95e32b1941ea217f1ef8c8 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 12:14:05 +0200 Subject: [PATCH 10/36] Yet more fixes to the test1 --- zaza/openstack/charm_tests/policyd/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 49d03a9..9e92d96 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -66,7 +66,7 @@ class PolicydTest(test_utils.OpenStackBaseTest): def _set_config_and_wait(self, state): s = "True" if state else "False" config = {"use-policyd-override": s} - logging.info("Setting config to", config) + logging.info("Setting config to {}".format(config)) zaza_model.set_application_config(self.application_name, config) zaza_model.block_until_all_units_idle() From ce8c72e1a18620684ab4aeda441b56c017383980 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 12:24:35 +0200 Subject: [PATCH 11/36] tests to determine app status --- zaza/openstack/charm_tests/policyd/tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 9e92d96..bad21d7 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -21,6 +21,7 @@ import tempfile import zipfile import zaza.model as zaza_model +import zaza.utilities.juju as zaza_juju import zaza.openstack.charm_tests.test_utils as test_utils import zaza.openstack.utilities.openstack as openstack_utils @@ -112,6 +113,8 @@ class PolicydTest(test_utils.OpenStackBaseTest): zaza_model.block_until_all_units_idle() # check that the status includes "PO:" at the beginning of the status # line + app_status = zaza_juju.get_application_status(self.application_name) + logging.info("App status is: {}".format(app_status)) # disable the policy override # verify that the file no longer exists From 2beddeafefe38e768ce19e7d29007b6ec403e4c2 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 13:52:34 +0200 Subject: [PATCH 12/36] more iteration to discover workload stats --- zaza/openstack/charm_tests/policyd/tests.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index bad21d7..dd418e5 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -114,7 +114,10 @@ class PolicydTest(test_utils.OpenStackBaseTest): # check that the status includes "PO:" at the beginning of the status # line app_status = zaza_juju.get_application_status(self.application_name) - logging.info("App status is: {}".format(app_status)) + wl_stats = [v['workload-status']['info'] + for k, v in app_status['units'].items() + if k.split('/')[0] == self.application_name] + logging.info("App status is: {}".format(wl_stats)) # disable the policy override # verify that the file no longer exists From aae3366fc9aececf81e0e8d3c97036ec7ec89097 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 14:14:00 +0200 Subject: [PATCH 13/36] Add further disable tests --- zaza/openstack/charm_tests/policyd/tests.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index dd418e5..2dee9bb 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -118,8 +118,20 @@ class PolicydTest(test_utils.OpenStackBaseTest): for k, v in app_status['units'].items() if k.split('/')[0] == self.application_name] logging.info("App status is: {}".format(wl_stats)) + self.assertTrue(all(s.startswith("PO:") for s in wl_stats)) + logging.info("App status is valid") # disable the policy override - # verify that the file no longer exists + logging.info("Disabling policy override ...") + self._set_config_and_wait(False) # check that the status no longer has "PO:" on it. + app_status = zaza_juju.get_application_status(self.application_name) + wl_stats = [v['workload-status']['info'] + for k, v in app_status['units'].items() + if k.split('/')[0] == self.application_name] + self.assertFalse(any(s.startswith("PO:") for s in wl_stats)) + logging.info("... done") + + # verify that the file no longer exists logging.info("...done") + From 67f1650917ed8d16e16f7818d553d0949052eee3 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 14:23:19 +0200 Subject: [PATCH 14/36] Add further disable tests1 --- zaza/openstack/charm_tests/policyd/tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 2dee9bb..55736a5 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -129,6 +129,7 @@ class PolicydTest(test_utils.OpenStackBaseTest): wl_stats = [v['workload-status']['info'] for k, v in app_status['units'].items() if k.split('/')[0] == self.application_name] + logging.info("App status is: {}".format(wl_stats)) self.assertFalse(any(s.startswith("PO:") for s in wl_stats)) logging.info("... done") From 1110ced7c17c45ff8ffb2578c207e0b8d12d2dcb Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 14:34:08 +0200 Subject: [PATCH 15/36] More test debugging --- zaza/openstack/charm_tests/policyd/tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 55736a5..bb6fa4a 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -124,12 +124,13 @@ class PolicydTest(test_utils.OpenStackBaseTest): # disable the policy override logging.info("Disabling policy override ...") self._set_config_and_wait(False) + logging.info("Done setting false?") # check that the status no longer has "PO:" on it. app_status = zaza_juju.get_application_status(self.application_name) wl_stats = [v['workload-status']['info'] for k, v in app_status['units'].items() if k.split('/')[0] == self.application_name] - logging.info("App status is: {}".format(wl_stats)) + logging.info("After App status is: {}".format(wl_stats)) self.assertFalse(any(s.startswith("PO:") for s in wl_stats)) logging.info("... done") From 89a6d9ca39b52744ea3aad5f239c51ee98829dcc Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 14:56:43 +0200 Subject: [PATCH 16/36] More test debugging again --- zaza/openstack/charm_tests/policyd/tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index bb6fa4a..b7d7f64 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -62,6 +62,7 @@ class PolicydTest(test_utils.OpenStackBaseTest): """Ensure that the policyd config is switched off and the charm is stable at the end of the test. """ + logging.info("tearDown") self._set_config_and_wait(False) def _set_config_and_wait(self, state): From 36f775390cca5aff530458d229dc898a9441124d Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 15:40:35 +0200 Subject: [PATCH 17/36] change approach to waiting on results --- zaza/openstack/charm_tests/policyd/tests.py | 72 ++++++++++++--------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index b7d7f64..f1abb00 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -20,6 +20,7 @@ import shutil import tempfile import zipfile +import zaza import zaza.model as zaza_model import zaza.utilities.juju as zaza_juju @@ -58,19 +59,11 @@ class PolicydTest(test_utils.OpenStackBaseTest): logging.error("Removing the policyd tempdir/files failed: {}" .format(str(e))) - def tearDown(self): - """Ensure that the policyd config is switched off and the charm is - stable at the end of the test. - """ - logging.info("tearDown") - self._set_config_and_wait(False) - - def _set_config_and_wait(self, state): + def _set_config(self, state): s = "True" if state else "False" config = {"use-policyd-override": s} logging.info("Setting config to {}".format(config)) zaza_model.set_application_config(self.application_name, config) - zaza_model.block_until_all_units_idle() def _make_zip_file_from(self, name, files): """Make a zip file from a dictionary of filename: string. @@ -102,7 +95,7 @@ class PolicydTest(test_utils.OpenStackBaseTest): logging.info("... waiting for idle") zaza_model.block_until_all_units_idle() logging.info("Now setting config to true") - self._set_config_and_wait(True) + self._set_config(True) # check that the file gets to the right location path = os.path.join( "/etc", self._service_name, "policy.d", 'file1.yaml') @@ -110,31 +103,52 @@ class PolicydTest(test_utils.OpenStackBaseTest): zaza_model.block_until_file_has_contents(self.application_name, path, "rule1: '!'") - logging.info("... waiting for idle") - zaza_model.block_until_all_units_idle() - # check that the status includes "PO:" at the beginning of the status - # line - app_status = zaza_juju.get_application_status(self.application_name) - wl_stats = [v['workload-status']['info'] - for k, v in app_status['units'].items() - if k.split('/')[0] == self.application_name] - logging.info("App status is: {}".format(wl_stats)) - self.assertTrue(all(s.startswith("PO:") for s in wl_stats)) + # ensure that the workload status info line starts with PO: + logging.info("Checking for workload status line starts with PO:") + block_until_wl_status_info_starts_with(self.application_name, "PO:") logging.info("App status is valid") # disable the policy override logging.info("Disabling policy override ...") - self._set_config_and_wait(False) - logging.info("Done setting false?") + self._set_config(False) # check that the status no longer has "PO:" on it. - app_status = zaza_juju.get_application_status(self.application_name) - wl_stats = [v['workload-status']['info'] - for k, v in app_status['units'].items() - if k.split('/')[0] == self.application_name] - logging.info("After App status is: {}".format(wl_stats)) - self.assertFalse(any(s.startswith("PO:") for s in wl_stats)) - logging.info("... done") + block_until_wl_status_info_starts_with( + self.application_name, "PO:", negate_match=True) # verify that the file no longer exists logging.info("...done") + +async def async_block_until_wl_status_info_starts_with( + app, status, model_name=None, negate_match=False, timeout=2700): + """Block until the all the units have a desired workload status that starts + with status. + + :param app: the application to check against + :type app: str + :param status: Status to wait for at the start of the string + :type status: str + :param model_name: Name of model to query. + :type model_name: Union[None, str] + :param negate_match: Wait until the match is not true; i.e. none match + :type negate_match: Union[None, bool] + :param timeout: Time to wait for unit to achieved desired status + :type timeout: float + """ + async def _unit_status(): + model_status = await zaza_model.async_get_status() + wl_infos = [v['workload-status']['info'] + for k, v in model_status.applications[app]['units'] + if k.split('/')[0] == app] + g = (s.startswith(status) for s in wl_infos) + if negate_match: + return not(any(g)) + else: + return all(g) + + async with zaza_model.run_in_model(model_name): + await zaza_model.async_block_until(_unit_status, timeout=timeout) + + +block_until_wl_status_info_starts_with = zaza.sync_wrapper( + async_block_until_wl_status_info_starts_with) From 3465dacb96331e7a688660e39f0526319fe33a31 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 15:49:14 +0200 Subject: [PATCH 18/36] change approach to waiting on results bug fix --- zaza/openstack/charm_tests/policyd/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index f1abb00..d42b4b7 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -138,7 +138,7 @@ async def async_block_until_wl_status_info_starts_with( async def _unit_status(): model_status = await zaza_model.async_get_status() wl_infos = [v['workload-status']['info'] - for k, v in model_status.applications[app]['units'] + for k, v in model_status.applications[app]['units'].items() if k.split('/')[0] == app] g = (s.startswith(status) for s in wl_infos) if negate_match: From 55ae4aad281c5b8267b912528a0079560fc2ecde Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 16:17:50 +0200 Subject: [PATCH 19/36] Add in check to test for removal of file --- zaza/openstack/charm_tests/policyd/tests.py | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index d42b4b7..abf997f 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -20,6 +20,8 @@ import shutil import tempfile import zipfile +from juju.errors import JujuError + import zaza import zaza.model as zaza_model import zaza.utilities.juju as zaza_juju @@ -112,10 +114,21 @@ class PolicydTest(test_utils.OpenStackBaseTest): logging.info("Disabling policy override ...") self._set_config(False) # check that the status no longer has "PO:" on it. + # we have to do it twice due to async races and that some info lines + # erase the PO: bit prior to actuall getting back to idle. The double + # check verifies that the charms have started, the idle waits until it + # is finiehed, and then the final check really makes sure they got + # switched off. + block_until_wl_status_info_starts_with( + self.application_name, "PO:", negate_match=True) + zaza_model.block_until_all_units_idle() block_until_wl_status_info_starts_with( self.application_name, "PO:", negate_match=True) # verify that the file no longer exists + logging.info("Checking that {} has been removed".format(path)) + block_until_file_missing(self.application_name, path) + logging.info("...done") @@ -152,3 +165,25 @@ async def async_block_until_wl_status_info_starts_with( block_until_wl_status_info_starts_with = zaza.sync_wrapper( async_block_until_wl_status_info_starts_with) + + +async def async_block_until_file_missing( + app, path, model_name=None, timeout=2700): + async def _check_for_file(model): + units = model.applications[app].units + results = [] + for unit in units: + try: + output = await unit.run('test -e {}; echo $?'.format(path)) + contents = output.data.get('results')['Stdout'] + results.append("1" in contents) + # libjuju throws a generic error for connection failure. So we + # cannot differentiate between a connectivity issue and a + # target file not existing error. For now just assume the + # latter. + except JujuError: + results.append(False) + return all(results) + + +block_until_file_missing = zaza.sync_wrapper(async_block_until_file_missing) From 024a4b0ff7e2cbb44256c82ab3d475ef0cc9f9d7 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Thu, 19 Sep 2019 17:26:29 +0200 Subject: [PATCH 20/36] move async code to zaza --- zaza/openstack/charm_tests/policyd/tests.py | 66 ++------------------- 1 file changed, 5 insertions(+), 61 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index abf997f..f92d633 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -107,7 +107,8 @@ class PolicydTest(test_utils.OpenStackBaseTest): "rule1: '!'") # ensure that the workload status info line starts with PO: logging.info("Checking for workload status line starts with PO:") - block_until_wl_status_info_starts_with(self.application_name, "PO:") + zaza_model.block_until_wl_status_info_starts_with( + self.application_name, "PO:") logging.info("App status is valid") # disable the policy override @@ -119,71 +120,14 @@ class PolicydTest(test_utils.OpenStackBaseTest): # check verifies that the charms have started, the idle waits until it # is finiehed, and then the final check really makes sure they got # switched off. - block_until_wl_status_info_starts_with( + zaza_model.block_until_wl_status_info_starts_with( self.application_name, "PO:", negate_match=True) zaza_model.block_until_all_units_idle() - block_until_wl_status_info_starts_with( + zaza_model.block_until_wl_status_info_starts_with( self.application_name, "PO:", negate_match=True) # verify that the file no longer exists logging.info("Checking that {} has been removed".format(path)) - block_until_file_missing(self.application_name, path) + zaza_model.block_until_file_missing(self.application_name, path) logging.info("...done") - - -async def async_block_until_wl_status_info_starts_with( - app, status, model_name=None, negate_match=False, timeout=2700): - """Block until the all the units have a desired workload status that starts - with status. - - :param app: the application to check against - :type app: str - :param status: Status to wait for at the start of the string - :type status: str - :param model_name: Name of model to query. - :type model_name: Union[None, str] - :param negate_match: Wait until the match is not true; i.e. none match - :type negate_match: Union[None, bool] - :param timeout: Time to wait for unit to achieved desired status - :type timeout: float - """ - async def _unit_status(): - model_status = await zaza_model.async_get_status() - wl_infos = [v['workload-status']['info'] - for k, v in model_status.applications[app]['units'].items() - if k.split('/')[0] == app] - g = (s.startswith(status) for s in wl_infos) - if negate_match: - return not(any(g)) - else: - return all(g) - - async with zaza_model.run_in_model(model_name): - await zaza_model.async_block_until(_unit_status, timeout=timeout) - - -block_until_wl_status_info_starts_with = zaza.sync_wrapper( - async_block_until_wl_status_info_starts_with) - - -async def async_block_until_file_missing( - app, path, model_name=None, timeout=2700): - async def _check_for_file(model): - units = model.applications[app].units - results = [] - for unit in units: - try: - output = await unit.run('test -e {}; echo $?'.format(path)) - contents = output.data.get('results')['Stdout'] - results.append("1" in contents) - # libjuju throws a generic error for connection failure. So we - # cannot differentiate between a connectivity issue and a - # target file not existing error. For now just assume the - # latter. - except JujuError: - results.append(False) - return all(results) - - -block_until_file_missing = zaza.sync_wrapper(async_block_until_file_missing) From c744b1a288f3fae05621e20857f1d385277dbd96 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Fri, 20 Sep 2019 14:00:19 +0200 Subject: [PATCH 21/36] Create generic / keystone test idea --- zaza/openstack/charm_tests/policyd/tests.py | 26 +++++++++++++-------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index f92d633..8bdacbe 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -20,17 +20,13 @@ import shutil import tempfile import zipfile -from juju.errors import JujuError - -import zaza import zaza.model as zaza_model -import zaza.utilities.juju as zaza_juju import zaza.openstack.charm_tests.test_utils as test_utils import zaza.openstack.utilities.openstack as openstack_utils -class PolicydTest(test_utils.OpenStackBaseTest): +class PolicydTest(object): """Charm operation tests. The policyd test needs some config from the tests.yaml in order to work @@ -90,13 +86,13 @@ class PolicydTest(test_utils.OpenStackBaseTest): 'file1.yaml': "{'rule1': '!'}" } good_zip_path = self._make_zip_file_from('good.zip', good) - logging.info("About to attach the resource") + logging.info("Attaching a resource.") zaza_model.attach_resource(self.application_name, 'policyd-override', good_zip_path) - logging.info("... waiting for idle") + logging.debug("... waiting for idle") zaza_model.block_until_all_units_idle() - logging.info("Now setting config to true") + logging.debug("Now setting config to true") self._set_config(True) # check that the file gets to the right location path = os.path.join( @@ -109,10 +105,10 @@ class PolicydTest(test_utils.OpenStackBaseTest): logging.info("Checking for workload status line starts with PO:") zaza_model.block_until_wl_status_info_starts_with( self.application_name, "PO:") - logging.info("App status is valid") + logging.debug("App status is valid") # disable the policy override - logging.info("Disabling policy override ...") + logging.info("Disabling policy override by setting config to false") self._set_config(False) # check that the status no longer has "PO:" on it. # we have to do it twice due to async races and that some info lines @@ -131,3 +127,13 @@ class PolicydTest(test_utils.OpenStackBaseTest): zaza_model.block_until_file_missing(self.application_name, path) logging.info("...done") + + +class KeystonePolicydTest(PolicydTest, test_utils.OpenStackBaseTest): + pass + + +class GenericPolicydTest(PolicydTest, test_utils.OpenStackBaseTest): + pass + + From 1337531926592439c6dbc5462d9c3ad00f802834 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Fri, 20 Sep 2019 14:51:57 +0200 Subject: [PATCH 22/36] Create specific policyd override for keystone --- zaza/openstack/charm_tests/policyd/tests.py | 49 +++++++++++++++++++-- zaza/openstack/utilities/exceptions.py | 6 +++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 8bdacbe..551e188 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -20,10 +20,14 @@ import shutil import tempfile import zipfile +import keystoneauth1 + import zaza.model as zaza_model import zaza.openstack.charm_tests.test_utils as test_utils import zaza.openstack.utilities.openstack as openstack_utils +import zaza.openstack.charm_tests.keystone as ch_keystone +import zaza.openstack.utilities.exceptions as zaza_exceptions class PolicydTest(object): @@ -79,6 +83,15 @@ class PolicydTest(object): zfp.writestr(name, contents) return path + def _set_policy_with(self, rules): + rules_zip_path = self._make_zip_file_from('rules.zip', rules) + zaza_model.attach_resource(self.application_name, + 'policyd-override', + rules_zip_path) + self._set_config(True) + zaza_model.block_until_wl_status_info_starts_with( + self.application_name, "PO:", negate_match=True) + def test_policyd_good_yaml(self): # Test that the policyd with a good zipped yaml file puts the yaml file # in the right directory @@ -129,11 +142,39 @@ class PolicydTest(object): logging.info("...done") -class KeystonePolicydTest(PolicydTest, test_utils.OpenStackBaseTest): - pass +class KeystonePolicydTest(PolicydTest, + ch_keystone.BaseKeystoneTest, + test_utils.OpenStackBaseTest): + + def test_disable_service(self): + self._set_policy_with({"identity:get_auth_domains": "!"}) + with self.config_change( + {'preferred-api-version': self.default_api_version}, + {'preferred-api-version': '3'}, + application_name="keystone"): + for ip in self.keystone_ips: + try: + logging.info('keystone IP {}'.format(ip)) + ks_session = openstack_utils.get_keystone_session( + openstack_utils.get_overcloud_auth(address=ip)) + ks_client = openstack_utils.get_keystone_session_client( + ks_session) + ks_client.domains.list() + raise zaza_exceptions.PolicydError( + 'Retrieve domain list as admin with project scoped ' + 'token passed and should have failed. IP = {}' + .format(ip)) + except keystoneauth1.exceptions.http.Forbidden: + logging.info("keystone IP:{} policyd override working" + .format(ip)) + finally: + self._set_config(False) + zaza_model.block_until_wl_status_info_starts_with( + self.application_name, "PO:", negate_match=True) + zaza_model.block_until_all_units_idle() + + logging.info('OK') class GenericPolicydTest(PolicydTest, test_utils.OpenStackBaseTest): pass - - diff --git a/zaza/openstack/utilities/exceptions.py b/zaza/openstack/utilities/exceptions.py index 868a7c6..28ff095 100644 --- a/zaza/openstack/utilities/exceptions.py +++ b/zaza/openstack/utilities/exceptions.py @@ -172,3 +172,9 @@ class NovaGuestRestartFailed(Exception): """Nova guest restart failed.""" pass + + +class PolicydError(Exception): + """Policyd override failed.""" + + pass From ae6b1d78497487c74122c63ed143fe61a3544b75 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Fri, 20 Sep 2019 14:56:19 +0200 Subject: [PATCH 23/36] Try to fix mixin class set up bug --- zaza/openstack/charm_tests/policyd/tests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 551e188..2b85c97 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -146,6 +146,10 @@ class KeystonePolicydTest(PolicydTest, ch_keystone.BaseKeystoneTest, test_utils.OpenStackBaseTest): + @classmethod + def setUpClass(cls, application_name=None): + super(KeystonePolicydTest, cls).setUpClass(application_name) + def test_disable_service(self): self._set_policy_with({"identity:get_auth_domains": "!"}) with self.config_change( From b1e6beb919699d2d9f031136695bd788fa019bcf Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Fri, 20 Sep 2019 15:18:10 +0200 Subject: [PATCH 24/36] Further work to get keystone specific test to work --- zaza/openstack/charm_tests/keystone/__init__.py | 2 +- zaza/openstack/charm_tests/policyd/tests.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/zaza/openstack/charm_tests/keystone/__init__.py b/zaza/openstack/charm_tests/keystone/__init__.py index 2f6dd1f..e244e16 100644 --- a/zaza/openstack/charm_tests/keystone/__init__.py +++ b/zaza/openstack/charm_tests/keystone/__init__.py @@ -30,7 +30,7 @@ class BaseKeystoneTest(test_utils.OpenStackBaseTest): """Base for Keystone charm tests.""" @classmethod - def setUpClass(cls): + def setUpClass(cls, application_name=None): """Run class setup for running Keystone charm operation tests.""" super(BaseKeystoneTest, cls).setUpClass(application_name='keystone') # Check if we are related to Vault TLS certificates diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 2b85c97..5605bc0 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -92,7 +92,7 @@ class PolicydTest(object): zaza_model.block_until_wl_status_info_starts_with( self.application_name, "PO:", negate_match=True) - def test_policyd_good_yaml(self): + def test_001_policyd_good_yaml(self): # Test that the policyd with a good zipped yaml file puts the yaml file # in the right directory good = { @@ -151,7 +151,9 @@ class KeystonePolicydTest(PolicydTest, super(KeystonePolicydTest, cls).setUpClass(application_name) def test_disable_service(self): - self._set_policy_with({"identity:get_auth_domains": "!"}) + logging.info("Doing policyd override to disable listing domains") + self._set_policy_with( + {'rule.yaml': "{'identity:get_auth_domains': '!'}"}) with self.config_change( {'preferred-api-version': self.default_api_version}, {'preferred-api-version': '3'}, From 61f7cce1d0e6636f5a793a08ef2d5e64855fb11e Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Mon, 23 Sep 2019 16:12:47 +0100 Subject: [PATCH 25/36] Fix keystone specific test --- zaza/openstack/charm_tests/policyd/tests.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 5605bc0..d2cf768 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -155,8 +155,10 @@ class KeystonePolicydTest(PolicydTest, self._set_policy_with( {'rule.yaml': "{'identity:get_auth_domains': '!'}"}) with self.config_change( - {'preferred-api-version': self.default_api_version}, - {'preferred-api-version': '3'}, + {'preferred-api-version': self.default_api_version, + 'use-policyd-override': 'False'}, + {'preferred-api-version': '3', + 'use-policyd-override': 'True'}, application_name="keystone"): for ip in self.keystone_ips: try: From 8a42af4e417e693b6bac31b84be660267a070433 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Mon, 23 Sep 2019 16:35:18 +0100 Subject: [PATCH 26/36] Fix keystone specific test - more --- zaza/openstack/charm_tests/policyd/tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index d2cf768..ef09196 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -176,6 +176,7 @@ class KeystonePolicydTest(PolicydTest, logging.info("keystone IP:{} policyd override working" .format(ip)) finally: + return self._set_config(False) zaza_model.block_until_wl_status_info_starts_with( self.application_name, "PO:", negate_match=True) From 6a0867e2233e036b6e80f0712cf9482f9e0c081f Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Mon, 23 Sep 2019 16:47:42 +0100 Subject: [PATCH 27/36] Fixed keystone spedific test --- zaza/openstack/charm_tests/policyd/tests.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index ef09196..6b6194a 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -175,12 +175,6 @@ class KeystonePolicydTest(PolicydTest, except keystoneauth1.exceptions.http.Forbidden: logging.info("keystone IP:{} policyd override working" .format(ip)) - finally: - return - self._set_config(False) - zaza_model.block_until_wl_status_info_starts_with( - self.application_name, "PO:", negate_match=True) - zaza_model.block_until_all_units_idle() logging.info('OK') From bdd225a1ee25153e6db39c75a04d991e1dcb5e77 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Tue, 24 Sep 2019 16:10:16 +0100 Subject: [PATCH 28/36] Switch keystone user to demo and try to list projects instead --- zaza/openstack/charm_tests/policyd/tests.py | 28 ++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 6b6194a..7965ca3 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -153,21 +153,37 @@ class KeystonePolicydTest(PolicydTest, def test_disable_service(self): logging.info("Doing policyd override to disable listing domains") self._set_policy_with( - {'rule.yaml': "{'identity:get_auth_domains': '!'}"}) + {'rule.yaml': "{'identity:list_projects': '!'}"}) with self.config_change( {'preferred-api-version': self.default_api_version, 'use-policyd-override': 'False'}, {'preferred-api-version': '3', 'use-policyd-override': 'True'}, application_name="keystone"): + zaza_model.block_until_all_units_idle() for ip in self.keystone_ips: try: logging.info('keystone IP {}'.format(ip)) - ks_session = openstack_utils.get_keystone_session( - openstack_utils.get_overcloud_auth(address=ip)) - ks_client = openstack_utils.get_keystone_session_client( - ks_session) - ks_client.domains.list() + openrc = { + 'API_VERSION': 3, + 'OS_USERNAME': DEMO_ADMIN_USER, + 'OS_PASSWORD': DEMO_ADMIN_USER_PASSWORD, + 'OS_AUTH_URL': 'http://{}:5000/v3'.format(ip), + 'OS_USER_DOMAIN_NAME': DEMO_DOMAIN, + 'OS_DOMAIN_NAME': DEMO_DOMAIN, + } + if self.tls_rid: + openrc['OS_CACERT'] = \ + openstack_utils.KEYSTONE_LOCAL_CACERT + openrc['OS_AUTH_URL'] = ( + openrc['OS_AUTH_URL'].replace('http', 'https')) + logging.info('keystone IP {}'.format(ip)) + keystone_session = openstack_utils.get_keystone_session( + openrc, scope='DOMAIN') + keystone_client = ( + openstack_utils.get_keystone_session_client( + keystone_session)) + keystone_client.projects.list() raise zaza_exceptions.PolicydError( 'Retrieve domain list as admin with project scoped ' 'token passed and should have failed. IP = {}' From d4568caf93aba659746d7585cff5e1686baaf491 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Tue, 24 Sep 2019 17:15:40 +0100 Subject: [PATCH 29/36] Fix missing prefixes from other module --- zaza/openstack/charm_tests/policyd/tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 7965ca3..b4855dd 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -166,11 +166,11 @@ class KeystonePolicydTest(PolicydTest, logging.info('keystone IP {}'.format(ip)) openrc = { 'API_VERSION': 3, - 'OS_USERNAME': DEMO_ADMIN_USER, - 'OS_PASSWORD': DEMO_ADMIN_USER_PASSWORD, + 'OS_USERNAME': ch_keystone.DEMO_ADMIN_USER, + 'OS_PASSWORD': ch_keystone.DEMO_ADMIN_USER_PASSWORD, 'OS_AUTH_URL': 'http://{}:5000/v3'.format(ip), - 'OS_USER_DOMAIN_NAME': DEMO_DOMAIN, - 'OS_DOMAIN_NAME': DEMO_DOMAIN, + 'OS_USER_DOMAIN_NAME': ch_keystone.DEMO_DOMAIN, + 'OS_DOMAIN_NAME': ch_keystone.DEMO_DOMAIN, } if self.tls_rid: openrc['OS_CACERT'] = \ From 9d4008c9f47b6f435951c46d1c853cbf0bb3e26d Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Tue, 24 Sep 2019 17:59:15 +0100 Subject: [PATCH 30/36] More robust test --- zaza/openstack/charm_tests/policyd/tests.py | 43 ++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index b4855dd..b7de295 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -154,6 +154,47 @@ class KeystonePolicydTest(PolicydTest, logging.info("Doing policyd override to disable listing domains") self._set_policy_with( {'rule.yaml': "{'identity:list_projects': '!'}"}) + + # verify (with the config off) that we can actually access + # these points + with self.config_change( + {'preferred-api-version': self.default_api_version}, + {'preferred-api-version': '3'}, + application_name="keystone"): + zaza_model.block_until_all_units_idle() + for ip in self.keystone_ips: + try: + logging.info('keystone IP {}'.format(ip)) + openrc = { + 'API_VERSION': 3, + 'OS_USERNAME': ch_keystone.DEMO_ADMIN_USER, + 'OS_PASSWORD': ch_keystone.DEMO_ADMIN_USER_PASSWORD, + 'OS_AUTH_URL': 'http://{}:5000/v3'.format(ip), + 'OS_USER_DOMAIN_NAME': ch_keystone.DEMO_DOMAIN, + 'OS_DOMAIN_NAME': ch_keystone.DEMO_DOMAIN, + } + if self.tls_rid: + openrc['OS_CACERT'] = \ + openstack_utils.KEYSTONE_LOCAL_CACERT + openrc['OS_AUTH_URL'] = ( + openrc['OS_AUTH_URL'].replace('http', 'https')) + logging.info('keystone IP {}'.format(ip)) + keystone_session = openstack_utils.get_keystone_session( + openrc, scope='DOMAIN') + keystone_client = ( + openstack_utils.get_keystone_session_client( + keystone_session)) + keystone_client.projects.list() + logging.info("keystone IP:{} without policyd override " + "projects list working" + .format(ip)) + except keystoneauth1.exceptions.http.Forbidden: + raise zaza_exceptions.PolicydError( + 'Retrieve project list as demo user with project ' + 'scoped token passed and should have passed. IP = {}' + .format(ip)) + + # now verify that the policy.d override does disable the endpoint with self.config_change( {'preferred-api-version': self.default_api_version, 'use-policyd-override': 'False'}, @@ -192,7 +233,7 @@ class KeystonePolicydTest(PolicydTest, logging.info("keystone IP:{} policyd override working" .format(ip)) - logging.info('OK') + logging.info('OK') class GenericPolicydTest(PolicydTest, test_utils.OpenStackBaseTest): From 086076c6fc525cfd7af65bd1fbef3a34553e76e7 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Tue, 24 Sep 2019 18:34:50 +0100 Subject: [PATCH 31/36] Still trying to find a call that works with the user --- zaza/openstack/charm_tests/policyd/tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index b7de295..7538cc9 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -184,13 +184,13 @@ class KeystonePolicydTest(PolicydTest, keystone_client = ( openstack_utils.get_keystone_session_client( keystone_session)) - keystone_client.projects.list() + keystone_client.services.list() logging.info("keystone IP:{} without policyd override " - "projects list working" + "services list working" .format(ip)) except keystoneauth1.exceptions.http.Forbidden: raise zaza_exceptions.PolicydError( - 'Retrieve project list as demo user with project ' + 'Retrieve services list as demo user with project ' 'scoped token passed and should have passed. IP = {}' .format(ip)) From 060d86fe27256d9bc0514a56747e5bc843b17b90 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Tue, 24 Sep 2019 19:01:32 +0100 Subject: [PATCH 32/36] Hopefully finalising the services test --- zaza/openstack/charm_tests/policyd/tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 7538cc9..915c23f 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -153,7 +153,7 @@ class KeystonePolicydTest(PolicydTest, def test_disable_service(self): logging.info("Doing policyd override to disable listing domains") self._set_policy_with( - {'rule.yaml': "{'identity:list_projects': '!'}"}) + {'rule.yaml': "{'identity:list_services': '!'}"}) # verify (with the config off) that we can actually access # these points @@ -224,9 +224,9 @@ class KeystonePolicydTest(PolicydTest, keystone_client = ( openstack_utils.get_keystone_session_client( keystone_session)) - keystone_client.projects.list() + keystone_client.services.list() raise zaza_exceptions.PolicydError( - 'Retrieve domain list as admin with project scoped ' + 'Retrieve service list as admin with project scoped ' 'token passed and should have failed. IP = {}' .format(ip)) except keystoneauth1.exceptions.http.Forbidden: From ec74e6a57ca4945a8e31474d88202b71330266f5 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Tue, 24 Sep 2019 19:21:37 +0100 Subject: [PATCH 33/36] Switch the order of the tests --- zaza/openstack/charm_tests/policyd/tests.py | 80 ++++++++++----------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 915c23f..07f4753 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -155,46 +155,7 @@ class KeystonePolicydTest(PolicydTest, self._set_policy_with( {'rule.yaml': "{'identity:list_services': '!'}"}) - # verify (with the config off) that we can actually access - # these points - with self.config_change( - {'preferred-api-version': self.default_api_version}, - {'preferred-api-version': '3'}, - application_name="keystone"): - zaza_model.block_until_all_units_idle() - for ip in self.keystone_ips: - try: - logging.info('keystone IP {}'.format(ip)) - openrc = { - 'API_VERSION': 3, - 'OS_USERNAME': ch_keystone.DEMO_ADMIN_USER, - 'OS_PASSWORD': ch_keystone.DEMO_ADMIN_USER_PASSWORD, - 'OS_AUTH_URL': 'http://{}:5000/v3'.format(ip), - 'OS_USER_DOMAIN_NAME': ch_keystone.DEMO_DOMAIN, - 'OS_DOMAIN_NAME': ch_keystone.DEMO_DOMAIN, - } - if self.tls_rid: - openrc['OS_CACERT'] = \ - openstack_utils.KEYSTONE_LOCAL_CACERT - openrc['OS_AUTH_URL'] = ( - openrc['OS_AUTH_URL'].replace('http', 'https')) - logging.info('keystone IP {}'.format(ip)) - keystone_session = openstack_utils.get_keystone_session( - openrc, scope='DOMAIN') - keystone_client = ( - openstack_utils.get_keystone_session_client( - keystone_session)) - keystone_client.services.list() - logging.info("keystone IP:{} without policyd override " - "services list working" - .format(ip)) - except keystoneauth1.exceptions.http.Forbidden: - raise zaza_exceptions.PolicydError( - 'Retrieve services list as demo user with project ' - 'scoped token passed and should have passed. IP = {}' - .format(ip)) - - # now verify that the policy.d override does disable the endpoint + # verify that the policy.d override does disable the endpoint with self.config_change( {'preferred-api-version': self.default_api_version, 'use-policyd-override': 'False'}, @@ -233,6 +194,45 @@ class KeystonePolicydTest(PolicydTest, logging.info("keystone IP:{} policyd override working" .format(ip)) + # now verify (with the config off) that we can actually access + # these points + with self.config_change( + {'preferred-api-version': self.default_api_version}, + {'preferred-api-version': '3'}, + application_name="keystone"): + zaza_model.block_until_all_units_idle() + for ip in self.keystone_ips: + try: + logging.info('keystone IP {}'.format(ip)) + openrc = { + 'API_VERSION': 3, + 'OS_USERNAME': ch_keystone.DEMO_ADMIN_USER, + 'OS_PASSWORD': ch_keystone.DEMO_ADMIN_USER_PASSWORD, + 'OS_AUTH_URL': 'http://{}:5000/v3'.format(ip), + 'OS_USER_DOMAIN_NAME': ch_keystone.DEMO_DOMAIN, + 'OS_DOMAIN_NAME': ch_keystone.DEMO_DOMAIN, + } + if self.tls_rid: + openrc['OS_CACERT'] = \ + openstack_utils.KEYSTONE_LOCAL_CACERT + openrc['OS_AUTH_URL'] = ( + openrc['OS_AUTH_URL'].replace('http', 'https')) + logging.info('keystone IP {}'.format(ip)) + keystone_session = openstack_utils.get_keystone_session( + openrc, scope='DOMAIN') + keystone_client = ( + openstack_utils.get_keystone_session_client( + keystone_session)) + keystone_client.services.list() + logging.info("keystone IP:{} without policyd override " + "services list working" + .format(ip)) + except keystoneauth1.exceptions.http.Forbidden: + raise zaza_exceptions.PolicydError( + 'Retrieve services list as demo user with project ' + 'scoped token passed and should have passed. IP = {}' + .format(ip)) + logging.info('OK') From 6648aaf0e9f491cfef90afff215dd4f847933aa6 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Wed, 25 Sep 2019 12:03:21 +0100 Subject: [PATCH 34/36] Add negative test for broken yaml --- zaza/openstack/charm_tests/policyd/tests.py | 33 ++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 07f4753..0e0fdce 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -99,7 +99,7 @@ class PolicydTest(object): 'file1.yaml': "{'rule1': '!'}" } good_zip_path = self._make_zip_file_from('good.zip', good) - logging.info("Attaching a resource.") + logging.info("Attaching good zip file as a resource.") zaza_model.attach_resource(self.application_name, 'policyd-override', good_zip_path) @@ -141,6 +141,37 @@ class PolicydTest(object): logging.info("...done") + def test_002_policyd_bad_yaml(self): + # Test that a bad yaml file in the zip file doesn't not put it in the + # override and puts up a status message that it is bad + bad = { + "file2.yaml": "{'rule': '!}" + } + bad_zip_path = self._make_zip_file_from('bad.zip', bad) + logging.info("Attaching bad zip file as a resource") + zaza_model.attach_resource(self.application_name, + 'policyd-override', + bad_zip_path) + logging.debug("... waiting for idle") + zaza_model.block_until_all_units_idle() + logging.debug("Now setting config to true") + self._set_config(True) + # ensure that the workload status info line starts with PO (broken): + # to show that it didn't work + logging.info("Checking for workload status line starts with PO:") + zaza_model.block_until_wl_status_info_starts_with( + self.application_name, "PO (broken):") + logging.debug("App status is valid for broken yaml file") + zaza_model.block_until_all_units_idle() + # now verify that no file got landed on the machine + path = os.path.join( + "/etc", self._service_name, "policy.d", 'file2.yaml') + logging.info("Now checking that file {} is not present.".format(path)) + zaza_model.block_until_file_missing(self.application_name, path) + self._set_config(True) + zaza_model.block_until_all_units_idle() + logging.info("...done") + class KeystonePolicydTest(PolicydTest, ch_keystone.BaseKeystoneTest, From 13128db1f4236f42dac2b47de0f0994f58d3aaa4 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Wed, 25 Sep 2019 14:31:15 +0100 Subject: [PATCH 35/36] Add docs to policyd module --- zaza/openstack/charm_tests/policyd/tests.py | 47 ++++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index 0e0fdce..cc643d9 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -12,7 +12,40 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Encapsulate policyd testing.""" +"""Encapsulate policyd testing. + +The Policyd Tests test the following: + +- Two general tests in the PolicydTest class that check that a policy zip can + drop policy files in the correct service policy.d directory. One test tests + that a valid yaml file is dropped; the 2nd that an invalid one is not dropped + and the workload info status line shows that it is broken. +- A custom policyd test that is per charm and tests that a policy zip file + attached does actually disable something in the associated service (i.e. + verify that the charm has implemented policy overrides and ensured that the + service actually picks them up). + +In order to use the generic tests, just include them in the specific test +class. The KeystonePolicydTest as an example does: + + class KeystonePolicydTest(PolicydTest, + ch_keystone.BaseKeystoneTest, + test_utils.OpenStackBaseTest): + + @classmethod + def setUpClass(cls, application_name=None): + super(KeystonePolicydTest, cls).setUpClass(application_name) + +Note that the generic test class (PolicyDTest) comes first, and then the +ch_keystone.BaseKeystoneTest, followed by the test_utils.OpenStackBaseTest. +This is to get the order of super().setUpClass(...) calls to work with +application_name. + +If a charm doesn't require a specific test, then the GenericPolicydTest class +can be used that just includes the two generic tests. The config in the +tests.yaml would stil be required. See the PolicydTest class docstring for +further details. +""" import logging import os @@ -103,7 +136,6 @@ class PolicydTest(object): zaza_model.attach_resource(self.application_name, 'policyd-override', good_zip_path) - logging.debug("... waiting for idle") zaza_model.block_until_all_units_idle() logging.debug("Now setting config to true") self._set_config(True) @@ -139,7 +171,7 @@ class PolicydTest(object): logging.info("Checking that {} has been removed".format(path)) zaza_model.block_until_file_missing(self.application_name, path) - logging.info("...done") + logging.info("OK") def test_002_policyd_bad_yaml(self): # Test that a bad yaml file in the zip file doesn't not put it in the @@ -152,13 +184,13 @@ class PolicydTest(object): zaza_model.attach_resource(self.application_name, 'policyd-override', bad_zip_path) - logging.debug("... waiting for idle") zaza_model.block_until_all_units_idle() logging.debug("Now setting config to true") self._set_config(True) # ensure that the workload status info line starts with PO (broken): # to show that it didn't work - logging.info("Checking for workload status line starts with PO:") + logging.info( + "Checking for workload status line starts with PO (broken):") zaza_model.block_until_wl_status_info_starts_with( self.application_name, "PO (broken):") logging.debug("App status is valid for broken yaml file") @@ -170,7 +202,7 @@ class PolicydTest(object): zaza_model.block_until_file_missing(self.application_name, path) self._set_config(True) zaza_model.block_until_all_units_idle() - logging.info("...done") + logging.info("OK") class KeystonePolicydTest(PolicydTest, @@ -222,7 +254,8 @@ class KeystonePolicydTest(PolicydTest, 'token passed and should have failed. IP = {}' .format(ip)) except keystoneauth1.exceptions.http.Forbidden: - logging.info("keystone IP:{} policyd override working" + logging.info("keystone IP:{} policyd override disabled " + "services listing by demo user" .format(ip)) # now verify (with the config off) that we can actually access From 3e2d98b1eda545b22d2fbfb5ca9dcd02cabbf8e4 Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Wed, 25 Sep 2019 14:42:38 +0100 Subject: [PATCH 36/36] Fix pep8 failures --- zaza/openstack/charm_tests/policyd/__init__.py | 4 +++- zaza/openstack/charm_tests/policyd/tests.py | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/zaza/openstack/charm_tests/policyd/__init__.py b/zaza/openstack/charm_tests/policyd/__init__.py index 89ec5f1..e8c1746 100644 --- a/zaza/openstack/charm_tests/policyd/__init__.py +++ b/zaza/openstack/charm_tests/policyd/__init__.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Collection of code for setting up and testing policyd overrides across a +"""Policyd. + +Collection of code for setting up and testing policyd overrides across a collection of charms. """ diff --git a/zaza/openstack/charm_tests/policyd/tests.py b/zaza/openstack/charm_tests/policyd/tests.py index cc643d9..9e76654 100644 --- a/zaza/openstack/charm_tests/policyd/tests.py +++ b/zaza/openstack/charm_tests/policyd/tests.py @@ -77,6 +77,7 @@ class PolicydTest(object): @classmethod def setUpClass(cls, application_name=None): + """Run class setup for running Policyd charm operation tests.""" super(PolicydTest, cls).setUpClass(application_name) if (openstack_utils.get_os_release() < openstack_utils.get_os_release('xenial_queens')): @@ -87,6 +88,7 @@ class PolicydTest(object): @classmethod def tearDownClass(cls): + """Run class tearDown for running Policyd charm operation tests.""" super(PolicydTest, cls).tearDownClass() try: shutil.rmtree(cls._tmp_dir, ignore_errors=True) @@ -126,8 +128,7 @@ class PolicydTest(object): self.application_name, "PO:", negate_match=True) def test_001_policyd_good_yaml(self): - # Test that the policyd with a good zipped yaml file puts the yaml file - # in the right directory + """Test that the policyd with a good zipped yaml file.""" good = { 'file1.yaml': "{'rule1': '!'}" } @@ -174,8 +175,7 @@ class PolicydTest(object): logging.info("OK") def test_002_policyd_bad_yaml(self): - # Test that a bad yaml file in the zip file doesn't not put it in the - # override and puts up a status message that it is bad + """Test bad yaml file in the zip file is handled.""" bad = { "file2.yaml": "{'rule': '!}" } @@ -208,12 +208,15 @@ class PolicydTest(object): class KeystonePolicydTest(PolicydTest, ch_keystone.BaseKeystoneTest, test_utils.OpenStackBaseTest): + """Specific test for policyd for keystone charm.""" @classmethod def setUpClass(cls, application_name=None): + """Run class setup for running KeystonePolicydTest tests.""" super(KeystonePolicydTest, cls).setUpClass(application_name) def test_disable_service(self): + """Test that service can be disabled.""" logging.info("Doing policyd override to disable listing domains") self._set_policy_with( {'rule.yaml': "{'identity:list_services': '!'}"}) @@ -301,4 +304,6 @@ class KeystonePolicydTest(PolicydTest, class GenericPolicydTest(PolicydTest, test_utils.OpenStackBaseTest): + """Generic policyd test for any charm without a specific test.""" + pass