Uberspace / paternoster

Paternoster allows you to run Ansible playbooks like ordinary Python or Bash scripts.
Other
122 stars 4 forks source link

test failures in Fedora 36 (to be released) #53

Open ibotty opened 2 years ago

ibotty commented 2 years ago

From the CI rebuild for Fedora 36 https://koji.fedoraproject.org/koji/taskinfo?taskID=81559080 Complete build log: https://kojipkgs.fedoraproject.org//work/tasks/9080/81559080/build.log

Notably it uses ansible-5.2.0-1.fc36.noarch and ansible-core-2.12.1-3.fc36.noarch. So it might be that. The complete (python) package list can be found in https://kojipkgs.fedoraproject.org//work/tasks/9080/81559080/root.log

I am a little lost what the error might be.

+ /usr/bin/py.test
============================= test session starts ==============================
platform linux -- Python 3.10.2, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /builddir/build/BUILD/paternoster-3.3.0, configfile: tox.ini, testpaths: paternoster/test
plugins: cov-3.0.0
collected 279 items
paternoster/test/test_ansible_runner.py .....FF............              [  6%]
paternoster/test/test_parameters.py .................................... [ 19%]
................................                                         [ 31%]
paternoster/test/test_paternoster.py ..                                  [ 31%]
paternoster/test/test_prompt.py ........................................ [ 46%]
..............................                                           [ 56%]
paternoster/test/test_root.py ..........                                 [ 60%]
paternoster/test/test_types.py ......................................... [ 75%]
.....................................................................    [100%]
=================================== FAILURES ===================================
__________________ test_verbose[True-keywords1-notkeywords1] ___________________
self = <ansible.executor.task_queue_manager.TaskQueueManager object at 0x7fff9aefad40>
method_name = 'v2_playbook_on_play_start', args = (all,), kwargs = {}
callback_plugin = <ansible.plugins.callback.default.CallbackModule object at 0x7fff9aefaf20>
wants_implicit_tasks = False
methods = [<bound method CallbackModule.v2_playbook_on_play_start of <ansible.plugins.callback.default.CallbackModule object at ...>, <bound method CallbackBase.v2_on_any of <ansible.plugins.callback.default.CallbackModule object at 0x7fff9aefaf20>>]
possible = 'v2_on_any'
gotit = <bound method CallbackBase.v2_on_any of <ansible.plugins.callback.default.CallbackModule object at 0x7fff9aefaf20>>
new_args = [all], is_implicit_task = False, arg = all
method = <bound method CallbackModule.v2_playbook_on_play_start of <ansible.plugins.callback.default.CallbackModule object at 0x7fff9aefaf20>>
    @lock_decorator(attr='_callback_lock')
    def send_callback(self, method_name, *args, **kwargs):
        for callback_plugin in [self._stdout_callback] + self._callback_plugins:
            # a plugin that set self.disabled to True will not be called
            # see osx_say.py example for such a plugin
            if getattr(callback_plugin, 'disabled', False):
                continue

            # a plugin can opt in to implicit tasks (such as meta). It does this
            # by declaring self.wants_implicit_tasks = True.
            wants_implicit_tasks = getattr(callback_plugin, 'wants_implicit_tasks', False)

            # try to find v2 method, fallback to v1 method, ignore callback if no method found
            methods = []
            for possible in [method_name, 'v2_on_any']:
                gotit = getattr(callback_plugin, possible, None)
                if gotit is None:
                    gotit = getattr(callback_plugin, possible.replace('v2_', ''), None)
                if gotit is not None:
                    methods.append(gotit)

            # send clean copies
            new_args = []

            # If we end up being given an implicit task, we'll set this flag in
            # the loop below. If the plugin doesn't care about those, then we
            # check and continue to the next iteration of the outer loop.
            is_implicit_task = False

            for arg in args:
                # FIXME: add play/task cleaners
                if isinstance(arg, TaskResult):
                    new_args.append(arg.clean_copy())
                # elif isinstance(arg, Play):
                # elif isinstance(arg, Task):
                else:
                    new_args.append(arg)

                if isinstance(arg, Task) and arg.implicit:
                    is_implicit_task = True

            if is_implicit_task and not wants_implicit_tasks:
                continue

            for method in methods:
                try:
