Exemple #1
0
def maybe_handle_policyd_override(openstack_release, hook):
    """Handle the use-policy-override config flag and resource file.

    This function checks that policy overrides are supported on this release,
    that the config flag is enabled, and then processes the resources, copies
    the package policies to the config area, loads the override files.  In the
    case where the config flag is false, it removes the policy overrides by
    deleting the config area policys.  Note that the template for
    `local_settings.py` controls where the horizon service actually reads the
    policies from.

    Note that for the 'config-changed' hook, the function is only interested in
    whether the config value of `use-policy-override` matches the current
    status of the policy overrides success file.  If it doesn't, either the
    config area policies are removed (i.e. False) or the policy overrides file
    is processed.

    :param openstack_release: The release of OpenStack installed.
    :type openstack_release: str
    :param hook: The hook name
    :type hook: str
    """
    log("Seeing if policyd overrides need doing", level=INFO)
    if not policyd.is_policyd_override_valid_on_this_release(
            openstack_release):
        log("... policy overrides not valid on this release: {}".format(
            openstack_release),
            level=INFO)
        return
    # if policy config is not set, then remove the entire directory
    _config = config()
    if not _config.get(policyd.POLICYD_CONFIG_NAME, False):
        _dir = policyd.policyd_dir_for('openstack-dashboard')
        if os.path.exists(_dir):
            log("... config is cleared, and removing {}".format(_dir), INFO)
            shutil.rmtree(_dir)
        else:
            log("... nothing to do", INFO)
        policyd.remove_policy_success_file()
        return
    # config-change and the policyd overrides have been performed just return
    if hook == "config-changed" and policyd.is_policy_success_file_set():
        log("... already setup, so skipping.", level=INFO)
        return
    # from now on it should succeed; if it doesn't then status line will show
    # broken.
    resource_filename = policyd.get_policy_resource_filename()
    restart = policyd.process_policy_resource_file(
        resource_filename,
        'openstack-dashboard',
        blacklist_paths=blacklist_policyd_paths(),
        preserve_topdir=True,
        preprocess_filename=policyd_preprocess_name,
        user='******',
        group='horizon')
    copy_conf_to_policyd()
    if restart:
        service('stop', 'apache2')
        service('start', 'apache2')
    log("Policy override processing complete.", level=INFO)
    def test_process_policy_resource_file(
        self,
        mock_maybe_create_directory_for,
        mock_write_file,
        mock_open_and_filter_yaml_files,
        mock_remove_policy_success_file,
        mock_clean_policyd_dir_for,
        mock_path_for_policy_file,
        mock_read_and_validate_yaml,
        mock_log,
        mock__policy_success_file,
        mock_yaml_dump,
    ):
        mock_zfp = mock.MagicMock()
        mod_fn = mock.Mock()
        mock_path_for_policy_file.side_effect = lambda s, n: s + "/" + n
        gen = [("file1", ".yaml", "file1.yaml", "file1-zipinfo"),
               ("file2", ".yml", "file2.yml", "file2-zipinfo")]
        mock_open_and_filter_yaml_files.return_value.__enter__.return_value = \
            (mock_zfp, gen)
        # first verify that we can blacklist a file
        res = policyd.process_policy_resource_file("resource.zip", "aservice",
                                                   ["aservice/file1"], [],
                                                   mod_fn)
        self.assertFalse(res)
        mock_remove_policy_success_file.assert_called_once_with()
        mock_clean_policyd_dir_for.assert_has_calls([
            mock.call("aservice", ["aservice/file1"],
                      user='******',
                      group='aservice'),
            mock.call("aservice", ["aservice/file1"],
                      user='******',
                      group='aservice')
        ])
        mock_zfp.open.assert_not_called()
        mod_fn.assert_not_called()
        mock_log.assert_any_call(
            "Processing resource.zip failed: policy.d"
            " name aservice/file1 is blacklisted",
            level=policyd.POLICYD_LOG_LEVEL_DEFAULT)

        # now test for success
        @contextlib.contextmanager
        def _patch_open():
            '''Patch open() to allow mocking both open() itself and the file that is
            yielded.

            Yields the mock for "open" and "file", respectively.'''
            mock_open = mock.MagicMock(spec=open)
            mock_file = mock.MagicMock(spec=io.FileIO)

            with mock.patch(builtin_open, mock_open):
                yield mock_open, mock_file

        mock_clean_policyd_dir_for.reset_mock()
        mock_zfp.reset_mock()
        mock_fp = mock.MagicMock()
        mock_fp.read.return_value = '{"rule1": "value1"}'
        mock_zfp.open.return_value.__enter__.return_value = mock_fp
        gen = [("file1", ".j2", "file1.j2", "file1-zipinfo")]
        mock_open_and_filter_yaml_files.return_value.__enter__.return_value = \
            (mock_zfp, gen)
        mock_read_and_validate_yaml.return_value = {"rule1": "modded_value1"}
        mod_fn.return_value = '{"rule1": "modded_value1"}'
        mock__policy_success_file.return_value = "policy-success-file"
        mock_yaml_dump.return_value = "dumped-file"
        with _patch_open() as (mock_open, mock_file):
            res = policyd.process_policy_resource_file("resource.zip",
                                                       "aservice", [], ["key"],
                                                       mod_fn)
            self.assertTrue(res)
            # mock_open.assert_any_call("aservice/file1", "wt")
            mock_write_file.assert_called_once_with("aservice/file1",
                                                    b'dumped-file', "aservice",
                                                    "aservice")
            mock_open.assert_any_call("policy-success-file", "w")
            mock_yaml_dump.assert_called_once_with({"rule1": "modded_value1"})
        mock_zfp.open.assert_called_once_with("file1-zipinfo")
        mock_read_and_validate_yaml.assert_called_once_with(
            '{"rule1": "modded_value1"}', ["key"])
        mod_fn.assert_called_once_with('{"rule1": "value1"}')

        # raise a BadPolicyZipFile if we have a template, but there is no
        # template function
        mock_log.reset_mock()
        with _patch_open() as (mock_open, mock_file):
            res = policyd.process_policy_resource_file("resource.zip",
                                                       "aservice", [], ["key"],
                                                       template_function=None)
            self.assertFalse(res)
        mock_log.assert_any_call(
            "Processing resource.zip failed: Template file1.j2 "
            "but no template_function is available",
            level=policyd.POLICYD_LOG_LEVEL_DEFAULT)

        # raise the IOError to validate that code path
        def raise_ioerror(*args):
            raise IOError("bang")

        mock_open_and_filter_yaml_files.side_effect = raise_ioerror
        mock_log.reset_mock()
        res = policyd.process_policy_resource_file("resource.zip", "aservice",
                                                   [], ["key"], mod_fn)
        self.assertFalse(res, False)
        mock_log.assert_any_call(
            "File resource.zip failed with IOError.  "
            "This really shouldn't happen -- error: bang",
            level=policyd.POLICYD_LOG_LEVEL_DEFAULT)

        # raise a general exception, so that is caught and logged too.

        def raise_exception(*args):
            raise Exception("bang2")

        mock_open_and_filter_yaml_files.reset_mock()
        mock_open_and_filter_yaml_files.side_effect = raise_exception
        mock_log.reset_mock()
        res = policyd.process_policy_resource_file("resource.zip", "aservice",
                                                   [], ["key"], mod_fn)
        self.assertFalse(res, False)
        mock_log.assert_any_call(
            "General Exception(bang2) during policyd processing",
            level=policyd.POLICYD_LOG_LEVEL_DEFAULT)