pyinfra 3.3__py2.py3-none-any.whl → 3.4__py2.py3-none-any.whl
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.
- pyinfra/api/arguments.py +8 -16
- pyinfra/api/deploy.py +1 -1
- pyinfra/api/facts.py +10 -26
- pyinfra/api/host.py +10 -4
- pyinfra/api/inventory.py +5 -2
- pyinfra/api/operation.py +1 -1
- pyinfra/api/util.py +20 -6
- pyinfra/connectors/docker.py +117 -38
- pyinfra/connectors/dockerssh.py +1 -0
- pyinfra/connectors/local.py +1 -0
- pyinfra/connectors/ssh.py +1 -0
- pyinfra/connectors/sshuserclient/client.py +5 -5
- pyinfra/connectors/terraform.py +3 -0
- pyinfra/connectors/vagrant.py +3 -0
- pyinfra/context.py +14 -5
- pyinfra/facts/brew.py +1 -0
- pyinfra/facts/docker.py +6 -2
- pyinfra/facts/git.py +10 -0
- pyinfra/facts/hardware.py +1 -1
- pyinfra/facts/opkg.py +1 -0
- pyinfra/facts/server.py +81 -23
- pyinfra/facts/systemd.py +1 -1
- pyinfra/operations/crontab.py +7 -5
- pyinfra/operations/docker.py +2 -0
- pyinfra/operations/files.py +64 -21
- pyinfra/operations/flatpak.py +17 -2
- pyinfra/operations/git.py +6 -2
- pyinfra/operations/server.py +34 -24
- pyinfra/operations/util/docker.py +4 -0
- pyinfra/operations/util/files.py +44 -3
- {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/METADATA +5 -4
- {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/RECORD +47 -47
- {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/entry_points.txt +1 -0
- pyinfra_cli/inventory.py +1 -1
- pyinfra_cli/main.py +4 -2
- tests/test_api/test_api_arguments.py +25 -20
- tests/test_api/test_api_facts.py +28 -15
- tests/test_api/test_api_operations.py +43 -44
- tests/test_cli/test_cli.py +17 -17
- tests/test_cli/test_cli_inventory.py +4 -4
- tests/test_cli/test_context_objects.py +26 -26
- tests/test_connectors/test_docker.py +83 -43
- tests/test_connectors/test_ssh.py +153 -132
- tests/test_connectors/test_sshuserclient.py +10 -5
- {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/LICENSE.md +0 -0
- {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/WHEEL +0 -0
- {pyinfra-3.3.dist-info → pyinfra-3.4.dist-info}/top_level.txt +0 -0
|
@@ -11,26 +11,13 @@ from pyinfra.connectors.util import make_unix_command
|
|
|
11
11
|
from ..util import make_inventory
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
class TestContainerConnector(TestCase):
|
|
15
|
+
# we use this class as a template to prevent the decorators from being invoked twice on
|
|
16
|
+
# the podman test class (since it needs to override fake_docker_shell)
|
|
17
|
+
__test__ = False # this class should not be tested.
|
|
18
|
+
cli_cmd = "docker"
|
|
19
|
+
connector_name = "docker"
|
|
20
20
|
|
|
21
|
-
if command == "docker rm -f containerid":
|
|
22
|
-
return []
|
|
23
|
-
|
|
24
|
-
raise PyinfraError("Invalid command: {0}".format(command))
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@patch("pyinfra.connectors.docker.local.shell", fake_docker_shell)
|
|
28
|
-
@patch("pyinfra.connectors.docker.mkstemp", lambda: (None, "__tempfile__"))
|
|
29
|
-
@patch("pyinfra.connectors.docker.os.remove", lambda f: None)
|
|
30
|
-
@patch("pyinfra.connectors.docker.os.close", lambda f: None)
|
|
31
|
-
@patch("pyinfra.connectors.docker.open", mock_open(read_data="test!"), create=True)
|
|
32
|
-
@patch("pyinfra.api.util.open", mock_open(read_data="test!"), create=True)
|
|
33
|
-
class TestDockerConnector(TestCase):
|
|
34
21
|
def setUp(self):
|
|
35
22
|
self.fake_popen_patch = patch("pyinfra.connectors.util.Popen")
|
|
36
23
|
self.fake_popen_mock = self.fake_popen_patch.start()
|
|
@@ -40,46 +27,46 @@ class TestDockerConnector(TestCase):
|
|
|
40
27
|
|
|
41
28
|
def test_missing_image(self):
|
|
42
29
|
with self.assertRaises(InventoryError):
|
|
43
|
-
make_inventory(hosts=("@
|
|
30
|
+
make_inventory(hosts=(f"@{self.connector_name}",))
|
|
44
31
|
|
|
45
32
|
def test_user_provided_container_id(self):
|
|
46
33
|
inventory = make_inventory(
|
|
47
|
-
hosts=(("@
|
|
34
|
+
hosts=((f"@{self.connector_name}/not-an-image", {"docker_container_id": "abc"}),),
|
|
48
35
|
)
|
|
49
36
|
State(inventory, Config())
|
|
50
|
-
host = inventory.get_host("@
|
|
37
|
+
host = inventory.get_host(f"@{self.connector_name}/not-an-image")
|
|
51
38
|
host.connect()
|
|
52
39
|
assert host.data.docker_container_id == "abc"
|
|
53
40
|
|
|
54
41
|
def test_connect_all(self):
|
|
55
|
-
inventory = make_inventory(hosts=("@
|
|
42
|
+
inventory = make_inventory(hosts=(f"@{self.connector_name}/not-an-image",))
|
|
56
43
|
state = State(inventory, Config())
|
|
57
44
|
connect_all(state)
|
|
58
45
|
assert len(state.active_hosts) == 1
|
|
59
46
|
|
|
60
47
|
def test_connect_all_error(self):
|
|
61
|
-
inventory = make_inventory(hosts=("@
|
|
48
|
+
inventory = make_inventory(hosts=(f"@{self.connector_name}/a-broken-image",))
|
|
62
49
|
state = State(inventory, Config())
|
|
63
50
|
|
|
64
51
|
with self.assertRaises(PyinfraError):
|
|
65
52
|
connect_all(state)
|
|
66
53
|
|
|
67
54
|
def test_connect_disconnect_host(self):
|
|
68
|
-
inventory = make_inventory(hosts=("@
|
|
55
|
+
inventory = make_inventory(hosts=(f"@{self.connector_name}/not-an-image",))
|
|
69
56
|
state = State(inventory, Config())
|
|
70
|
-
host = inventory.get_host("@
|
|
57
|
+
host = inventory.get_host(f"@{self.connector_name}/not-an-image")
|
|
71
58
|
host.connect(reason=True)
|
|
72
59
|
assert len(state.active_hosts) == 0
|
|
73
60
|
host.disconnect()
|
|
74
61
|
|
|
75
62
|
def test_run_shell_command(self):
|
|
76
|
-
inventory = make_inventory(hosts=("@
|
|
63
|
+
inventory = make_inventory(hosts=(f"@{self.connector_name}/not-an-image",))
|
|
77
64
|
State(inventory, Config())
|
|
78
65
|
|
|
79
66
|
command = "echo hi"
|
|
80
67
|
self.fake_popen_mock().returncode = 0
|
|
81
68
|
|
|
82
|
-
host = inventory.get_host("@
|
|
69
|
+
host = inventory.get_host(f"@{self.connector_name}/not-an-image")
|
|
83
70
|
host.connect()
|
|
84
71
|
out = host.run_shell_command(
|
|
85
72
|
command,
|
|
@@ -92,7 +79,7 @@ class TestDockerConnector(TestCase):
|
|
|
92
79
|
|
|
93
80
|
command = make_unix_command(command).get_raw_value()
|
|
94
81
|
command = shlex.quote(command)
|
|
95
|
-
docker_command = "
|
|
82
|
+
docker_command = f"{self.cli_cmd} exec -it containerid sh -c {command}"
|
|
96
83
|
shell_command = make_unix_command(docker_command).get_raw_value()
|
|
97
84
|
|
|
98
85
|
self.fake_popen_mock.assert_called_with(
|
|
@@ -104,34 +91,34 @@ class TestDockerConnector(TestCase):
|
|
|
104
91
|
)
|
|
105
92
|
|
|
106
93
|
def test_run_shell_command_success_exit_codes(self):
|
|
107
|
-
inventory = make_inventory(hosts=("@
|
|
94
|
+
inventory = make_inventory(hosts=(f"@{self.connector_name}/not-an-image",))
|
|
108
95
|
State(inventory, Config())
|
|
109
96
|
|
|
110
97
|
command = "echo hi"
|
|
111
98
|
self.fake_popen_mock().returncode = 1
|
|
112
99
|
|
|
113
|
-
host = inventory.get_host("@
|
|
100
|
+
host = inventory.get_host(f"@{self.connector_name}/not-an-image")
|
|
114
101
|
host.connect()
|
|
115
102
|
out = host.run_shell_command(command, _success_exit_codes=[1])
|
|
116
103
|
assert out[0] is True
|
|
117
104
|
|
|
118
105
|
def test_run_shell_command_error(self):
|
|
119
|
-
inventory = make_inventory(hosts=("@
|
|
106
|
+
inventory = make_inventory(hosts=(f"@{self.connector_name}/not-an-image",))
|
|
120
107
|
state = State(inventory, Config())
|
|
121
108
|
|
|
122
109
|
command = "echo hi"
|
|
123
110
|
self.fake_popen_mock().returncode = 1
|
|
124
111
|
|
|
125
|
-
host = inventory.get_host("@
|
|
112
|
+
host = inventory.get_host(f"@{self.connector_name}/not-an-image")
|
|
126
113
|
host.connect(state)
|
|
127
114
|
out = host.run_shell_command(command)
|
|
128
115
|
assert out[0] is False
|
|
129
116
|
|
|
130
117
|
def test_put_file(self):
|
|
131
|
-
inventory = make_inventory(hosts=("@
|
|
118
|
+
inventory = make_inventory(hosts=(f"@{self.connector_name}/not-an-image",))
|
|
132
119
|
State(inventory, Config())
|
|
133
120
|
|
|
134
|
-
host = inventory.get_host("@
|
|
121
|
+
host = inventory.get_host(f"@{self.connector_name}/not-an-image")
|
|
135
122
|
host.connect()
|
|
136
123
|
|
|
137
124
|
fake_process = MagicMock(returncode=0)
|
|
@@ -140,7 +127,7 @@ class TestDockerConnector(TestCase):
|
|
|
140
127
|
host.put_file("not-a-file", "not-another-file", print_output=True)
|
|
141
128
|
|
|
142
129
|
self.fake_popen_mock.assert_called_with(
|
|
143
|
-
"sh -c '
|
|
130
|
+
f"sh -c '{self.cli_cmd} cp __tempfile__ containerid:not-another-file'",
|
|
144
131
|
shell=True,
|
|
145
132
|
stdout=PIPE,
|
|
146
133
|
stderr=PIPE,
|
|
@@ -148,10 +135,10 @@ class TestDockerConnector(TestCase):
|
|
|
148
135
|
)
|
|
149
136
|
|
|
150
137
|
def test_put_file_error(self):
|
|
151
|
-
inventory = make_inventory(hosts=("@
|
|
138
|
+
inventory = make_inventory(hosts=(f"@{self.connector_name}/not-an-image",))
|
|
152
139
|
State(inventory, Config())
|
|
153
140
|
|
|
154
|
-
host = inventory.get_host("@
|
|
141
|
+
host = inventory.get_host(f"@{self.connector_name}/not-an-image")
|
|
155
142
|
host.connect()
|
|
156
143
|
|
|
157
144
|
fake_process = MagicMock(returncode=1)
|
|
@@ -161,10 +148,10 @@ class TestDockerConnector(TestCase):
|
|
|
161
148
|
host.put_file("not-a-file", "not-another-file", print_output=True)
|
|
162
149
|
|
|
163
150
|
def test_get_file(self):
|
|
164
|
-
inventory = make_inventory(hosts=("@
|
|
151
|
+
inventory = make_inventory(hosts=(f"@{self.connector_name}/not-an-image",))
|
|
165
152
|
State(inventory, Config())
|
|
166
153
|
|
|
167
|
-
host = inventory.get_host("@
|
|
154
|
+
host = inventory.get_host(f"@{self.connector_name}/not-an-image")
|
|
168
155
|
host.connect()
|
|
169
156
|
|
|
170
157
|
fake_process = MagicMock(returncode=0)
|
|
@@ -173,7 +160,7 @@ class TestDockerConnector(TestCase):
|
|
|
173
160
|
host.get_file("not-a-file", "not-another-file", print_output=True)
|
|
174
161
|
|
|
175
162
|
self.fake_popen_mock.assert_called_with(
|
|
176
|
-
"sh -c '
|
|
163
|
+
f"sh -c '{self.cli_cmd} cp containerid:not-a-file __tempfile__'",
|
|
177
164
|
shell=True,
|
|
178
165
|
stdout=PIPE,
|
|
179
166
|
stderr=PIPE,
|
|
@@ -181,10 +168,10 @@ class TestDockerConnector(TestCase):
|
|
|
181
168
|
)
|
|
182
169
|
|
|
183
170
|
def test_get_file_error(self):
|
|
184
|
-
inventory = make_inventory(hosts=("@
|
|
171
|
+
inventory = make_inventory(hosts=(f"@{self.connector_name}/not-an-image",))
|
|
185
172
|
State(inventory, Config())
|
|
186
173
|
|
|
187
|
-
host = inventory.get_host("@
|
|
174
|
+
host = inventory.get_host(f"@{self.connector_name}/not-an-image")
|
|
188
175
|
host.connect()
|
|
189
176
|
|
|
190
177
|
fake_process = MagicMock(returncode=1)
|
|
@@ -192,3 +179,56 @@ class TestDockerConnector(TestCase):
|
|
|
192
179
|
|
|
193
180
|
with self.assertRaises(IOError):
|
|
194
181
|
host.get_file("not-a-file", "not-another-file", print_output=True)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# Reuse the container testing code for docker and podman
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def fake_docker_shell(command, splitlines=None):
|
|
188
|
+
if command == "docker run -d not-an-image tail -f /dev/null":
|
|
189
|
+
return ["containerid"]
|
|
190
|
+
|
|
191
|
+
if command == "docker commit containerid":
|
|
192
|
+
return ["sha256:blahsomerandomstringdata"]
|
|
193
|
+
|
|
194
|
+
if command == "docker rm -f containerid":
|
|
195
|
+
return []
|
|
196
|
+
|
|
197
|
+
raise PyinfraError("Invalid command: {0}".format(command))
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@patch("pyinfra.connectors.docker.local.shell", fake_docker_shell)
|
|
201
|
+
@patch("pyinfra.connectors.docker.mkstemp", lambda: (None, "__tempfile__"))
|
|
202
|
+
@patch("pyinfra.connectors.docker.os.remove", lambda f: None)
|
|
203
|
+
@patch("pyinfra.connectors.docker.os.close", lambda f: None)
|
|
204
|
+
@patch("pyinfra.connectors.docker.open", mock_open(read_data="test!"), create=True)
|
|
205
|
+
@patch("pyinfra.api.util.open", mock_open(read_data="test!"), create=True)
|
|
206
|
+
class TestDocker2Connector(TestContainerConnector):
|
|
207
|
+
__test__ = True
|
|
208
|
+
cli_cmd = "docker"
|
|
209
|
+
connector_name = "docker"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def fake_podman_shell(command, splitlines=None):
|
|
213
|
+
if command == "podman run -d not-an-image tail -f /dev/null":
|
|
214
|
+
return ["containerid"]
|
|
215
|
+
|
|
216
|
+
if command == "podman commit containerid":
|
|
217
|
+
return ["sha256:blahsomerandomstringdata"]
|
|
218
|
+
|
|
219
|
+
if command == "podman rm -f containerid":
|
|
220
|
+
return []
|
|
221
|
+
|
|
222
|
+
raise PyinfraError("Invalid command: {0}".format(command))
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@patch("pyinfra.connectors.docker.local.shell", fake_podman_shell)
|
|
226
|
+
@patch("pyinfra.connectors.docker.mkstemp", lambda: (None, "__tempfile__"))
|
|
227
|
+
@patch("pyinfra.connectors.docker.os.remove", lambda f: None)
|
|
228
|
+
@patch("pyinfra.connectors.docker.os.close", lambda f: None)
|
|
229
|
+
@patch("pyinfra.connectors.docker.open", mock_open(read_data="test!"), create=True)
|
|
230
|
+
@patch("pyinfra.api.util.open", mock_open(read_data="test!"), create=True)
|
|
231
|
+
class TestPodmanConnector(TestContainerConnector):
|
|
232
|
+
__test__ = True
|
|
233
|
+
cli_cmd = "podman"
|
|
234
|
+
connector_name = "podman"
|