cli2 5.0.0rc14__tar.gz → 5.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {cli2-5.0.0rc14/cli2.egg-info → cli2-5.0.2}/PKG-INFO +1 -1
  2. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/__init__.py +13 -0
  3. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/mask.py +6 -3
  4. {cli2-5.0.0rc14 → cli2-5.0.2/cli2.egg-info}/PKG-INFO +1 -1
  5. {cli2-5.0.0rc14 → cli2-5.0.2}/setup.py +1 -1
  6. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_ansible.py +64 -10
  7. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_ansible_variables.py +14 -7
  8. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_client.py +4 -0
  9. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_mask.py +5 -0
  10. {cli2-5.0.0rc14 → cli2-5.0.2}/MANIFEST.in +0 -0
  11. {cli2-5.0.0rc14 → cli2-5.0.2}/README.rst +0 -0
  12. {cli2-5.0.0rc14 → cli2-5.0.2}/classifiers.txt +0 -0
  13. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/asyncio.py +0 -0
  14. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/cli.py +0 -0
  15. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/cli2.py +0 -0
  16. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/colors.py +0 -0
  17. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/configuration.py +0 -0
  18. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/decorators.py +0 -0
  19. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/display.py +0 -0
  20. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/examples/__init__.py +0 -0
  21. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/examples/conf.py +0 -0
  22. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/examples/example.py +0 -0
  23. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/examples/example_obj.py +0 -0
  24. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/examples/nesting.py +0 -0
  25. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/examples/obj.py +0 -0
  26. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/examples/obj2.py +0 -0
  27. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/examples/test.py +0 -0
  28. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/lock.py +0 -0
  29. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/log.py +0 -0
  30. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/node.py +0 -0
  31. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/sphinx.py +0 -0
  32. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/table.py +0 -0
  33. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2/test.py +0 -0
  34. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2.egg-info/SOURCES.txt +0 -0
  35. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2.egg-info/dependency_links.txt +0 -0
  36. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2.egg-info/entry_points.txt +0 -0
  37. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2.egg-info/requires.txt +0 -0
  38. {cli2-5.0.0rc14 → cli2-5.0.2}/cli2.egg-info/top_level.txt +0 -0
  39. {cli2-5.0.0rc14 → cli2-5.0.2}/setup.cfg +0 -0
  40. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_asyncio.py +0 -0
  41. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_cli.py +0 -0
  42. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_command.py +0 -0
  43. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_configuration.py +0 -0
  44. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_decorators.py +0 -0
  45. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_display.py +0 -0
  46. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_entry_point.py +0 -0
  47. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_group.py +0 -0
  48. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_inject.py +0 -0
  49. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_lock.py +0 -0
  50. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_node.py +0 -0
  51. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_restful.py +0 -0
  52. {cli2-5.0.0rc14 → cli2-5.0.2}/tests/test_table.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cli2
3
- Version: 5.0.0rc14
3
+ Version: 5.0.2
4
4
  Summary: image:: https://yourlabs.io/oss/cli2/badges/master/pipeline.svg
5
5
  Home-page: https://yourlabs.io/oss/cli2
6
6
  Author: James Pic
@@ -30,3 +30,16 @@ def which(cmd):
30
30
  path = Path(os.getenv('HOME')) / '.local/bin' / cmd
31
31
  if path.exists():
32
32
  return str(path)
33
+
34
+
35
+ def mutable(obj):
36
+ types = (
37
+ int,
38
+ float,
39
+ str,
40
+ tuple,
41
+ frozenset,
42
+ bool,
43
+ bytes,
44
+ )
45
+ return not isinstance(obj, types)
@@ -32,8 +32,9 @@ class Mask:
32
32
  Because:
33
33
 
34
34
  - ``1337p4ssw0rD`` was given as a value to mask
35
- - ``xx`` was the value of a key named ``password`` which was given as a key
36
- to mask
35
+ - ``password``'s value because ``password`` was given as a key to match
36
+ - the Mask object learned the value of the ``password`` key, and masked it
37
+ in ``text``
37
38
 
38
39
  .. py:attribute:: keys
39
40
 
@@ -109,8 +110,10 @@ class Mask:
109
110
  data[key] = self._mask(value)
110
111
  elif isinstance(data, list):
111
112
  return [self._mask(item) for item in data]
113
+ elif isinstance(data, set):
114
+ return {self._mask(item) for item in data}
112
115
  elif isinstance(data, str):
113
- for value in self.values:
116
+ for value in sorted(self.values, key=len, reverse=True):
114
117
  data = data.replace(str(value), '***MASKED***')
