Skip to content

Commit

Permalink
Merge pull request #855 from samdoran/issue/852-playbook-as-dict
Browse files Browse the repository at this point in the history
Accept a dictionary for playbook

Fixes #852
Fixes #642
The validation function isplaybook() expects a playbook to be a list of dictionaries, not a bare dictionary or a string. Update the docs to reflect this requirement and cast a bare dictionary to a list before passing to isplaybook().

 Add tests

Reviewed-by: David Shrewsbury <None>
Reviewed-by: Sam Doran <sdoran@redhat.com>
Reviewed-by: Shane McDonald <me@shanemcd.com>
Reviewed-by: None <None>
  • Loading branch information
ansible-zuul[bot] authored Oct 13, 2021
2 parents 405d8aa + 0fecae8 commit 3d6886d
Show file tree
Hide file tree
Showing 8 changed files with 425 additions and 427 deletions.
2 changes: 1 addition & 1 deletion ansible_runner/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def run(**kwargs):
:param ident: The run identifier for this invocation of Runner. Will be used to create and name
the artifact directory holding the results of the invocation.
:param json_mode: Store event data in place of stdout on the console and in the stdout file
:param playbook: The playbook (either supplied here as a list or string... or as a path relative to
:param playbook: The playbook (either a list or dictionary of plays, or as a path relative to
``private_data_dir/project``) that will be invoked by runner when executing Ansible.
:param module: The module that will be invoked in ad-hoc mode by runner when executing Ansible.
:param module_args: The module arguments that will be supplied to ad-hoc mode.
Expand Down
24 changes: 15 additions & 9 deletions ansible_runner/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
import signal

from distutils.spawn import find_executable

from ansible_runner.exceptions import ConfigurationError

try:
from collections.abc import Iterable, Mapping
from collections.abc import Iterable, MutableMapping
except ImportError:
from collections import Iterable, Mapping
from collections import Iterable, MutableMapping
from io import StringIO
from six import string_types, PY2, PY3, text_type, binary_type

Expand Down Expand Up @@ -72,7 +73,7 @@ def isplaybook(obj):
Returns:
boolean: True if the object is a list and False if it is not
'''
return isinstance(obj, Iterable) and (not isinstance(obj, string_types) and not isinstance(obj, Mapping))
return isinstance(obj, Iterable) and (not isinstance(obj, string_types) and not isinstance(obj, MutableMapping))


def isinventory(obj):
Expand All @@ -85,7 +86,7 @@ def isinventory(obj):
Returns:
boolean: True if the object is an inventory dict and False if it is not
'''
return isinstance(obj, Mapping) or isinstance(obj, string_types)
return isinstance(obj, MutableMapping) or isinstance(obj, string_types)


def check_isolation_executable_installed(isolation_executable):
Expand Down Expand Up @@ -202,15 +203,20 @@ def dump_artifacts(kwargs):

kwargs['envvars']['ANSIBLE_ROLES_PATH'] = roles_path

obj = kwargs.get('playbook')
if obj and isplaybook(obj):
path = os.path.join(private_data_dir, 'project')
kwargs['playbook'] = dump_artifact(json.dumps(obj), path, 'main.json')
playbook = kwargs.get('playbook')
if playbook:
# Ensure the play is a list of dictionaries
if isinstance(playbook, MutableMapping):
playbook = [playbook]

if isplaybook(playbook):
path = os.path.join(private_data_dir, 'project')
kwargs['playbook'] = dump_artifact(json.dumps(playbook), path, 'main.json')

obj = kwargs.get('inventory')
if obj and isinventory(obj):
path = os.path.join(private_data_dir, 'inventory')
if isinstance(obj, Mapping):
if isinstance(obj, MutableMapping):
kwargs['inventory'] = dump_artifact(json.dumps(obj), path, 'hosts.json')
elif isinstance(obj, string_types):
if not os.path.exists(obj):
Expand Down
4 changes: 2 additions & 2 deletions docs/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
Installing Ansible Runner
=========================

Ansible Runner is provided from several different locations depending on how you want to use it.
Ansible Runner requires Python >= 3.8 and is provided from several different locations depending on how you want to use it.

Using pip
---------

Python 2.7+ and 3.6+ are supported and installable via pip::
To install the latest version from the Python Package Index::

$ pip install ansible-runner

Expand Down
15 changes: 13 additions & 2 deletions test/integration/test_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,19 @@ def test_run():
assert r.status == 'successful'


def test_run_async():
thread, r = run_async(module='debug', host_pattern='localhost')
@pytest.mark.parametrize(
'playbook', (
[{'hosts': 'localhost', 'tasks': [{'ping': ''}]}],
{'hosts': 'localhost', 'tasks': [{'ping': ''}]},
)
)
def test_run_playbook_data(playbook, tmp_path):
r = run(private_data_dir=str(tmp_path), playbook=playbook)
assert r.status == 'successful'


def test_run_async(tmp_path):
thread, r = run_async(private_data_dir=str(tmp_path), module='debug', host_pattern='localhost')
thread.join()
assert r.status == 'successful'

Expand Down
Loading

0 comments on commit 3d6886d

Please sign in to comment.