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
pyinfra/connectors/local.py
CHANGED
|
@@ -1,244 +1,232 @@
|
|
|
1
|
-
"""
|
|
2
|
-
The ``@local`` connector executes changes on the local machine using subprocesses.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
1
|
import os
|
|
6
|
-
from
|
|
2
|
+
from shutil import which
|
|
7
3
|
from tempfile import mkstemp
|
|
8
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Tuple
|
|
9
5
|
|
|
10
6
|
import click
|
|
7
|
+
from typing_extensions import Unpack
|
|
11
8
|
|
|
12
9
|
from pyinfra import logger
|
|
13
10
|
from pyinfra.api.command import QuoteString, StringCommand
|
|
14
|
-
from pyinfra.api.connectors import BaseConnectorMeta
|
|
15
11
|
from pyinfra.api.exceptions import InventoryError
|
|
16
12
|
from pyinfra.api.util import get_file_io
|
|
17
13
|
|
|
14
|
+
from .base import BaseConnector
|
|
18
15
|
from .util import (
|
|
16
|
+
CommandOutput,
|
|
19
17
|
execute_command_with_sudo_retry,
|
|
20
18
|
make_unix_command_for_host,
|
|
21
19
|
run_local_process,
|
|
22
|
-
split_combined_output,
|
|
23
20
|
)
|
|
24
21
|
|
|
25
22
|
if TYPE_CHECKING:
|
|
26
|
-
from pyinfra.api.
|
|
27
|
-
from pyinfra.api.state import State
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class Meta(BaseConnectorMeta):
|
|
31
|
-
handles_execution = True
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def make_names_data(_=None):
|
|
35
|
-
if _ is not None:
|
|
36
|
-
raise InventoryError("Cannot have more than one @local")
|
|
37
|
-
|
|
38
|
-
yield "@local", {}, ["@local"]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def connect(state: "State", host: "Host"):
|
|
42
|
-
return True
|
|
23
|
+
from pyinfra.api.arguments import ConnectorArguments
|
|
43
24
|
|
|
44
25
|
|
|
45
|
-
|
|
46
|
-
state: "State",
|
|
47
|
-
host: "Host",
|
|
48
|
-
command,
|
|
49
|
-
get_pty: bool = False, # ignored
|
|
50
|
-
timeout=None,
|
|
51
|
-
stdin=None,
|
|
52
|
-
success_exit_codes=None,
|
|
53
|
-
print_output: bool = False,
|
|
54
|
-
print_input: bool = False,
|
|
55
|
-
return_combined_output: bool = False,
|
|
56
|
-
**command_kwargs,
|
|
57
|
-
):
|
|
26
|
+
class LocalConnector(BaseConnector):
|
|
58
27
|
"""
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Args:
|
|
62
|
-
state (``pyinfra.api.State`` object): state object for this command
|
|
63
|
-
host (``pyinfra.api.Host`` object): the target host
|
|
64
|
-
command (string): actual command to execute
|
|
65
|
-
sudo (boolean): whether to wrap the command with sudo
|
|
66
|
-
sudo_user (string): user to sudo to
|
|
67
|
-
env (dict): environment variables to set
|
|
68
|
-
timeout (int): timeout for this command to complete before erroring
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
tuple: (exit_code, stdout, stderr)
|
|
72
|
-
stdout and stderr are both lists of strings from each buffer.
|
|
73
|
-
"""
|
|
74
|
-
|
|
75
|
-
def execute_command():
|
|
76
|
-
unix_command = make_unix_command_for_host(state, host, command, **command_kwargs)
|
|
77
|
-
actual_command = unix_command.get_raw_value()
|
|
78
|
-
|
|
79
|
-
logger.debug("--> Running command on localhost: %s", unix_command)
|
|
80
|
-
|
|
81
|
-
if print_input:
|
|
82
|
-
click.echo("{0}>>> {1}".format(host.print_prefix, unix_command), err=True)
|
|
83
|
-
|
|
84
|
-
return run_local_process(
|
|
85
|
-
actual_command,
|
|
86
|
-
stdin=stdin,
|
|
87
|
-
timeout=timeout,
|
|
88
|
-
print_output=print_output,
|
|
89
|
-
print_prefix=host.print_prefix,
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
return_code, combined_output = execute_command_with_sudo_retry(
|
|
93
|
-
host,
|
|
94
|
-
command_kwargs,
|
|
95
|
-
execute_command,
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
if success_exit_codes:
|
|
99
|
-
status = return_code in success_exit_codes
|
|
100
|
-
else:
|
|
101
|
-
status = return_code == 0
|
|
28
|
+
The ``@local`` connector executes changes on the local machine using
|
|
29
|
+
subprocesses. **This connector is only compatible with MacOS & Linux hosts**.
|
|
102
30
|
|
|
103
|
-
|
|
104
|
-
return status, combined_output
|
|
105
|
-
|
|
106
|
-
stdout, stderr = split_combined_output(combined_output)
|
|
107
|
-
return status, stdout, stderr
|
|
31
|
+
Examples:
|
|
108
32
|
|
|
33
|
+
.. code::
|
|
109
34
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
host: "Host",
|
|
113
|
-
filename_or_io,
|
|
114
|
-
remote_filename,
|
|
115
|
-
remote_temp_filename=None, # ignored
|
|
116
|
-
print_output: bool = False,
|
|
117
|
-
print_input: bool = False,
|
|
118
|
-
**command_kwargs,
|
|
119
|
-
):
|
|
120
|
-
"""
|
|
121
|
-
Upload a local file or IO object by copying it to a temporary directory
|
|
122
|
-
and then writing it to the upload location.
|
|
35
|
+
# Install nginx
|
|
36
|
+
pyinfra inventory.py apt.packages nginx update=true _sudo=true
|
|
123
37
|
"""
|
|
124
38
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
try:
|
|
128
|
-
# Load our file or IO object and write it to the temporary file
|
|
129
|
-
with get_file_io(filename_or_io) as file_io:
|
|
130
|
-
with open(temp_filename, "wb") as temp_f:
|
|
131
|
-
data = file_io.read()
|
|
132
|
-
|
|
133
|
-
if isinstance(data, str):
|
|
134
|
-
data = data.encode()
|
|
135
|
-
|
|
136
|
-
temp_f.write(data)
|
|
137
|
-
|
|
138
|
-
# Copy the file using `cp` such that we support sudo/su
|
|
139
|
-
status, _, stderr = run_shell_command(
|
|
140
|
-
state,
|
|
141
|
-
host,
|
|
142
|
-
StringCommand("cp", temp_filename, QuoteString(remote_filename)),
|
|
143
|
-
print_output=print_output,
|
|
144
|
-
print_input=print_input,
|
|
145
|
-
**command_kwargs,
|
|
146
|
-
)
|
|
39
|
+
handles_execution = True
|
|
147
40
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
41
|
+
@staticmethod
|
|
42
|
+
def make_names_data(name=None):
|
|
43
|
+
if name is not None:
|
|
44
|
+
raise InventoryError("Cannot have more than one @local")
|
|
45
|
+
|
|
46
|
+
yield "@local", {}, ["@local"]
|
|
47
|
+
|
|
48
|
+
def run_shell_command(
|
|
49
|
+
self,
|
|
50
|
+
command: StringCommand,
|
|
51
|
+
print_output: bool = False,
|
|
52
|
+
print_input: bool = False,
|
|
53
|
+
**arguments: Unpack["ConnectorArguments"],
|
|
54
|
+
) -> Tuple[bool, CommandOutput]:
|
|
55
|
+
"""
|
|
56
|
+
Execute a command on the local machine.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
command (StringCommand): actual command to execute
|
|
60
|
+
print_output (bool): whether to print command output
|
|
61
|
+
print_input (bool): whether to print command input
|
|
62
|
+
arguments: (ConnectorArguments): connector global arguments
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
tuple: (bool, CommandOutput)
|
|
66
|
+
Bool indicating success and CommandOutput with stdout/stderr lines.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
arguments.pop("_get_pty", False)
|
|
70
|
+
_timeout = arguments.pop("_timeout", None)
|
|
71
|
+
_stdin = arguments.pop("_stdin", None)
|
|
72
|
+
_success_exit_codes = arguments.pop("_success_exit_codes", None)
|
|
73
|
+
|
|
74
|
+
def execute_command() -> Tuple[int, CommandOutput]:
|
|
75
|
+
unix_command = make_unix_command_for_host(self.state, self.host, command, **arguments)
|
|
76
|
+
actual_command = unix_command.get_raw_value()
|
|
77
|
+
|
|
78
|
+
logger.debug("--> Running command on localhost: %s", unix_command)
|
|
79
|
+
|
|
80
|
+
if print_input:
|
|
81
|
+
click.echo("{0}>>> {1}".format(self.host.print_prefix, unix_command), err=True)
|
|
82
|
+
|
|
83
|
+
return run_local_process(
|
|
84
|
+
actual_command,
|
|
85
|
+
stdin=_stdin,
|
|
86
|
+
timeout=_timeout,
|
|
87
|
+
print_output=print_output,
|
|
88
|
+
print_prefix=self.host.print_prefix,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return_code, combined_output = execute_command_with_sudo_retry(
|
|
92
|
+
self.host,
|
|
93
|
+
arguments,
|
|
94
|
+
execute_command,
|
|
157
95
|
)
|
|
158
96
|
|
|
159
|
-
|
|
97
|
+
if _success_exit_codes:
|
|
98
|
+
status = return_code in _success_exit_codes
|
|
99
|
+
else:
|
|
100
|
+
status = return_code == 0
|
|
160
101
|
|
|
102
|
+
return status, combined_output
|
|
161
103
|
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
104
|
+
def put_file(
|
|
105
|
+
self,
|
|
106
|
+
filename_or_io,
|
|
107
|
+
remote_filename,
|
|
108
|
+
remote_temp_filename=None, # ignored
|
|
109
|
+
print_output: bool = False,
|
|
110
|
+
print_input: bool = False,
|
|
111
|
+
**arguments,
|
|
112
|
+
) -> bool:
|
|
113
|
+
"""
|
|
114
|
+
Upload a local file or IO object by copying it to a temporary directory
|
|
115
|
+
and then writing it to the upload location.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
bool: Indicating success or failure
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
_, temp_filename = mkstemp()
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
# Load our file or IO object and write it to the temporary file
|
|
125
|
+
with get_file_io(filename_or_io) as file_io:
|
|
126
|
+
with open(temp_filename, "wb") as temp_f:
|
|
127
|
+
data = file_io.read()
|
|
128
|
+
|
|
129
|
+
if isinstance(data, str):
|
|
130
|
+
data = data.encode()
|
|
131
|
+
|
|
132
|
+
temp_f.write(data)
|
|
133
|
+
|
|
134
|
+
# Copy the file using `cp` such that we support sudo/su
|
|
135
|
+
status, output = self.run_shell_command(
|
|
136
|
+
StringCommand("cp", temp_filename, QuoteString(remote_filename)),
|
|
137
|
+
print_output=print_output,
|
|
138
|
+
print_input=print_input,
|
|
139
|
+
**arguments,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if not status:
|
|
143
|
+
raise IOError(output.stderr)
|
|
144
|
+
finally:
|
|
145
|
+
os.remove(temp_filename)
|
|
146
|
+
|
|
147
|
+
if print_output:
|
|
148
|
+
click.echo(
|
|
149
|
+
"{0}file copied: {1}".format(self.host.print_prefix, remote_filename),
|
|
150
|
+
err=True,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return status
|
|
154
|
+
|
|
155
|
+
def get_file(
|
|
156
|
+
self,
|
|
157
|
+
remote_filename,
|
|
158
|
+
filename_or_io,
|
|
159
|
+
remote_temp_filename=None, # ignored
|
|
160
|
+
print_output: bool = False,
|
|
161
|
+
print_input: bool = False,
|
|
162
|
+
**arguments,
|
|
163
|
+
) -> bool:
|
|
164
|
+
"""
|
|
165
|
+
Download a local file by copying it to a temporary location and then writing
|
|
166
|
+
it to our filename or IO object.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
bool: Indicating success or failure
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
_, temp_filename = mkstemp()
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
# Copy the file using `cp` such that we support sudo/su
|
|
176
|
+
status, output = self.run_shell_command(
|
|
177
|
+
StringCommand("cp", remote_filename, temp_filename),
|
|
178
|
+
print_output=print_output,
|
|
179
|
+
print_input=print_input,
|
|
180
|
+
**arguments,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if not status:
|
|
184
|
+
raise IOError(output.stderr)
|
|
185
|
+
|
|
186
|
+
# Load our file or IO object and write it to the temporary file
|
|
187
|
+
with open(temp_filename, encoding="utf-8") as temp_f:
|
|
188
|
+
with get_file_io(filename_or_io, "wb") as file_io:
|
|
189
|
+
data_bytes: bytes
|
|
190
|
+
|
|
191
|
+
data = temp_f.read()
|
|
192
|
+
if isinstance(data, str):
|
|
193
|
+
data_bytes = data.encode()
|
|
194
|
+
else:
|
|
195
|
+
data_bytes = data
|
|
196
|
+
|
|
197
|
+
file_io.write(data_bytes)
|
|
198
|
+
finally:
|
|
199
|
+
os.remove(temp_filename)
|
|
200
|
+
|
|
201
|
+
if print_output:
|
|
202
|
+
click.echo(
|
|
203
|
+
"{0}file copied: {1}".format(self.host.print_prefix, remote_filename),
|
|
204
|
+
err=True,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
return True
|
|
208
|
+
|
|
209
|
+
def check_can_rsync(self):
|
|
210
|
+
if not which("rsync"):
|
|
211
|
+
raise NotImplementedError("The `rsync` binary is not available on this system.")
|
|
212
|
+
|
|
213
|
+
def rsync(
|
|
214
|
+
self,
|
|
215
|
+
src,
|
|
216
|
+
dest,
|
|
217
|
+
flags,
|
|
218
|
+
print_output: bool = False,
|
|
219
|
+
print_input: bool = False,
|
|
220
|
+
**arguments,
|
|
221
|
+
) -> bool:
|
|
222
|
+
status, output = self.run_shell_command(
|
|
223
|
+
StringCommand("rsync", " ".join(flags), src, dest),
|
|
185
224
|
print_output=print_output,
|
|
186
225
|
print_input=print_input,
|
|
187
|
-
**
|
|
226
|
+
**arguments,
|
|
188
227
|
)
|
|
189
228
|
|
|
190
229
|
if not status:
|
|
191
|
-
raise IOError(
|
|
192
|
-
|
|
193
|
-
# Load our file or IO object and write it to the temporary file
|
|
194
|
-
with open(temp_filename, encoding="utf-8") as temp_f:
|
|
195
|
-
with get_file_io(filename_or_io, "wb") as file_io:
|
|
196
|
-
data_bytes: bytes
|
|
197
|
-
|
|
198
|
-
data = temp_f.read()
|
|
199
|
-
if isinstance(data, str):
|
|
200
|
-
data_bytes = data.encode()
|
|
201
|
-
else:
|
|
202
|
-
data_bytes = data
|
|
203
|
-
|
|
204
|
-
file_io.write(data_bytes)
|
|
205
|
-
finally:
|
|
206
|
-
os.remove(temp_filename)
|
|
207
|
-
|
|
208
|
-
if print_output:
|
|
209
|
-
click.echo(
|
|
210
|
-
"{0}file copied: {1}".format(host.print_prefix, remote_filename),
|
|
211
|
-
err=True,
|
|
212
|
-
)
|
|
230
|
+
raise IOError(output.stderr)
|
|
213
231
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def check_can_rsync(host):
|
|
218
|
-
if not find_executable("rsync"):
|
|
219
|
-
raise NotImplementedError("The `rsync` binary is not available on this system.")
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def rsync(
|
|
223
|
-
state: "State",
|
|
224
|
-
host: "Host",
|
|
225
|
-
src,
|
|
226
|
-
dest,
|
|
227
|
-
flags,
|
|
228
|
-
print_output: bool = False,
|
|
229
|
-
print_input: bool = False,
|
|
230
|
-
**command_kwargs,
|
|
231
|
-
):
|
|
232
|
-
status, _, stderr = run_shell_command(
|
|
233
|
-
state,
|
|
234
|
-
host,
|
|
235
|
-
"rsync {0} {1} {2}".format(" ".join(flags), src, dest),
|
|
236
|
-
print_output=print_output,
|
|
237
|
-
print_input=print_input,
|
|
238
|
-
**command_kwargs,
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
if not status:
|
|
242
|
-
raise IOError("\n".join(stderr))
|
|
243
|
-
|
|
244
|
-
return True
|
|
232
|
+
return True
|