115
118
  return data
116
119
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cli2
3
- Version: 5.0.0rc14
3
+ Version: 5.0.2
4
4
  Summary: image:: https://yourlabs.io/oss/cli2/badges/master/pipeline.svg
5
5
  Home-page: https://yourlabs.io/oss/cli2
6
6
  Author: James Pic
@@ -44,7 +44,7 @@ from setuptools import setup
44
44
 
45
45
  setup(
46
46
  name='cli2',
47
- version='5.0.0rc14',
47
+ version='5.0.2',
48
48
  setup_requires='setupmeta',
49
49
  packages=['cli2'],
50
50
  install_requires=[
@@ -9,17 +9,15 @@ import yaml
9
9
  import cansible
10
10
 
11
11
 
12
- class ActionModule(cansible.ActionBase):
13
- mask_keys = ['a']
14
-
15
- async def run_async(self):
16
- self.result['x'] = dict(a='a', b='b', c='c', d='foo a rrr')
17
-
18
-
19
12
  @pytest.mark.asyncio
20
13
  async def test_mask(monkeypatch):
21
- printer = mock.Mock()
22
- monkeypatch.setattr(ActionModule, 'print', printer)
14
+ class ActionModule(cansible.ActionBase):
15
+ masked_keys = ['a']
16
+ print = mock.Mock()
17
+
18
+ async def run_async(self):
19
+ self.result['x'] = dict(a='a', b='b', c='c', d='foo a rrr')
20
+
23
21
  module = await ActionModule.run_test_async(facts=dict(mask_keys=['b']))
24
22
  # result is untouched
25
23
  assert module.result == {'x':
@@ -27,7 +25,19 @@ async def test_mask(monkeypatch):
27
25
  }
28
26
  # output has proper masking
29
27
  expected = "\x1b[94mx\x1b[39;49;00m:\x1b[37m\x1b[39;49;00m\n\x1b[37m \x1b[39;49;00m\x1b[94ma\x1b[39;49;00m:\x1b[37m \x1b[39;49;00m\x1b[33m'\x1b[39;49;00m\x1b[33m***MASKED***\x1b[39;49;00m\x1b[33m'\x1b[39;49;00m\x1b[37m\x1b[39;49;00m\n\x1b[37m \x1b[39;49;00m\x1b[94mb\x1b[39;49;00m:\x1b[37m \x1b[39;49;00m\x1b[33m'\x1b[39;49;00m\x1b[33m***MASKED***\x1b[39;49;00m\x1b[33m'\x1b[39;49;00m\x1b[37m\x1b[39;49;00m\n\x1b[37m \x1b[39;49;00m\x1b[94mc\x1b[39;49;00m:\x1b[37m \x1b[39;49;00mc\x1b[37m\x1b[39;49;00m\n\x1b[37m \x1b[39;49;00m\x1b[94md\x1b[39;49;00m:\x1b[37m \x1b[39;49;00mfoo ***MASKED*** rrr\x1b[37m\x1b[39;49;00m\n" # noqa
30
- printer.assert_called_once_with(expected)
28
+ module.print.assert_called_once_with(expected, mask=False)
29
+
30
+
31
+ def test_subprocess_remote(playbook):
32
+ playbook.task_add(
33
+ 'yourlabs.test.password_get',
34
+ no_log=True,
35
+ )
36
+ result = playbook()
37
+ expected = "foo:***MASKED***:bar\n\nansible_facts:\n\n mask_values:\n\n - \'***MASKED***\'\n\nsecret: \'***MASKED***\'\n\nstdout: foo:***MASKED***:bar" # noqa
38
+ assert expected in result['stdout']
39
+
40
+
31
41
 
32
42
 
33
43
  @pytest.mark.asyncio
@@ -120,6 +130,41 @@ async def test_diff(monkeypatch):
120
130
  _print.assert_called_once_with(expected)
121
131
 
122
132
 
133
+ @pytest.mark.asyncio
134
+ async def test_fact_set():
135
+ class Action(cansible.ActionBase):
136
+ foo = cansible.Option(fact='foo')
137
+
138
+ async def run_async(self):
139
+ self.foo = 'bar'
140
+ assert self.foo == 'bar'
141
+
142
+ action = await Action.run_test_async(facts=dict(foo='bar'))
143
+ assert 'ansible_facts' not in action.result
144
+
145
+ action = await Action.run_test_async()
146
+ assert action.result['ansible_facts'] == dict(foo='bar')
147
+
148
+
149
+ @pytest.mark.asyncio
150
+ async def test_fact_set_mutable():
151
+ class Action(cansible.ActionBase):
152
+ async def run_async(self):
153
+ assert self.mask_values is self.mask.values
154
+ self.mask_values.add('foo')
155
+
156
+ action = await Action.run_test_async()
157
+ assert 'mask_values' in action.facts_values
158
+ assert action.result['ansible_facts'] == dict(mask_values=['foo'])
159
+
160
+ action = await Action.run_test_async(facts=dict(mask_values=['foo']))
161
+ assert 'ansible_facts' not in action.result
162
+
163
+ action = await Action.run_test_async(facts=dict(mask_values=['bar']))
164
+ result = action.result['ansible_facts']['mask_values']
165
+ assert sorted(result) == ['bar', 'foo']
166
+
167
+
123
168
  def test_playbook_render(playbook):
124
169
  assert playbook.name == 'test_playbook_render'
125
170
  playbook.task_add(
@@ -166,3 +211,12 @@ def test_playbook_exec(playbook):
166
211
  result = playbook()
167
212
  assert result['changed'] == 0
168
213
  assert result['ok'] == 2
214
+
215
+
216
+ def test_masking_ansible_story(playbook):
217
+ with open('tests/test_ansible_masking.yml', 'r') as f:
218
+ data = yaml.safe_load(f.read())
219
+ playbook.tasks += data[0]['tasks']
220
+ result = playbook()
221
+ expected = "hello ***MASKED*** ***MASKED*** bye\n\nansible_facts:\n\n mask_values:\n\n - \'***MASKED***\'\n\n - \'***MASKED***\'\n\ncmd: echo hello ***MASKED*** ***MASKED*** bye\n\nstdout: hello ***MASKED*** ***MASKED*** bye" # noqa
222
+ assert expected in result['stdout']
@@ -13,27 +13,34 @@ def test_story():
13
13
  assert variables['variables_vault.yml'] == dict(bar='foo')
14
14
 
15
15
 
16
- def test_exceptions():
16
+ def test_unresolvable_path_error():
17
17
  variables = cansible.Variables()
18
- with pytest.raises(Exception) as exc:
18
+ with pytest.raises(cansible.UnresolvablePathError) as exc:
19
19
  variables['variables.yml']
20
20
  assert exc.value.args == (
21
21
  'variables.yml must be absolute if root_path not set',
22
22
  )
23
23
 
24
- with pytest.raises(Exception) as exc:
25
- variables['/variables_vault.yml']
26
- assert exc.value.args == ('/variables_vault.yml does not exist',)
27
24
 
25
+ def test_path_not_found_error():
26
+ variables = cansible.Variables()
27
+ with pytest.raises(cansible.PathNotFoundError) as exc:
28
+ variables['/nonexistent.yml']
29
+ assert exc.value.args == ('/nonexistent.yml does not exist',)
30
+
31
+
32
+ def test_vault_password_required_error():
28
33
  variables = cansible.Variables(root_path=os.path.dirname(__file__))
29
- with pytest.raises(Exception) as exc:
34
+ with pytest.raises(cansible.VaultPasswordFileRequiredError) as exc:
30
35
  variables['variables_vault.yml']
31
36
  assert exc.value.args == ('Vault password required in pass_path',)
32
37
 
38
+
39
+ def test_vault_password_file_not_found_error():
33
40
  variables = cansible.Variables(
34
41
  root_path=os.path.dirname(__file__),
35
42
  pass_path='/does/not/exist',
36
43
  )
37
- with pytest.raises(Exception) as exc:
44
+ with pytest.raises(cansible.VaultPasswordFileNotFoundError) as exc:
38
45
  variables['variables_vault.yml']
39
46
  assert exc.value.args == ('/does/not/exist does not exist',)
@@ -99,6 +99,10 @@ async def test_client_cli_override(client_class, httpx_mock):
99
99
  async def find(cls, foo):
100
100
  return cls.url_list
101
101
 
102
+ @Client.cli.cmd
103
+ def test():
104
+ pass
105
+
102
106
  class TestModel2(Client.Model):
103
107
  @classmethod
104
108
  @cli2.cmd
@@ -23,3 +23,8 @@ def test_mask():
23
23
 
24
24
  assert mask
25
25
  assert not cli2.Mask()
26
+
27
+
28
+ def test_mask_longest():
29
+ mask = cli2.Mask(values=['val', 'val2'])
30
+ assert mask('val2 val') == '***MASKED*** ***MASKED***'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes