pyinfra 2.9.2__py2.py3-none-any.whl → 3.0__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/__init__.py +3 -0
- pyinfra/api/arguments.py +265 -253
- pyinfra/api/arguments_typed.py +80 -0
- pyinfra/api/command.py +68 -53
- pyinfra/api/config.py +139 -32
- pyinfra/api/connect.py +1 -1
- pyinfra/api/connectors.py +7 -26
- pyinfra/api/deploy.py +21 -52
- pyinfra/api/exceptions.py +33 -8
- pyinfra/api/facts.py +102 -137
- pyinfra/api/host.py +150 -82
- pyinfra/api/inventory.py +21 -25
- pyinfra/api/operation.py +240 -198
- pyinfra/api/operations.py +102 -148
- pyinfra/api/state.py +137 -79
- pyinfra/api/util.py +79 -86
- pyinfra/connectors/base.py +147 -0
- pyinfra/connectors/chroot.py +160 -169
- pyinfra/connectors/docker.py +220 -237
- pyinfra/connectors/dockerssh.py +231 -253
- pyinfra/connectors/local.py +196 -208
- pyinfra/connectors/ssh.py +530 -613
- pyinfra/connectors/ssh_util.py +114 -0
- pyinfra/connectors/sshuserclient/client.py +5 -3
- pyinfra/connectors/terraform.py +86 -65
- pyinfra/connectors/util.py +211 -137
- pyinfra/connectors/vagrant.py +60 -53
- pyinfra/context.py +4 -2
- pyinfra/facts/apk.py +2 -0
- pyinfra/facts/apt.py +2 -0
- pyinfra/facts/brew.py +2 -0
- pyinfra/facts/bsdinit.py +2 -0
- pyinfra/facts/cargo.py +2 -0
- pyinfra/facts/choco.py +2 -0
- pyinfra/facts/deb.py +7 -2
- pyinfra/facts/dnf.py +2 -0
- pyinfra/facts/docker.py +19 -0
- pyinfra/facts/files.py +47 -32
- pyinfra/facts/gem.py +2 -0
- pyinfra/facts/git.py +3 -1
- pyinfra/facts/gpg.py +3 -1
- pyinfra/facts/hardware.py +34 -24
- pyinfra/facts/iptables.py +5 -3
- pyinfra/facts/launchd.py +2 -0
- pyinfra/facts/lxd.py +2 -0
- pyinfra/facts/mysql.py +13 -6
- pyinfra/facts/npm.py +1 -0
- pyinfra/facts/openrc.py +2 -0
- pyinfra/facts/pacman.py +6 -2
- pyinfra/facts/pip.py +2 -0
- pyinfra/facts/pkg.py +2 -0
- pyinfra/facts/pkgin.py +2 -0
- pyinfra/facts/postgres.py +168 -0
- pyinfra/facts/postgresql.py +6 -160
- pyinfra/facts/rpm.py +12 -9
- pyinfra/facts/runit.py +68 -0
- pyinfra/facts/selinux.py +3 -1
- pyinfra/facts/server.py +80 -36
- pyinfra/facts/snap.py +2 -0
- pyinfra/facts/systemd.py +31 -12
- pyinfra/facts/sysvinit.py +10 -10
- pyinfra/facts/upstart.py +2 -0
- pyinfra/facts/util/packaging.py +7 -4
- pyinfra/facts/vzctl.py +2 -0
- pyinfra/facts/xbps.py +2 -0
- pyinfra/facts/yum.py +2 -0
- pyinfra/facts/zypper.py +2 -0
- pyinfra/local.py +4 -5
- pyinfra/operations/apk.py +6 -4
- pyinfra/operations/apt.py +46 -65
- pyinfra/operations/brew.py +17 -22
- pyinfra/operations/bsdinit.py +9 -7
- pyinfra/operations/cargo.py +4 -2
- pyinfra/operations/choco.py +4 -2
- pyinfra/operations/dnf.py +19 -23
- pyinfra/operations/docker.py +339 -0
- pyinfra/operations/files.py +188 -386
- pyinfra/operations/gem.py +4 -2
- pyinfra/operations/git.py +24 -53
- pyinfra/operations/iptables.py +29 -35
- pyinfra/operations/launchd.py +6 -7
- pyinfra/operations/lxd.py +8 -13
- pyinfra/operations/mysql.py +62 -81
- pyinfra/operations/npm.py +9 -2
- pyinfra/operations/openrc.py +6 -4
- pyinfra/operations/pacman.py +7 -8
- pyinfra/operations/pip.py +25 -24
- pyinfra/operations/pkg.py +4 -2
- pyinfra/operations/pkgin.py +6 -4
- pyinfra/operations/postgres.py +349 -0
- pyinfra/operations/postgresql.py +18 -379
- pyinfra/operations/puppet.py +3 -1
- pyinfra/operations/python.py +8 -19
- pyinfra/operations/runit.py +182 -0
- pyinfra/operations/selinux.py +47 -44
- pyinfra/operations/server.py +111 -127
- pyinfra/operations/snap.py +4 -4
- pyinfra/operations/ssh.py +20 -33
- pyinfra/operations/systemd.py +19 -15
- pyinfra/operations/sysvinit.py +9 -16
- pyinfra/operations/upstart.py +9 -7
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/docker.py +177 -0
- pyinfra/operations/util/files.py +24 -16
- pyinfra/operations/util/packaging.py +55 -57
- pyinfra/operations/util/service.py +39 -51
- pyinfra/operations/vzctl.py +12 -10
- pyinfra/operations/xbps.py +6 -4
- pyinfra/operations/yum.py +18 -22
- pyinfra/operations/zypper.py +12 -13
- pyinfra/version.py +5 -2
- {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/METADATA +40 -41
- pyinfra-3.0.dist-info/RECORD +167 -0
- {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/WHEEL +1 -1
- pyinfra-3.0.dist-info/entry_points.txt +11 -0
- pyinfra_cli/__main__.py +4 -3
- pyinfra_cli/commands.py +7 -2
- pyinfra_cli/exceptions.py +78 -42
- pyinfra_cli/inventory.py +40 -6
- pyinfra_cli/log.py +17 -3
- pyinfra_cli/main.py +133 -90
- pyinfra_cli/prints.py +95 -127
- pyinfra_cli/util.py +62 -29
- tests/test_api/test_api.py +2 -0
- tests/test_api/test_api_arguments.py +13 -13
- tests/test_api/test_api_deploys.py +28 -29
- tests/test_api/test_api_facts.py +60 -98
- tests/test_api/test_api_operations.py +101 -201
- tests/test_cli/test_cli.py +18 -49
- tests/test_cli/test_cli_deploy.py +11 -37
- tests/test_cli/test_cli_exceptions.py +50 -19
- tests/test_cli/util.py +1 -1
- tests/test_connectors/test_chroot.py +6 -6
- tests/test_connectors/test_docker.py +4 -4
- tests/test_connectors/test_dockerssh.py +38 -50
- tests/test_connectors/test_local.py +11 -12
- tests/test_connectors/test_ssh.py +105 -93
- tests/test_connectors/test_terraform.py +9 -15
- tests/test_connectors/test_util.py +24 -46
- tests/test_connectors/test_vagrant.py +7 -7
- pyinfra/api/operation.pyi +0 -117
- pyinfra/connectors/ansible.py +0 -171
- pyinfra/connectors/mech.py +0 -186
- pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
- pyinfra/connectors/winrm.py +0 -320
- pyinfra/facts/windows.py +0 -366
- pyinfra/facts/windows_files.py +0 -90
- pyinfra/operations/windows.py +0 -59
- pyinfra/operations/windows_files.py +0 -551
- pyinfra-2.9.2.dist-info/RECORD +0 -170
- pyinfra-2.9.2.dist-info/entry_points.txt +0 -14
- tests/test_connectors/test_ansible.py +0 -64
- tests/test_connectors/test_mech.py +0 -126
- tests/test_connectors/test_winrm.py +0 -76
- {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/LICENSE.md +0 -0
- {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/top_level.txt +0 -0
|
@@ -18,6 +18,7 @@ from pyinfra.api.connect import connect_all, disconnect_all
|
|
|
18
18
|
from pyinfra.api.exceptions import PyinfraError
|
|
19
19
|
from pyinfra.api.operation import OperationMeta, add_op
|
|
20
20
|
from pyinfra.api.operations import run_ops
|
|
21
|
+
from pyinfra.api.state import StateOperationMeta
|
|
21
22
|
from pyinfra.context import ctx_host, ctx_state
|
|
22
23
|
from pyinfra.operations import files, python, server
|
|
23
24
|
|
|
@@ -27,12 +28,12 @@ from ..util import make_inventory
|
|
|
27
28
|
|
|
28
29
|
class TestOperationMeta(TestCase):
|
|
29
30
|
def test_operation_meta_repr_no_change(self):
|
|
30
|
-
op_meta = OperationMeta("hash",
|
|
31
|
-
assert repr(op_meta) == "OperationMeta(
|
|
31
|
+
op_meta = OperationMeta("hash", False)
|
|
32
|
+
assert repr(op_meta) == "OperationMeta(executed=False, maybeChange=False, hash=hash)"
|
|
32
33
|
|
|
33
34
|
def test_operation_meta_repr_changes(self):
|
|
34
|
-
op_meta = OperationMeta("hash",
|
|
35
|
-
assert repr(op_meta) == "OperationMeta(
|
|
35
|
+
op_meta = OperationMeta("hash", True)
|
|
36
|
+
assert repr(op_meta) == "OperationMeta(executed=False, maybeChange=True, hash=hash)"
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
class TestOperationsApi(PatchSSHTestCase):
|
|
@@ -60,11 +61,11 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
60
61
|
group="pyinfra",
|
|
61
62
|
mode="644",
|
|
62
63
|
create_remote_dir=False,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
_sudo=True,
|
|
65
|
+
_sudo_user="test_sudo",
|
|
66
|
+
_su_user="test_su",
|
|
67
|
+
_ignore_errors=True,
|
|
68
|
+
_env={
|
|
68
69
|
"TEST": "what",
|
|
69
70
|
},
|
|
70
71
|
)
|
|
@@ -77,68 +78,43 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
77
78
|
first_op_hash = op_order[0]
|
|
78
79
|
|
|
79
80
|
# Ensure the op name
|
|
80
|
-
assert state.op_meta[first_op_hash]
|
|
81
|
+
assert state.op_meta[first_op_hash].names == {"files.file"}
|
|
82
|
+
|
|
83
|
+
# Ensure the global kwargs (same for both hosts)
|
|
84
|
+
somehost_global_arguments = state.ops[somehost][first_op_hash].global_arguments
|
|
85
|
+
assert somehost_global_arguments["_sudo"] is True
|
|
86
|
+
assert somehost_global_arguments["_sudo_user"] == "test_sudo"
|
|
87
|
+
assert somehost_global_arguments["_su_user"] == "test_su"
|
|
88
|
+
assert somehost_global_arguments["_ignore_errors"] is True
|
|
89
|
+
|
|
90
|
+
anotherhost_global_arguments = state.ops[anotherhost][first_op_hash].global_arguments
|
|
91
|
+
assert anotherhost_global_arguments["_sudo"] is True
|
|
92
|
+
assert anotherhost_global_arguments["_sudo_user"] == "test_sudo"
|
|
93
|
+
assert anotherhost_global_arguments["_su_user"] == "test_su"
|
|
94
|
+
assert anotherhost_global_arguments["_ignore_errors"] is True
|
|
95
|
+
|
|
96
|
+
# Ensure run ops works
|
|
97
|
+
run_ops(state)
|
|
81
98
|
|
|
82
99
|
# Ensure the commands
|
|
83
|
-
assert state.ops[somehost][first_op_hash]
|
|
100
|
+
assert state.ops[somehost][first_op_hash].operation_meta._commands == [
|
|
84
101
|
StringCommand("touch /var/log/pyinfra.log"),
|
|
85
102
|
StringCommand("chmod 644 /var/log/pyinfra.log"),
|
|
86
103
|
StringCommand("chown pyinfra:pyinfra /var/log/pyinfra.log"),
|
|
87
104
|
]
|
|
88
105
|
|
|
89
|
-
# Ensure the global kwargs (same for both hosts)
|
|
90
|
-
somehost_global_kwargs = state.ops[somehost][first_op_hash]["global_kwargs"]
|
|
91
|
-
assert somehost_global_kwargs["sudo"] is True
|
|
92
|
-
assert somehost_global_kwargs["sudo_user"] == "test_sudo"
|
|
93
|
-
assert somehost_global_kwargs["su_user"] == "test_su"
|
|
94
|
-
assert somehost_global_kwargs["ignore_errors"] is True
|
|
95
|
-
|
|
96
|
-
anotherhost_global_kwargs = state.ops[anotherhost][first_op_hash]["global_kwargs"]
|
|
97
|
-
assert anotherhost_global_kwargs["sudo"] is True
|
|
98
|
-
assert anotherhost_global_kwargs["sudo_user"] == "test_sudo"
|
|
99
|
-
assert anotherhost_global_kwargs["su_user"] == "test_su"
|
|
100
|
-
assert anotherhost_global_kwargs["ignore_errors"] is True
|
|
101
|
-
|
|
102
|
-
# Ensure run ops works
|
|
103
|
-
run_ops(state)
|
|
104
|
-
|
|
105
106
|
# Ensure ops completed OK
|
|
106
|
-
assert state.results[somehost]
|
|
107
|
-
assert state.results[somehost]
|
|
108
|
-
assert state.results[anotherhost]
|
|
109
|
-
assert state.results[anotherhost]
|
|
107
|
+
assert state.results[somehost].success_ops == 1
|
|
108
|
+
assert state.results[somehost].ops == 1
|
|
109
|
+
assert state.results[anotherhost].success_ops == 1
|
|
110
|
+
assert state.results[anotherhost].ops == 1
|
|
110
111
|
|
|
111
112
|
# And w/o errors
|
|
112
|
-
assert state.results[somehost]
|
|
113
|
-
assert state.results[anotherhost]
|
|
114
|
-
|
|
115
|
-
# And with the different modes
|
|
116
|
-
run_ops(state, serial=True)
|
|
117
|
-
run_ops(state, no_wait=True)
|
|
113
|
+
assert state.results[somehost].error_ops == 0
|
|
114
|
+
assert state.results[anotherhost].error_ops == 0
|
|
118
115
|
|
|
119
116
|
disconnect_all(state)
|
|
120
117
|
|
|
121
|
-
# def test_op_call_direct_falls(self):
|
|
122
|
-
# inventory = make_inventory()
|
|
123
|
-
# somehost = inventory.get_host('somehost')
|
|
124
|
-
# state = State(inventory, Config())
|
|
125
|
-
|
|
126
|
-
# # Enable printing on this test to catch any exceptions in the formatting
|
|
127
|
-
# state.print_output = True
|
|
128
|
-
# state.print_input = True
|
|
129
|
-
# state.print_fact_info = True
|
|
130
|
-
# state.print_noop_info = True
|
|
131
|
-
|
|
132
|
-
# connect_all(state)
|
|
133
|
-
|
|
134
|
-
# with self.assertRaises(PyinfraError) as context:
|
|
135
|
-
# server.shell(commands='echo hi')
|
|
136
|
-
|
|
137
|
-
# assert context.exception.args[0] == (
|
|
138
|
-
# 'Operation order number not provided in API mode - '
|
|
139
|
-
# 'you must use `add_op` to add operations.'
|
|
140
|
-
# )
|
|
141
|
-
|
|
142
118
|
@patch("pyinfra.api.util.open", mock_open(read_data="test!"), create=True)
|
|
143
119
|
@patch("pyinfra.operations.files.os.path.isfile", lambda *args, **kwargs: True)
|
|
144
120
|
def test_file_upload_op(self):
|
|
@@ -162,8 +138,8 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
162
138
|
files.put,
|
|
163
139
|
src="files/file.txt",
|
|
164
140
|
dest="/home/vagrant/file.txt",
|
|
165
|
-
|
|
166
|
-
|
|
141
|
+
_sudo=True,
|
|
142
|
+
_sudo_user="pyinfra",
|
|
167
143
|
)
|
|
168
144
|
|
|
169
145
|
# And with su
|
|
@@ -172,8 +148,8 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
172
148
|
files.put,
|
|
173
149
|
src="files/file.txt",
|
|
174
150
|
dest="/home/vagrant/file.txt",
|
|
175
|
-
|
|
176
|
-
|
|
151
|
+
_sudo=True,
|
|
152
|
+
_su_user="pyinfra",
|
|
177
153
|
)
|
|
178
154
|
|
|
179
155
|
op_order = state.get_op_order()
|
|
@@ -185,36 +161,36 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
185
161
|
second_op_hash = op_order[1]
|
|
186
162
|
|
|
187
163
|
# Ensure first op is the right one
|
|
188
|
-
assert state.op_meta[first_op_hash]
|
|
164
|
+
assert state.op_meta[first_op_hash].names == {"First op name"}
|
|
189
165
|
|
|
190
166
|
somehost = inventory.get_host("somehost")
|
|
191
167
|
anotherhost = inventory.get_host("anotherhost")
|
|
192
168
|
|
|
193
|
-
# Ensure first op has the right (upload) command
|
|
194
|
-
assert state.ops[somehost][first_op_hash]["commands"] == [
|
|
195
|
-
StringCommand("mkdir -p /home/vagrant"),
|
|
196
|
-
FileUploadCommand("files/file.txt", "/home/vagrant/file.txt"),
|
|
197
|
-
]
|
|
198
|
-
|
|
199
169
|
# Ensure second op has sudo/sudo_user
|
|
200
|
-
assert state.ops[somehost][second_op_hash]["
|
|
201
|
-
assert state.ops[somehost][second_op_hash]["
|
|
170
|
+
assert state.ops[somehost][second_op_hash].global_arguments["_sudo"] is True
|
|
171
|
+
assert state.ops[somehost][second_op_hash].global_arguments["_sudo_user"] == "pyinfra"
|
|
202
172
|
|
|
203
173
|
# Ensure third has su_user
|
|
204
|
-
assert state.ops[somehost][op_order[2]]["
|
|
174
|
+
assert state.ops[somehost][op_order[2]].global_arguments["_su_user"] == "pyinfra"
|
|
205
175
|
|
|
206
176
|
# Check run ops works
|
|
207
177
|
run_ops(state)
|
|
208
178
|
|
|
179
|
+
# Ensure first op used the right (upload) command
|
|
180
|
+
assert state.ops[somehost][first_op_hash].operation_meta._commands == [
|
|
181
|
+
StringCommand("mkdir -p /home/vagrant"),
|
|
182
|
+
FileUploadCommand("files/file.txt", "/home/vagrant/file.txt"),
|
|
183
|
+
]
|
|
184
|
+
|
|
209
185
|
# Ensure ops completed OK
|
|
210
|
-
assert state.results[somehost]
|
|
211
|
-
assert state.results[somehost]
|
|
212
|
-
assert state.results[anotherhost]
|
|
213
|
-
assert state.results[anotherhost]
|
|
186
|
+
assert state.results[somehost].success_ops == 3
|
|
187
|
+
assert state.results[somehost].ops == 3
|
|
188
|
+
assert state.results[anotherhost].success_ops == 3
|
|
189
|
+
assert state.results[anotherhost].ops == 3
|
|
214
190
|
|
|
215
191
|
# And w/o errors
|
|
216
|
-
assert state.results[somehost]
|
|
217
|
-
assert state.results[anotherhost]
|
|
192
|
+
assert state.results[somehost].error_ops == 0
|
|
193
|
+
assert state.results[anotherhost].error_ops == 0
|
|
218
194
|
|
|
219
195
|
def test_file_download_op(self):
|
|
220
196
|
inventory = make_inventory()
|
|
@@ -236,25 +212,25 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
236
212
|
assert len(op_order) == 1
|
|
237
213
|
|
|
238
214
|
first_op_hash = op_order[0]
|
|
239
|
-
assert state.op_meta[first_op_hash]
|
|
215
|
+
assert state.op_meta[first_op_hash].names == {"First op name"}
|
|
240
216
|
|
|
241
217
|
somehost = inventory.get_host("somehost")
|
|
242
218
|
anotherhost = inventory.get_host("anotherhost")
|
|
243
219
|
|
|
220
|
+
with patch("pyinfra.api.util.open", mock_open(read_data="test!"), create=True):
|
|
221
|
+
run_ops(state)
|
|
222
|
+
|
|
244
223
|
# Ensure first op has the right (upload) command
|
|
245
|
-
assert state.ops[somehost][first_op_hash]
|
|
224
|
+
assert state.ops[somehost][first_op_hash].operation_meta._commands == [
|
|
246
225
|
FileDownloadCommand("/home/vagrant/file.txt", "files/file.txt"),
|
|
247
226
|
]
|
|
248
227
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
assert state.results[
|
|
253
|
-
assert state.results[somehost]
|
|
254
|
-
assert state.results[anotherhost]
|
|
255
|
-
assert state.results[anotherhost]["ops"] == 1
|
|
256
|
-
assert state.results[somehost]["error_ops"] == 0
|
|
257
|
-
assert state.results[anotherhost]["error_ops"] == 0
|
|
228
|
+
assert state.results[somehost].success_ops == 1
|
|
229
|
+
assert state.results[somehost].ops == 1
|
|
230
|
+
assert state.results[anotherhost].success_ops == 1
|
|
231
|
+
assert state.results[anotherhost].ops == 1
|
|
232
|
+
assert state.results[somehost].error_ops == 0
|
|
233
|
+
assert state.results[anotherhost].error_ops == 0
|
|
258
234
|
|
|
259
235
|
def test_function_call_op(self):
|
|
260
236
|
inventory = make_inventory()
|
|
@@ -283,7 +259,7 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
283
259
|
connect_all(state)
|
|
284
260
|
|
|
285
261
|
# Add a run once op
|
|
286
|
-
add_op(state, server.shell, 'echo "hi"',
|
|
262
|
+
add_op(state, server.shell, 'echo "hi"', _run_once=True, _serial=True)
|
|
287
263
|
|
|
288
264
|
# Ensure it's added to op_order
|
|
289
265
|
assert len(state.get_op_order()) == 1
|
|
@@ -297,17 +273,15 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
297
273
|
# Check run works
|
|
298
274
|
run_ops(state)
|
|
299
275
|
|
|
300
|
-
assert (
|
|
301
|
-
state.results[somehost]["success_ops"] + state.results[anotherhost]["success_ops"]
|
|
302
|
-
) == 1
|
|
276
|
+
assert (state.results[somehost].success_ops + state.results[anotherhost].success_ops) == 1
|
|
303
277
|
|
|
278
|
+
@patch("pyinfra.connectors.ssh.SSHConnector.check_can_rsync", lambda _: True)
|
|
304
279
|
def test_rsync_op(self):
|
|
305
280
|
inventory = make_inventory(hosts=("somehost",))
|
|
306
281
|
state = State(inventory, Config())
|
|
307
282
|
connect_all(state)
|
|
308
283
|
|
|
309
|
-
|
|
310
|
-
add_op(state, files.rsync, "src", "dest", sudo=True, sudo_user="root")
|
|
284
|
+
add_op(state, files.rsync, "src", "dest", _sudo=True, _sudo_user="root")
|
|
311
285
|
|
|
312
286
|
assert len(state.get_op_order()) == 1
|
|
313
287
|
|
|
@@ -318,20 +292,20 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
318
292
|
fake_run_local_process.assert_called_with(
|
|
319
293
|
(
|
|
320
294
|
"rsync -ax --delete --rsh "
|
|
321
|
-
'"ssh -o BatchMode=yes"'
|
|
295
|
+
'"ssh -o BatchMode=yes -o \\"StrictHostKeyChecking=accept-new\\""'
|
|
322
296
|
" --rsync-path 'sudo -u root rsync' src vagrant@somehost:dest"
|
|
323
297
|
),
|
|
324
298
|
print_output=False,
|
|
325
299
|
print_prefix=inventory.get_host("somehost").print_prefix,
|
|
326
300
|
)
|
|
327
301
|
|
|
302
|
+
@patch("pyinfra.connectors.ssh.SSHConnector.check_can_rsync", lambda _: True)
|
|
328
303
|
def test_rsync_op_with_strict_host_key_checking_disabled(self):
|
|
329
304
|
inventory = make_inventory(hosts=(("somehost", {"ssh_strict_host_key_checking": "no"}),))
|
|
330
305
|
state = State(inventory, Config())
|
|
331
306
|
connect_all(state)
|
|
332
307
|
|
|
333
|
-
|
|
334
|
-
add_op(state, files.rsync, "src", "dest", sudo=True, sudo_user="root")
|
|
308
|
+
add_op(state, files.rsync, "src", "dest", _sudo=True, _sudo_user="root")
|
|
335
309
|
|
|
336
310
|
assert len(state.get_op_order()) == 1
|
|
337
311
|
|
|
@@ -349,6 +323,7 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
349
323
|
print_prefix=inventory.get_host("somehost").print_prefix,
|
|
350
324
|
)
|
|
351
325
|
|
|
326
|
+
@patch("pyinfra.connectors.ssh.SSHConnector.check_can_rsync", lambda _: True)
|
|
352
327
|
def test_rsync_op_with_strict_host_key_checking_disabled_and_custom_config_file(self):
|
|
353
328
|
inventory = make_inventory(
|
|
354
329
|
hosts=(
|
|
@@ -364,8 +339,7 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
364
339
|
state = State(inventory, Config())
|
|
365
340
|
connect_all(state)
|
|
366
341
|
|
|
367
|
-
|
|
368
|
-
add_op(state, files.rsync, "src", "dest", sudo=True, sudo_user="root")
|
|
342
|
+
add_op(state, files.rsync, "src", "dest", _sudo=True, _sudo_user="root")
|
|
369
343
|
|
|
370
344
|
assert len(state.get_op_order()) == 1
|
|
371
345
|
|
|
@@ -384,6 +358,7 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
384
358
|
print_prefix=inventory.get_host("somehost").print_prefix,
|
|
385
359
|
)
|
|
386
360
|
|
|
361
|
+
@patch("pyinfra.connectors.ssh.SSHConnector.check_can_rsync", lambda _: True)
|
|
387
362
|
def test_rsync_op_with_sanitized_custom_config_file(self):
|
|
388
363
|
inventory = make_inventory(
|
|
389
364
|
hosts=(("somehost", {"ssh_config_file": "/home/me/ssh_test_config && echo hi"}),)
|
|
@@ -391,8 +366,7 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
391
366
|
state = State(inventory, Config())
|
|
392
367
|
connect_all(state)
|
|
393
368
|
|
|
394
|
-
|
|
395
|
-
add_op(state, files.rsync, "src", "dest", sudo=True, sudo_user="root")
|
|
369
|
+
add_op(state, files.rsync, "src", "dest", _sudo=True, _sudo_user="root")
|
|
396
370
|
|
|
397
371
|
assert len(state.get_op_order()) == 1
|
|
398
372
|
|
|
@@ -403,7 +377,8 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
403
377
|
fake_run_local_process.assert_called_with(
|
|
404
378
|
(
|
|
405
379
|
"rsync -ax --delete --rsh "
|
|
406
|
-
"
|
|
380
|
+
'"ssh -o BatchMode=yes -o \\"StrictHostKeyChecking=accept-new\\" '
|
|
381
|
+
"-F '/home/me/ssh_test_config && echo hi'\""
|
|
407
382
|
" --rsync-path 'sudo -u root rsync' src vagrant@somehost:dest"
|
|
408
383
|
),
|
|
409
384
|
print_output=False,
|
|
@@ -415,7 +390,7 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
415
390
|
state = State(inventory, Config())
|
|
416
391
|
connect_all(state)
|
|
417
392
|
|
|
418
|
-
with patch("pyinfra.connectors.ssh.
|
|
393
|
+
with patch("pyinfra.connectors.ssh.which", lambda x: None):
|
|
419
394
|
with self.assertRaises(OperationError) as context:
|
|
420
395
|
add_op(state, files.rsync, "src", "dest")
|
|
421
396
|
|
|
@@ -430,14 +405,16 @@ class TestOperationsApi(PatchSSHTestCase):
|
|
|
430
405
|
def setdefault(self, key, _):
|
|
431
406
|
return self[key]
|
|
432
407
|
|
|
433
|
-
|
|
408
|
+
op_meta_item = StateOperationMeta(tuple())
|
|
409
|
+
op_meta_item.global_arguments = {"_serial": True}
|
|
410
|
+
state.op_meta = NoSetDefaultDict(lambda: op_meta_item)
|
|
434
411
|
|
|
435
412
|
connect_all(state)
|
|
436
413
|
|
|
437
414
|
with self.assertRaises(OperationValueError) as context:
|
|
438
|
-
add_op(state, files.file, "/var/log/pyinfra.log",
|
|
415
|
+
add_op(state, files.file, "/var/log/pyinfra.log", _serial=False)
|
|
439
416
|
|
|
440
|
-
assert context.exception.args[0] == "Cannot have different values for `
|
|
417
|
+
assert context.exception.args[0] == "Cannot have different values for `_serial`."
|
|
441
418
|
|
|
442
419
|
|
|
443
420
|
class TestNestedOperationsApi(PatchSSHTestCase):
|
|
@@ -456,11 +433,11 @@ class TestNestedOperationsApi(PatchSSHTestCase):
|
|
|
456
433
|
|
|
457
434
|
try:
|
|
458
435
|
outer_result = server.shell(commands="echo outer")
|
|
459
|
-
assert outer_result.
|
|
436
|
+
assert outer_result._combined_output is None
|
|
460
437
|
|
|
461
438
|
def callback():
|
|
462
439
|
inner_result = server.shell(commands="echo inner")
|
|
463
|
-
assert inner_result.
|
|
440
|
+
assert inner_result._combined_output is not None
|
|
464
441
|
|
|
465
442
|
python.call(function=callback)
|
|
466
443
|
|
|
@@ -469,8 +446,8 @@ class TestNestedOperationsApi(PatchSSHTestCase):
|
|
|
469
446
|
run_ops(state)
|
|
470
447
|
|
|
471
448
|
assert len(state.get_op_order()) == 3
|
|
472
|
-
assert state.results[somehost]
|
|
473
|
-
assert outer_result.
|
|
449
|
+
assert state.results[somehost].success_ops == 3
|
|
450
|
+
assert outer_result._combined_output is not None
|
|
474
451
|
|
|
475
452
|
disconnect_all(state)
|
|
476
453
|
finally:
|
|
@@ -485,7 +462,7 @@ class TestOperationFailures(PatchSSHTestCase):
|
|
|
485
462
|
|
|
486
463
|
add_op(state, server.shell, 'echo "hi"')
|
|
487
464
|
|
|
488
|
-
with patch("pyinfra.connectors.ssh.run_shell_command") as fake_run_command:
|
|
465
|
+
with patch("pyinfra.connectors.ssh.SSHConnector.run_shell_command") as fake_run_command:
|
|
489
466
|
fake_channel = FakeChannel(1)
|
|
490
467
|
fake_run_command.return_value = (
|
|
491
468
|
False,
|
|
@@ -500,18 +477,18 @@ class TestOperationFailures(PatchSSHTestCase):
|
|
|
500
477
|
somehost = inventory.get_host("somehost")
|
|
501
478
|
|
|
502
479
|
# Ensure the op was not flagged as success
|
|
503
|
-
assert state.results[somehost]
|
|
480
|
+
assert state.results[somehost].success_ops == 0
|
|
504
481
|
# And was flagged asn an error
|
|
505
|
-
assert state.results[somehost]
|
|
482
|
+
assert state.results[somehost].error_ops == 1
|
|
506
483
|
|
|
507
484
|
def test_ignore_errors_op_fail(self):
|
|
508
485
|
inventory = make_inventory()
|
|
509
486
|
state = State(inventory, Config())
|
|
510
487
|
connect_all(state)
|
|
511
488
|
|
|
512
|
-
add_op(state, server.shell, 'echo "hi"',
|
|
489
|
+
add_op(state, server.shell, 'echo "hi"', _ignore_errors=True)
|
|
513
490
|
|
|
514
|
-
with patch("pyinfra.connectors.ssh.run_shell_command") as fake_run_command:
|
|
491
|
+
with patch("pyinfra.connectors.ssh.SSHConnector.run_shell_command") as fake_run_command:
|
|
515
492
|
fake_channel = FakeChannel(1)
|
|
516
493
|
fake_run_command.return_value = (
|
|
517
494
|
False,
|
|
@@ -524,25 +501,10 @@ class TestOperationFailures(PatchSSHTestCase):
|
|
|
524
501
|
somehost = inventory.get_host("somehost")
|
|
525
502
|
|
|
526
503
|
# Ensure the op was added to results
|
|
527
|
-
assert state.results[somehost]
|
|
528
|
-
assert state.results[somehost]
|
|
504
|
+
assert state.results[somehost].ops == 1
|
|
505
|
+
assert state.results[somehost].ignored_error_ops == 1
|
|
529
506
|
# But not as a success
|
|
530
|
-
assert state.results[somehost]
|
|
531
|
-
|
|
532
|
-
# def test_no_invalid_op_call(self):
|
|
533
|
-
# inventory = make_inventory()
|
|
534
|
-
# state = State(inventory, Config())
|
|
535
|
-
# connect_all(state)
|
|
536
|
-
# ctx_state.set(state)
|
|
537
|
-
|
|
538
|
-
# state.in_op = True
|
|
539
|
-
# with self.assertRaises(PyinfraError):
|
|
540
|
-
# server.user('user')
|
|
541
|
-
|
|
542
|
-
# state.in_op = False
|
|
543
|
-
# state.in_deploy = True
|
|
544
|
-
# with self.assertRaises(PyinfraError):
|
|
545
|
-
# server.user('user')
|
|
507
|
+
assert state.results[somehost].success_ops == 0
|
|
546
508
|
|
|
547
509
|
|
|
548
510
|
class TestOperationOrdering(PatchSSHTestCase):
|
|
@@ -567,18 +529,19 @@ class TestOperationOrdering(PatchSSHTestCase):
|
|
|
567
529
|
# Add op to just the second host - using the context modules such that
|
|
568
530
|
# it replicates a deploy file.
|
|
569
531
|
ctx_host.set(inventory.get_host("anotherhost"))
|
|
570
|
-
first_context_hash = server.user("anotherhost_user").
|
|
532
|
+
first_context_hash = server.user("anotherhost_user")._hash
|
|
571
533
|
|
|
572
534
|
# Add op to just the first host - using the context modules such that
|
|
573
535
|
# it replicates a deploy file.
|
|
574
536
|
ctx_host.set(inventory.get_host("somehost"))
|
|
575
|
-
second_context_hash = server.user("somehost_user").
|
|
537
|
+
second_context_hash = server.user("somehost_user")._hash
|
|
576
538
|
|
|
577
539
|
ctx_state.reset()
|
|
578
540
|
ctx_host.reset()
|
|
579
541
|
|
|
580
542
|
pyinfra.is_cli = False
|
|
581
543
|
|
|
544
|
+
print(state.ops)
|
|
582
545
|
# Ensure there are two ops
|
|
583
546
|
op_order = state.get_op_order()
|
|
584
547
|
assert len(op_order) == 3
|
|
@@ -602,9 +565,9 @@ class TestOperationOrdering(PatchSSHTestCase):
|
|
|
602
565
|
another_host = inventory.get_host("anotherhost")
|
|
603
566
|
|
|
604
567
|
def add_another_op():
|
|
605
|
-
return add_op(state, server.shell, "echo second-op")[another_host].
|
|
568
|
+
return add_op(state, server.shell, "echo second-op")[another_host]._hash
|
|
606
569
|
|
|
607
|
-
first_op_hash = add_op(state, server.shell, "echo first-op")[another_host].
|
|
570
|
+
first_op_hash = add_op(state, server.shell, "echo first-op")[another_host]._hash
|
|
608
571
|
second_op_hash = add_another_op() # note `add_op` will be called on an earlier line
|
|
609
572
|
|
|
610
573
|
op_order = state.get_op_order()
|
|
@@ -615,66 +578,3 @@ class TestOperationOrdering(PatchSSHTestCase):
|
|
|
615
578
|
|
|
616
579
|
|
|
617
580
|
this_filename = path.join("tests", "test_api", "test_api_operations.py")
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
# class TestOperationExceptions(TestCase):
|
|
621
|
-
# def test_add_op_rejects_cli(self):
|
|
622
|
-
# pyinfra.is_cli = True
|
|
623
|
-
|
|
624
|
-
# with self.assertRaises(PyinfraError) as context:
|
|
625
|
-
# add_op(None, server.shell)
|
|
626
|
-
# call_line = getframeinfo(currentframe()).lineno - 1
|
|
627
|
-
|
|
628
|
-
# pyinfra.is_cli = False
|
|
629
|
-
|
|
630
|
-
# assert context.exception.args[0] == (
|
|
631
|
-
# '`add_op` should not be called when pyinfra is executing in CLI mode! '
|
|
632
|
-
# '(line {0} in {1})'.format(call_line, this_filename)
|
|
633
|
-
# )
|
|
634
|
-
|
|
635
|
-
# def test_op_call_rejects_no_cli(self):
|
|
636
|
-
# with self.assertRaises(PyinfraError) as context:
|
|
637
|
-
# server.shell()
|
|
638
|
-
# call_line = getframeinfo(currentframe()).lineno - 1
|
|
639
|
-
|
|
640
|
-
# assert context.exception.args[0] == (
|
|
641
|
-
# 'API operation called without state/host: '
|
|
642
|
-
# 'server.shell (line {0} in {1})'.format(call_line, this_filename)
|
|
643
|
-
# )
|
|
644
|
-
|
|
645
|
-
# def test_op_call_rejects_in_op(self):
|
|
646
|
-
# state = FakeState()
|
|
647
|
-
|
|
648
|
-
# pyinfra.is_cli = True
|
|
649
|
-
# ctx_state.set(state)
|
|
650
|
-
|
|
651
|
-
# with self.assertRaises(PyinfraError) as context:
|
|
652
|
-
# server.shell()
|
|
653
|
-
# call_line = getframeinfo(currentframe()).lineno - 1
|
|
654
|
-
|
|
655
|
-
# pyinfra.is_cli = False
|
|
656
|
-
# ctx_state.reset()
|
|
657
|
-
|
|
658
|
-
# assert context.exception.args[0] == (
|
|
659
|
-
# 'Nested operation called without state/host: '
|
|
660
|
-
# 'server.shell (line {0} in {1})'.format(call_line, this_filename)
|
|
661
|
-
# )
|
|
662
|
-
|
|
663
|
-
# def test_op_call_rejects_in_deploy(self):
|
|
664
|
-
# state = FakeState()
|
|
665
|
-
# state.in_op = False
|
|
666
|
-
|
|
667
|
-
# pyinfra.is_cli = True
|
|
668
|
-
# ctx_state.set(state)
|
|
669
|
-
|
|
670
|
-
# with self.assertRaises(PyinfraError) as context:
|
|
671
|
-
# server.shell()
|
|
672
|
-
# call_line = getframeinfo(currentframe()).lineno - 1
|
|
673
|
-
|
|
674
|
-
# pyinfra.is_cli = False
|
|
675
|
-
# ctx_state.reset()
|
|
676
|
-
|
|
677
|
-
# assert context.exception.args[0] == (
|
|
678
|
-
# 'Nested deploy operation called without state/host: '
|
|
679
|
-
# 'server.shell (line {0} in {1})'.format(call_line, this_filename)
|
|
680
|
-
# )
|