diff --git a/AUTHORS b/AUTHORS index a6036395..211a38e0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -57,3 +57,4 @@ Neilen Marais Rory Geoghegan Alexey Diyan Kamil Kisiel +Jonas Lundberg diff --git a/fabric/task_utils.py b/fabric/task_utils.py index 5d5f96b8..d658d7f5 100644 --- a/fabric/task_utils.py +++ b/fabric/task_utils.py @@ -52,6 +52,9 @@ def merge(hosts, roles, exclude, roledefs): role_hosts = [] for role in roles: value = roledefs[role] + # Handle dict style roledefs + if isinstance(value, dict): + value = value['hosts'] # Handle "lazy" roles (callables) if callable(value): value = value() diff --git a/sites/docs/usage/execution.rst b/sites/docs/usage/execution.rst index b916c191..b79bcf77 100644 --- a/sites/docs/usage/execution.rst +++ b/sites/docs/usage/execution.rst @@ -133,11 +133,29 @@ loading other fabfiles which also modify it, of course):: 'dns': ['ns1', 'ns2'] } -In addition to list/iterable object types, the values in ``env.roledefs`` may -be callables, and will thus be called when looked up when tasks are run instead -of at module load time. (For example, you could connect to remote servers -to obtain role definitions, and not worry about causing delays at fabfile load -time when calling e.g. ``fab --list``.) +Role definitions are not necessary configuration of hosts only, but could hold +other role specific settings of your choice. This is achieved by defining the +roles as dicts and host strings under a ``hosts`` key:: + + from fabric.api import env + + env.roledefs = { + 'web': { + 'hosts': ['www1', 'www2', 'www3'], + 'foo': 'bar' + }, + 'dns': { + 'hosts': ['ns1', 'ns2'], + 'foo': 'baz' + } + } + +In addition to list/iterable object types, the values in ``env.roledefs`` +(or value of ``hosts`` key in dict style definition) may be callables, and will +thus be called when looked up when tasks are run instead of at module load +time. (For example, you could connect to remote servers to obtain role +definitions, and not worry about causing delays at fabfile load time when +calling e.g. ``fab --list``.) Use of roles is not required in any way -- it's simply a convenience in situations where you have common groupings of servers. diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst index 4cfa0c9c..1fd5b70b 100644 --- a/sites/www/changelog.rst +++ b/sites/www/changelog.rst @@ -2,6 +2,8 @@ Changelog ========= +* :feature:`1098` Add support for dict style roledefs. + Thanks to Jonas Lundberg, `@lundberg` * :release:`1.9.1 <2014-08-06>` * :release:`1.8.5 <2014-08-06>` * :release:`1.7.5 <2014-08-06>` diff --git a/tests/test_main.py b/tests/test_main.py index 97f735d0..e46105ff 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -271,6 +271,21 @@ def command(): eq_hosts(command, ['a', 'b'], env={'roledefs': spaced_roles}) +dict_roles = { + 'r1': {'hosts': ['a', 'b']}, + 'r2': ['b', 'c'], +} + +def test_hosts_in_role_dict(): + """ + Make sure hosts defined in env.roles are cleaned of extra spaces + """ + @roles('r1') + def command(): + pass + eq_hosts(command, ['a', 'b'], env={'roledefs': dict_roles}) + + def test_hosts_decorator_expands_single_iterable(): """ @hosts(iterable) should behave like @hosts(*iterable)