>                   method(*new_args, **kwargs)
/usr/lib/python3.10/site-packages/ansible/executor/task_queue_manager.py:450: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
self = <ansible.plugins.callback.default.CallbackModule object at 0x7fff9aefaf20>
play = all
    def v2_playbook_on_play_start(self, play):
        name = play.get_name().strip()
        if play.check_mode and self.check_mode_markers:
            checkmsg = " [CHECK MODE]"
        else:
            checkmsg = ""
        if not name:
            msg = u"PLAY%s" % checkmsg
        else:
            msg = u"PLAY [%s]%s" % (name, checkmsg)

        self._play = play

>       self._display.banner(msg)
/usr/lib/python3.10/site-packages/ansible/plugins/callback/default.py:245: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
self = <ansible.utils.display.Display object at 0x7fff9cd8c6a0>
msg = 'PLAY [all]', color = None, cows = True
    def banner(self, msg, color=None, cows=True):
        '''
        Prints a header-looking line with cowsay or stars with length depending on terminal width (3 minimum)
        '''
        msg = to_text(msg)

        if self.b_cowsay and cows:
            try:
                self.banner_cowsay(msg)
                return
            except OSError:
                self.warning("somebody cleverly deleted cowsay or something during the PB run.  heh.")

        msg = msg.strip()
        try:
>           star_len = self.columns - get_text_width(msg)
/usr/lib/python3.10/site-packages/ansible/utils/display.py:431: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
text = 'PLAY [all]'
    def get_text_width(text):
        """Function that utilizes ``wcswidth`` or ``wcwidth`` to determine the
        number of columns used to display a text string.

        We try first with ``wcswidth``, and fallback to iterating each
        character and using wcwidth individually, falling back to a value of 0
        for non-printable wide characters

        On Py2, this depends on ``locale.setlocale(locale.LC_ALL, '')``,
        that in the case of Ansible is done in ``bin/ansible``
        """
        if not isinstance(text, text_type):
            raise TypeError('get_text_width requires text, not %s' % type(text))

        if _LOCALE_INITIALIZATION_ERR:
            Display().warning(
                'An error occurred while calling ansible.utils.display.initialize_locale '
                '(%s). This may result in incorrectly calculated text widths that can '
                'cause Display to print incorrect line lengths' % _LOCALE_INITIALIZATION_ERR
            )
        elif not _LOCALE_INITIALIZED:
>           Display().warning(
                'ansible.utils.display.initialize_locale has not been called, '
                'this may result in incorrectly calculated text widths that can '
                'cause Display to print incorrect line lengths'
            )
/usr/lib/python3.10/site-packages/ansible/utils/display.py:100: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
msg = 'ansible.utils.display.initialize_locale has not been called, this may result in incorrectly calculated text widths that can cause Display to print incorrect line lengths'
args = (), kwargs = {}
    def display_warning(msg, *args, **kwargs):
        if not msg.startswith('Could not match supplied host pattern'):
>           __main__._real_warning(msg, *args, **kwargs)
paternoster/runners/ansiblerunner.py:144: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
msg = 'ansible.utils.display.initialize_locale has not been called, this may result in incorrectly calculated text widths that can cause Display to print incorrect line lengths'
args = (), kwargs = {}
    def display_warning(msg, *args, **kwargs):
        if not msg.startswith('Could not match supplied host pattern'):
