added option to skip hooks

adds the argument --skip-hooks/-sh which will stop the execution of
hooked ansible playbooks. Either a pattern can be specified to match
against or hook execution can be stopped altogether with "all"

Story: 2009241
Task: 43390

Change-Id: I4f2176aa056fec62e31d07140e3d05779480a93d
This commit is contained in:
Alex-Welsh 2022-09-16 13:39:30 +01:00
parent 608951fcce
commit d7069283fd
5 changed files with 87 additions and 8 deletions

View File

@ -213,6 +213,24 @@ you could do the following::
The sequence number for the ``foo.yml`` playbook is ``10``.
Hook execution can be disabled with ``--skip-hooks``. ``--skip-hooks all`` will halt hook execution altogether.
``--skip-hooks <pattern>`` will skip playbooks matching the ``<pattern>``.
For example, if the following playbooks exist:
- ``$KAYOBE_CONFIG_PATH/hooks/control-host-bootstrap/pre.d/example1.yml``
- ``$KAYOBE_CONFIG_PATH/hooks/control-host-bootstrap/pre.d/example2.yml``
- ``$KAYOBE_CONFIG_PATH/hooks/control-host-bootstrap/post.d/example1.yml``
And the following command is used::
(kayobe) $ kayobe control host bootstrap --skip-hooks example1
Only ``$KAYOBE_CONFIG_PATH/hooks/control-host-bootstrap/pre.d/example2.yml`` will be executed.
This example assumes that the term ``example1`` does not appear in
``$KAYOBE_CONFIG_PATH``. If it did, all hooks would be skipped.
Failure handling
----------------

View File

@ -77,6 +77,9 @@ def add_args(parser):
action="store_true",
help="only print names of tasks, don't run them, "
"note this has no affect on kolla-ansible.")
parser.add_argument("-sh", "--skip-hooks", action="store", default=None,
help="disables hooks. Specify a pattern to skip"
"specific playbooks. \"all\" skips all playbooks")
def _get_kayobe_environment_path(parsed_args):

View File

@ -15,6 +15,7 @@
import glob
import json
import os
import re
import sys
from cliff.command import Command
@ -190,18 +191,27 @@ class HookDispatcher(CommandHook):
self.logger.debug("Discovered the following hooks: %s" % hooks)
return hooks
def hooks(self, config_path, target):
hooks = self._find_hooks(config_path, target)
def hooks(self, config_path, target, filter):
hooks_out = []
if filter == "all":
self.logger.debug("Skipping all hooks")
return hooks_out
hooks_in = self._find_hooks(config_path, target)
# Hooks can be prefixed with a sequence number to adjust running order,
# e.g 10-my-custom-playbook.yml. Sort by sequence number.
hooks = sorted(hooks, key=_split_hook_sequence_number)
hooks_in = sorted(hooks_in, key=_split_hook_sequence_number)
for hook in hooks_in:
# Resolve symlinks so that we can reference roles.
hooks = [os.path.realpath(hook) for hook in hooks]
return hooks
hook = os.path.realpath(hook)
if filter and re.search(filter, hook):
self.logger.debug("Skipping hook: %s", hook)
else:
hooks_out.append(hook)
return hooks_out
def run_hooks(self, parsed_args, target):
config_path = parsed_args.config_path
hooks = self.hooks(config_path, target)
hooks = self.hooks(config_path, target, parsed_args.skip_hooks)
if hooks:
self.logger.debug("Running hooks: %s" % hooks)
self.command.run_kayobe_playbooks(parsed_args, hooks)

View File

@ -2506,5 +2506,47 @@ class TestHookDispatcher(unittest.TestCase):
"z-test-alphabetical.yml",
]
mock_path.realpath.side_effect = lambda x: x
actual = dispatcher.hooks("config/path", "pre")
actual = dispatcher.hooks("config/path", "pre", None)
self.assertListEqual(actual, expected_result)
@mock.patch('kayobe.cli.commands.os.path')
def test_hook_filter_all(self, mock_path):
mock_command = mock.MagicMock()
dispatcher = commands.HookDispatcher(command=mock_command)
dispatcher._find_hooks = mock.MagicMock()
dispatcher._find_hooks.return_value = [
"5-hook.yml",
"5-multiple-dashes-in-name.yml",
"10-before-hook.yml",
"10-hook.yml",
"no-prefix.yml",
"z-test-alphabetical.yml",
]
mock_path.realpath.side_effect = lambda x: x
actual = dispatcher.hooks("config/path", "pre", "all")
self.assertListEqual(actual, [])
@mock.patch('kayobe.cli.commands.os.path')
def test_hook_filter_one(self, mock_path):
mock_command = mock.MagicMock()
dispatcher = commands.HookDispatcher(command=mock_command)
dispatcher._find_hooks = mock.MagicMock()
dispatcher._find_hooks.return_value = [
"5-hook.yml",
"5-multiple-dashes-in-name.yml",
"10-before-hook.yml",
"10-hook.yml",
"no-prefix.yml",
"z-test-alphabetical.yml",
]
expected_result = [
"5-hook.yml",
"10-before-hook.yml",
"10-hook.yml",
"no-prefix.yml",
"z-test-alphabetical.yml",
]
mock_path.realpath.side_effect = lambda x: x
actual = dispatcher.hooks("config/path", "pre",
"5-multiple-dashes-in-name.yml")
self.assertListEqual(actual, expected_result)

View File

@ -0,0 +1,6 @@
---
features:
- |
Adds the --skip-hooks argument to ignore hooks for the execution of a
command. See `story 2009241
<https://storyboard.openstack.org/#!/story/2009241>`_ for details.