Skip to content

Commit d96a011

Browse files
committed
Add --data key=value CLI flag.
* Implement `--data key=value` CLI flag. This reworks how `--ssh-*` and `--winrm-*` CLI flags are applied as data, removing specific code for them from the main `pyinfra` package. * Add inventory API tests.
1 parent c15ea18 commit d96a011

File tree

6 files changed

+116
-68
lines changed

6 files changed

+116
-68
lines changed

pyinfra/api/inventory.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,19 @@ class Inventory(object):
2828
2929
Args:
3030
names_data: tuple of ``(names, data)``
31-
ssh_user: override SSH user
32-
ssh_port: override SSH port
33-
ssh_key: override SSH key filename
34-
ssh_key_password: override password for the SSH key
35-
ssh_password: override SSH password
36-
winrm_username: override WINRM username
37-
winrm_password: override WINRM pasword
38-
winrm_port: override WINRM port
39-
winrm_transport: override WINRM transport
40-
**groups: map of group names -> ``(names, data)``
31+
override_data: dictionary of data overrides
32+
ssh_*: deprecated, use ``override_data.ssh_*``
33+
winrm_*: deprecated, use ``override_data.winrm_*``
34+
**groups: map of group name -> ``(names, data)``
4135
'''
4236

4337
state = None
4438

4539
def __init__(
4640
self, names_data,
41+
override_data=None,
42+
# The below are deprecated, override_data.X is now preferred
43+
# TODO: remove these in v2
4744
ssh_user=None, ssh_port=None, ssh_key=None,
4845
ssh_key_password=None, ssh_password=None,
4946
winrm_username=None, winrm_password=None,
@@ -55,24 +52,27 @@ def __init__(
5552
self.host_data = defaultdict(dict) # dict of name -> data
5653
self.group_data = defaultdict(dict) # dict of name -> data
5754

58-
# In CLI mode these are --user, --key, etc
59-
override_data = {
60-
'ssh_user': ssh_user,
61-
'ssh_key': ssh_key,
62-
'ssh_key_password': ssh_key_password,
63-
'ssh_port': ssh_port,
64-
'ssh_password': ssh_password,
65-
'winrm_username': winrm_username,
66-
'winrm_password': winrm_password,
67-
'winrm_port': winrm_port,
68-
'winrm_transport': winrm_transport,
69-
}
70-
# Strip None values
71-
override_data = {
72-
key: value
73-
for key, value in six.iteritems(override_data)
74-
if value is not None
75-
}
55+
override_data = override_data or {}
56+
57+
# TODO: remove these in v2
58+
for key, value in (
59+
('ssh_user', ssh_user),
60+
('ssh_key', ssh_key),
61+
('ssh_key_password', ssh_key_password),
62+
('ssh_port', ssh_port),
63+
('ssh_password', ssh_password),
64+
('winrm_username', winrm_username),
65+
('winrm_password', winrm_password),
66+
('winrm_port', winrm_port),
67+
('winrm_transport', winrm_transport),
68+
):
69+
if value is not None:
70+
logger.warning((
71+
'Use of `{0}` is deprecated when creating `Inventory` classes, '
72+
'please use `override_data.{0}`'
73+
).format(key))
74+
override_data[key] = value
75+
7676
self.override_data = override_data
7777

7878
names, data = names_data

pyinfra_cli/inventory.py

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -77,27 +77,12 @@ def _get_groups_from_filename(inventory_filename):
7777
}
7878

7979

80-
def make_inventory(
81-
inventory_filename,
82-
deploy_dir=None,
83-
ssh_port=None,
84-
ssh_user=None,
85-
ssh_key=None,
86-
ssh_key_password=None,
87-
ssh_password=None,
88-
winrm_username=None,
89-
winrm_password=None,
90-
winrm_port=None,
91-
winrm_transport=None,
92-
):
80+
def make_inventory(inventory_filename, override_data=None, deploy_dir=None):
9381
'''
9482
Builds a ``pyinfra.api.Inventory`` from the filesystem. If the file does not exist
9583
and doesn't contain a / attempts to use that as the only hostname.
9684
'''
9785

98-
if ssh_port is not None:
99-
ssh_port = int(ssh_port)
100-
10186
file_groupname = None
10287

10388
# If we're not a valid file we assume a list of comma separated hostnames
@@ -181,14 +166,6 @@ def make_inventory(
181166

182167
return Inventory(
183168
groups.pop('all'),
184-
ssh_user=ssh_user,
185-
ssh_key=ssh_key,
186-
ssh_key_password=ssh_key_password,
187-
ssh_port=ssh_port,
188-
ssh_password=ssh_password,
189-
winrm_username=winrm_username,
190-
winrm_password=winrm_password,
191-
winrm_port=winrm_port,
192-
winrm_transport=winrm_transport,
169+
override_data=override_data,
193170
**groups
194171
), file_groupname and file_groupname.lower()

pyinfra_cli/main.py

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
get_operation_and_args,
5454
list_dirs_above_file,
5555
load_deploy_file,
56+
parse_cli_arg,
5657
)
5758
from .virtualenv import init_virtualenv
5859

@@ -118,6 +119,11 @@ def _print_support(ctx, param, value):
118119
multiple=True,
119120
)
120121
@click.option('--fail-percent', type=int, help='% of hosts allowed to fail.')
122+
@click.option(
123+
'--data',
124+
multiple=True,
125+
help='Override data values, format key=value',
126+
)
121127
# Auth args
122128
@click.option(
123129
'--sudo', is_flag=True, default=False,
@@ -264,7 +270,7 @@ def _main(
264270
winrm_username, winrm_password, winrm_port,
265271
winrm_transport, shell_executable,
266272
sudo, sudo_user, use_sudo_password, su_user,
267-
parallel, fail_percent,
273+
parallel, fail_percent, data,
268274
dry, limit, no_wait, serial, quiet,
269275
debug, debug_data, debug_facts, debug_operations,
270276
facts=None, print_operations=None, support=None,
@@ -431,19 +437,36 @@ def _main(
431437
if not quiet:
432438
click.echo('--> Loading inventory...', err=True)
433439

440+
override_data = {}
441+
442+
for arg in data:
443+
key, value = arg.split('=', 1)
444+
override_data[key] = value
445+
446+
override_data = {
447+
key: parse_cli_arg(value)
448+
for key, value in override_data.items()
449+
}
450+
451+
for key, value in (
452+
('ssh_user', ssh_user),
453+
('ssh_key', ssh_key),
454+
('ssh_key_password', ssh_key_password),
455+
('ssh_port', ssh_port),
456+
('ssh_password', ssh_password),
457+
('winrm_username', winrm_username),
458+
('winrm_password', winrm_password),
459+
('winrm_port', winrm_port),
460+
('winrm_transport', winrm_transport),
461+
):
462+
if value:
463+
override_data[key] = value
464+
434465
# Load up the inventory from the filesystem
435466
inventory, inventory_group = make_inventory(
436467
inventory,
437468
deploy_dir=deploy_dir,
438-
ssh_port=ssh_port,
439-
ssh_user=ssh_user,
440-
ssh_key=ssh_key,
441-
ssh_key_password=ssh_key_password,
442-
ssh_password=ssh_password,
443-
winrm_username=winrm_username,
444-
winrm_password=winrm_password,
445-
winrm_port=winrm_port,
446-
winrm_transport=winrm_transport,
469+
override_data=override_data,
447470
)
448471

449472
# Attach to pseudo inventory

pyinfra_cli/util.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ def json_encode(obj):
121121
raise TypeError('Cannot serialize: {0} ({1})'.format(type(obj), obj))
122122

123123

124-
def _parse_arg(arg):
124+
def parse_cli_arg(arg):
125125
if isinstance(arg, list):
126-
return [_parse_arg(a) for a in arg]
126+
return [parse_cli_arg(a) for a in arg]
127127

128128
if arg.lower() == 'false':
129129
return False
@@ -177,12 +177,12 @@ def get_operation_and_args(commands):
177177
pass
178178

179179
args = [
180-
_parse_arg(arg)
180+
parse_cli_arg(arg)
181181
for arg in operation_args if '=' not in arg
182182
]
183183

184184
kwargs = {
185-
key: _parse_arg(value)
185+
key: parse_cli_arg(value)
186186
for key, value in [
187187
arg.split('=', 1)
188188
for arg in operation_args if '=' in arg

tests/test_api/test_api_inventory.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from unittest import TestCase
2+
3+
from pyinfra.api import Inventory
4+
5+
6+
class TestInventoryApi(TestCase):
7+
def test_create_inventory(self):
8+
inventory = Inventory((['somehost', 'anotherhost', 'anotherotherhost'], {}))
9+
assert len(inventory) == 3
10+
11+
def test_create_inventory_data(self):
12+
default_data = {'default_data': 'default_data'}
13+
inventory = Inventory((['somehost'], default_data))
14+
assert inventory.get_data() == default_data
15+
assert inventory.get_host_data('somehost') == {}
16+
17+
def test_create_inventory_host_data(self):
18+
default_data = {'default_data': 'default_data', 'host_data': 'none'}
19+
somehost_data = {'host_data': 'host_data'}
20+
21+
inventory = Inventory((
22+
[('somehost', somehost_data), 'anotherhost'],
23+
default_data,
24+
))
25+
26+
assert inventory.get_data() == default_data
27+
assert inventory.get_host_data('somehost') == somehost_data
28+
assert inventory.get_host_data('anotherhost') == {}
29+
assert inventory.get_host('anotherhost').data.host_data == 'none'
30+
31+
def test_create_inventory_override_data(self):
32+
default_data = {'default_data': 'default_data', 'override_data': 'ignored'}
33+
override_data = {'override_data': 'override_data'}
34+
somehost_data = {'host_data': 'host_data'}
35+
36+
inventory = Inventory((
37+
[('somehost', somehost_data), 'anotherhost'],
38+
default_data,
39+
), override_data=override_data)
40+
41+
assert inventory.get_data() == default_data
42+
assert inventory.get_override_data() == override_data
43+
44+
assert inventory.get_host('somehost').data.host_data == 'host_data'
45+
assert inventory.get_host('anotherhost').data.host_data is None
46+
47+
assert inventory.get_host('somehost').data.override_data == 'override_data'
48+
assert inventory.get_host('anotherhost').data.override_data == 'override_data'

tests/test_cli/test_cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ def test_deploy_operation_direct(self):
347347
sudo=False, sudo_user=None, use_sudo_password=False, su_user=None,
348348
parallel=None, fail_percent=0, dry=False, limit=None, no_wait=False, serial=False,
349349
winrm_username=None, winrm_password=None, winrm_port=None, winrm_transport=None,
350-
shell_executable=None, quiet=False,
350+
shell_executable=None, quiet=False, data=tuple(),
351351
debug=False, debug_data=False, debug_facts=False, debug_operations=False,
352352
)
353353
assert e.args == (0,)

0 commit comments

Comments
 (0)