>           __main__._real_warning(msg, *args, **kwargs)
E           RecursionError: maximum recursion depth exceeded in comparison
paternoster/runners/ansiblerunner.py:144: RecursionError
!!! Recursion detected (same locals & position)
During handling of the above exception, another exception occurred:
verbosity = True, keywords = ['TASK [debug]', 'PLAY RECAP']
notkeywords = ['ESTABLISH LOCAL CONNECTION']
capsys = <_pytest.capture.CaptureFixture object at 0x7fff9ae54790>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fff9ae57ac0>
    @pytest.mark.skipif(SKIP_ANSIBLE_TESTS, reason="ansible <2.4 requires python2")
    @pytest.mark.parametrize("verbosity,keywords,notkeywords", [
        (False, [], ["TASK [debug]", "PLAY RECAP"]),
        (True, ["TASK [debug]", "PLAY RECAP"], ["ESTABLISH LOCAL CONNECTION"]),
        (3, ["TASK [debug]", "PLAY RECAP", "task path"], []),
    ])
    def test_verbose(verbosity, keywords, notkeywords, capsys, monkeypatch):
        import os
        from ..runners.ansiblerunner import AnsibleRunner

        playbook_path = '/tmp/paternoster-test-playbook.yml'
        playbook = """
        - hosts: all
          gather_facts: no
          tasks:
            - debug: msg=a
        """

        with open(playbook_path, 'w') as f:
            f.write(playbook)

        monkeypatch.setattr(os, 'chdir', lambda *args, **kwargs: None)
>       AnsibleRunner(playbook_path).run([], verbosity)
paternoster/test/test_ansible_runner.py:59: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
paternoster/runners/ansiblerunner.py:238: in run
    status = self._get_playbook_executor(variables, verbosity).run()
/usr/lib/python3.10/site-packages/ansible/executor/playbook_executor.py:190: in run
    result = self._tqm.run(play=play)
/usr/lib/python3.10/site-packages/ansible/executor/task_queue_manager.py:281: in run
    self.send_callback('v2_playbook_on_play_start', new_play)
/usr/lib/python3.10/site-packages/ansible/utils/lock.py:41: in inner
    return func(*args, **kwargs)
/usr/lib/python3.10/site-packages/ansible/executor/task_queue_manager.py:453: in send_callback
    display.warning(u"Failure using method (%s) in callback plugin (%s): %s" % (to_text(method_name), to_text(callback_plugin), to_text(e)))
paternoster/runners/ansiblerunner.py:144: in display_warning
    __main__._real_warning(msg, *args, **kwargs)
paternoster/runners/ansiblerunner.py:144: in display_warning
    __main__._real_warning(msg, *args, **kwargs)
E   RecursionError: maximum recursion depth exceeded in comparison
!!! Recursion detected (same locals & position)
____________________ test_verbose[3-keywords2-notkeywords2] ____________________
self = <ansible.executor.task_queue_manager.TaskQueueManager object at 0x7fff997d2ec0>
method_name = 'v2_playbook_on_start'
args = (<ansible.playbook.Playbook object at 0x7fff997d2ce0>,), kwargs = {}
callback_plugin = <ansible.plugins.callback.default.CallbackModule object at 0x7fff997d2cb0>
wants_implicit_tasks = False
methods = [<bound method CallbackModule.v2_playbook_on_start of <ansible.plugins.callback.default.CallbackModule object at 0x7ff...>, <bound method CallbackBase.v2_on_any of <ansible.plugins.callback.default.CallbackModule object at 0x7fff997d2cb0>>]
possible = 'v2_on_any'
gotit = <bound method CallbackBase.v2_on_any of <ansible.plugins.callback.default.CallbackModule object at 0x7fff997d2cb0>>
new_args = [<ansible.playbook.Playbook object at 0x7fff997d2ce0>]
is_implicit_task = False
arg = <ansible.playbook.Playbook object at 0x7fff997d2ce0>
method = <bound method CallbackModule.v2_playbook_on_start of <ansible.plugins.callback.default.CallbackModule object at 0x7fff997d2cb0>>
    @lock_decorator(attr='_callback_lock')
    def send_callback(self, method_name, *args, **kwargs):
        for callback_plugin in [self._stdout_callback] + self._callback_plugins:
            # a plugin that set self.disabled to True will not be called
            # see osx_say.py example for such a plugin
            if getattr(callback_plugin, 'disabled', False):
                continue

            # a plugin can opt in to implicit tasks (such as meta). It does this
            # by declaring self.wants_implicit_tasks = True.
            wants_implicit_tasks = getattr(callback_plugin, 'wants_implicit_tasks', False)

            # try to find v2 method, fallback to v1 method, ignore callback if no method found
            methods = []
            for possible in [method_name, 'v2_on_any']:
                gotit = getattr(callback_plugin, possible, None)
                if gotit is None:
                    gotit = getattr(callback_plugin, possible.replace('v2_', ''), None)
                if gotit is not None:
                    methods.append(gotit)

            # send clean copies
            new_args = []

            # If we end up being given an implicit task, we'll set this flag in
            # the loop below. If the plugin doesn't care about those, then we
            # check and continue to the next iteration of the outer loop.
            is_implicit_task = False

            for arg in args:
                # FIXME: add play/task cleaners
                if isinstance(arg, TaskResult):
                    new_args.append(arg.clean_copy())
                # elif isinstance(arg, Play):
                # elif isinstance(arg, Task):
                else:
                    new_args.append(arg)

                if isinstance(arg, Task) and arg.implicit:
                    is_implicit_task = True

            if is_implicit_task and not wants_implicit_tasks:
                continue

            for method in methods:
                try:
>                   method(*new_args, **kwargs)
/usr/lib/python3.10/site-packages/ansible/executor/task_queue_manager.py:450: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
self = <ansible.plugins.callback.default.CallbackModule object at 0x7fff997d2cb0>
playbook = <ansible.playbook.Playbook object at 0x7fff997d2ce0>
    def v2_playbook_on_start(self, playbook):
        if self._display.verbosity > 1:
            from os.path import basename
>           self._display.banner("PLAYBOOK: %s" % basename(playbook._file_name))
/usr/lib/python3.10/site-packages/ansible/plugins/callback/default.py:383: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
self = <ansible.utils.display.Display object at 0x7fff9cd8c6a0>
msg = 'PLAYBOOK: paternoster-test-playbook.yml', color = None, cows = True
    def banner(self, msg, color=None, cows=True):
        '''
        Prints a header-looking line with cowsay or stars with length depending on terminal width (3 minimum)
        '''
        msg = to_text(msg)

        if self.b_cowsay and cows:
            try:
                self.banner_cowsay(msg)
                return
            except OSError:
                self.warning("somebody cleverly deleted cowsay or something during the PB run.  heh.")

        msg = msg.strip()
        try:
>           star_len = self.columns - get_text_width(msg)
/usr/lib/python3.10/site-packages/ansible/utils/display.py:431: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
text = 'PLAYBOOK: paternoster-test-playbook.yml'
    def get_text_width(text):
        """Function that utilizes ``wcswidth`` or ``wcwidth`` to determine the
        number of columns used to display a text string.

        We try first with ``wcswidth``, and fallback to iterating each
        character and using wcwidth individually, falling back to a value of 0
        for non-printable wide characters

        On Py2, this depends on ``locale.setlocale(locale.LC_ALL, '')``,
        that in the case of Ansible is done in ``bin/ansible``
        """
        if not isinstance(text, text_type):
            raise TypeError('get_text_width requires text, not %s' % type(text))

        if _LOCALE_INITIALIZATION_ERR:
            Display().warning(
                'An error occurred while calling ansible.utils.display.initialize_locale '
                '(%s). This may result in incorrectly calculated text widths that can '
                'cause Display to print incorrect line lengths' % _LOCALE_INITIALIZATION_ERR
            )
        elif not _LOCALE_INITIALIZED:
>           Display().warning(
                'ansible.utils.display.initialize_locale has not been called, '
                'this may result in incorrectly calculated text widths that can '
                'cause Display to print incorrect line lengths'
            )
/usr/lib/python3.10/site-packages/ansible/utils/display.py:100: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
msg = 'ansible.utils.display.initialize_locale has not been called, this may result in incorrectly calculated text widths that can cause Display to print incorrect line lengths'
args = (), kwargs = {}
    def display_warning(msg, *args, **kwargs):
        if not msg.startswith('Could not match supplied host pattern'):
>           __main__._real_warning(msg, *args, **kwargs)
paternoster/runners/ansiblerunner.py:144: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
msg = 'ansible.utils.display.initialize_locale has not been called, this may result in incorrectly calculated text widths that can cause Display to print incorrect line lengths'
args = (), kwargs = {}
    def display_warning(msg, *args, **kwargs):
        if not msg.startswith('Could not match supplied host pattern'):
>           __main__._real_warning(msg, *args, **kwargs)
E           RecursionError: maximum recursion depth exceeded in comparison
paternoster/runners/ansiblerunner.py:144: RecursionError
!!! Recursion detected (same locals & position)
During handling of the above exception, another exception occurred:
verbosity = 3, keywords = ['TASK [debug]', 'PLAY RECAP', 'task path']
notkeywords = []
capsys = <_pytest.capture.CaptureFixture object at 0x7fff997d3eb0>
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x7fff997d3910>
    @pytest.mark.skipif(SKIP_ANSIBLE_TESTS, reason="ansible <2.4 requires python2")
    @pytest.mark.parametrize("verbosity,keywords,notkeywords", [
        (False, [], ["TASK [debug]", "PLAY RECAP"]),
        (True, ["TASK [debug]", "PLAY RECAP"], ["ESTABLISH LOCAL CONNECTION"]),
        (3, ["TASK [debug]", "PLAY RECAP", "task path"], []),
    ])
    def test_verbose(verbosity, keywords, notkeywords, capsys, monkeypatch):
        import os
        from ..runners.ansiblerunner import AnsibleRunner

        playbook_path = '/tmp/paternoster-test-playbook.yml'
        playbook = """
        - hosts: all
          gather_facts: no
          tasks:
            - debug: msg=a
        """

        with open(playbook_path, 'w') as f:
            f.write(playbook)

        monkeypatch.setattr(os, 'chdir', lambda *args, **kwargs: None)
>       AnsibleRunner(playbook_path).run([], verbosity)
paternoster/test/test_ansible_runner.py:59: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
paternoster/runners/ansiblerunner.py:238: in run
    status = self._get_playbook_executor(variables, verbosity).run()
/usr/lib/python3.10/site-packages/ansible/executor/playbook_executor.py:120: in run
    self._tqm.send_callback('v2_playbook_on_start', pb)
/usr/lib/python3.10/site-packages/ansible/utils/lock.py:41: in inner
    return func(*args, **kwargs)
/usr/lib/python3.10/site-packages/ansible/executor/task_queue_manager.py:453: in send_callback
    display.warning(u"Failure using method (%s) in callback plugin (%s): %s" % (to_text(method_name), to_text(callback_plugin), to_text(e)))
paternoster/runners/ansiblerunner.py:144: in display_warning
    __main__._real_warning(msg, *args, **kwargs)
paternoster/runners/ansiblerunner.py:144: in display_warning
    __main__._real_warning(msg, *args, **kwargs)
E   RecursionError: maximum recursion depth exceeded in comparison
!!! Recursion detected (same locals & position)
----------------------------- Captured stdout call -----------------------------
Skipping callback 'default', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.
=============================== warnings summary ===============================
paternoster/runners/ansiblerunner.py:5
  /builddir/build/BUILD/paternoster-3.3.0/paternoster/runners/ansiblerunner.py:5: DeprecationWarning: The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives
    from distutils.version import LooseVersion
-- Docs: https://docs.pytest.org/en/stable/warnings.html
---------- coverage: platform linux, python 3.10.2-final-0 -----------
Coverage HTML written to dir htmlcov
=========================== short test summary info ============================
FAILED paternoster/test/test_ansible_runner.py::test_verbose[True-keywords1-notkeywords1]
FAILED paternoster/test/test_ansible_runner.py::test_verbose[3-keywords2-notkeywords2]
================== 2 failed, 277 passed, 1 warning in 12.65s ===================
ibotty commented 2 years ago

Any update? I cannot get it to work on Fedora 36 and Fedora 37. This will most likely mean, that it won't work on other future distributions either.

luto commented 2 years ago

We currently only test up to ansible 2.10. Since the ansible team has a habit of changing their project and (internal) API structure around regularly, Paternoster likely doesn't work with newer versions until someone steps up and fixes it.