pyinfra 2.9.1__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.1.dist-info → pyinfra-3.0.dist-info}/METADATA +40 -41
- pyinfra-3.0.dist-info/RECORD +167 -0
- {pyinfra-2.9.1.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.1.dist-info/RECORD +0 -170
- pyinfra-2.9.1.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.1.dist-info → pyinfra-3.0.dist-info}/LICENSE.md +0 -0
- {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/top_level.txt +0 -0
pyinfra/connectors/dockerssh.py
CHANGED
|
@@ -1,313 +1,291 @@
|
|
|
1
|
-
"""
|
|
2
|
-
**Note**: this connector is in beta!
|
|
3
|
-
|
|
4
|
-
The ``@dockerssh`` connector allows you to run commands on Docker containers on a remote machine.
|
|
5
|
-
|
|
6
|
-
.. code:: shell
|
|
7
|
-
|
|
8
|
-
# A Docker base image must be provided
|
|
9
|
-
pyinfra @dockerssh/remotehost:alpine:3.8 ...
|
|
10
|
-
|
|
11
|
-
# pyinfra can run on multiple Docker images in parallel
|
|
12
|
-
pyinfra @dockerssh/remotehost:alpine:3.8,@dockerssh/remotehost:ubuntu:bionic ...
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
1
|
import os
|
|
16
2
|
from tempfile import mkstemp
|
|
17
3
|
from typing import TYPE_CHECKING
|
|
18
4
|
|
|
19
5
|
import click
|
|
6
|
+
from typing_extensions import Unpack
|
|
20
7
|
|
|
21
8
|
from pyinfra import logger
|
|
22
9
|
from pyinfra.api import QuoteString, StringCommand
|
|
23
|
-
from pyinfra.api.connectors import BaseConnectorMeta
|
|
24
10
|
from pyinfra.api.exceptions import ConnectError, InventoryError, PyinfraError
|
|
25
11
|
from pyinfra.api.util import get_file_io, memoize
|
|
26
12
|
from pyinfra.progress import progress_spinner
|
|
27
13
|
|
|
28
|
-
from . import
|
|
29
|
-
from .
|
|
14
|
+
from .base import BaseConnector
|
|
15
|
+
from .ssh import SSHConnector
|
|
16
|
+
from .util import extract_control_arguments, make_unix_command_for_host
|
|
30
17
|
|
|
31
18
|
if TYPE_CHECKING:
|
|
19
|
+
from pyinfra.api.arguments import ConnectorArguments
|
|
32
20
|
from pyinfra.api.host import Host
|
|
33
21
|
from pyinfra.api.state import State
|
|
34
22
|
|
|
35
23
|
|
|
36
|
-
class Meta(BaseConnectorMeta):
|
|
37
|
-
handles_execution = True
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def remote_remove(
|
|
41
|
-
state: "State", host: "Host", filename, print_output: bool = False, print_input: bool = False
|
|
42
|
-
):
|
|
43
|
-
"""
|
|
44
|
-
Deletes a file on a remote machine over ssh.
|
|
45
|
-
"""
|
|
46
|
-
remove_status, _, remove_stderr = ssh.run_shell_command(
|
|
47
|
-
state,
|
|
48
|
-
host,
|
|
49
|
-
"rm -f {0}".format(filename),
|
|
50
|
-
print_output=print_output,
|
|
51
|
-
print_input=print_input,
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
if not remove_status:
|
|
55
|
-
raise IOError("\n".join(remove_stderr))
|
|
56
|
-
|
|
57
|
-
|
|
58
24
|
@memoize
|
|
59
25
|
def show_warning():
|
|
60
26
|
logger.warning("The @dockerssh connector is in beta!")
|
|
61
27
|
|
|
62
28
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
except (AttributeError, ValueError): # failure to parse the host_image_str
|
|
67
|
-
raise InventoryError("No ssh host or docker base image provided!")
|
|
29
|
+
class DockerSSHConnector(BaseConnector):
|
|
30
|
+
"""
|
|
31
|
+
**Note**: this connector is in beta!
|
|
68
32
|
|
|
69
|
-
|
|
70
|
-
|
|
33
|
+
The ``@dockerssh`` connector allows you to run commands on Docker containers \
|
|
34
|
+
on a remote machine.
|
|
71
35
|
|
|
72
|
-
|
|
36
|
+
.. code:: shell
|
|
73
37
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{"ssh_hostname": hostname, "docker_image": image},
|
|
77
|
-
["@dockerssh"],
|
|
78
|
-
)
|
|
38
|
+
# A Docker base image must be provided
|
|
39
|
+
pyinfra @dockerssh/remotehost:alpine:3.8 ...
|
|
79
40
|
|
|
41
|
+
# pyinfra can run on multiple Docker images in parallel
|
|
42
|
+
pyinfra @dockerssh/remotehost:alpine:3.8,@dockerssh/remotehost:ubuntu:bionic ...
|
|
43
|
+
"""
|
|
80
44
|
|
|
81
|
-
|
|
82
|
-
if not host.connection:
|
|
83
|
-
host.connection = ssh.connect(state, host)
|
|
45
|
+
handles_execution = True
|
|
84
46
|
|
|
85
|
-
|
|
86
|
-
return host.connection
|
|
47
|
+
ssh: SSHConnector
|
|
87
48
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
status, stdout, stderr = ssh.run_shell_command(
|
|
92
|
-
state,
|
|
93
|
-
host,
|
|
94
|
-
"docker run -d {0} tail -f /dev/null".format(host.data.docker_image),
|
|
95
|
-
)
|
|
96
|
-
if not status:
|
|
97
|
-
raise IOError("\n".join(stderr))
|
|
98
|
-
container_id = stdout[-1]
|
|
49
|
+
def __init__(self, state: "State", host: "Host"):
|
|
50
|
+
super().__init__(state, host)
|
|
51
|
+
self.ssh = SSHConnector(state, host)
|
|
99
52
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
53
|
+
@staticmethod
|
|
54
|
+
def make_names_data(name):
|
|
55
|
+
try:
|
|
56
|
+
hostname, image = name.split(":", 1)
|
|
57
|
+
except (AttributeError, ValueError): # failure to parse the name
|
|
58
|
+
raise InventoryError("No ssh host or docker base image provided!")
|
|
103
59
|
|
|
104
|
-
|
|
105
|
-
|
|
60
|
+
if not image:
|
|
61
|
+
raise InventoryError("No docker base image provided!")
|
|
106
62
|
|
|
63
|
+
show_warning()
|
|
107
64
|
|
|
108
|
-
|
|
109
|
-
|
|
65
|
+
yield (
|
|
66
|
+
"@dockerssh/{0}:{1}".format(hostname, image),
|
|
67
|
+
{"ssh_hostname": hostname, "docker_image": image},
|
|
68
|
+
["@dockerssh"],
|
|
69
|
+
)
|
|
110
70
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
71
|
+
def connect(self) -> None:
|
|
72
|
+
self.ssh.connect()
|
|
73
|
+
|
|
74
|
+
if "docker_container_id" in self.host.host_data: # user can provide a docker_container_id
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
with progress_spinner({"docker run"}):
|
|
79
|
+
# last line is the container ID
|
|
80
|
+
status, output = self.ssh.run_shell_command(
|
|
81
|
+
StringCommand(
|
|
82
|
+
"docker",
|
|
83
|
+
"run",
|
|
84
|
+
"-d",
|
|
85
|
+
self.host.data.docker_image,
|
|
86
|
+
"tail",
|
|
87
|
+
"-f",
|
|
88
|
+
"/dev/null",
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
if not status:
|
|
92
|
+
raise IOError(output.stderr)
|
|
93
|
+
container_id = output.stdout_lines[-1]
|
|
94
|
+
|
|
95
|
+
except PyinfraError as e:
|
|
96
|
+
raise ConnectError(e.args[0])
|
|
97
|
+
|
|
98
|
+
self.host.host_data["docker_container_id"] = container_id
|
|
99
|
+
|
|
100
|
+
def disconnect(self) -> None:
|
|
101
|
+
container_id = self.host.host_data["docker_container_id"][:12]
|
|
102
|
+
|
|
103
|
+
with progress_spinner({"docker commit"}):
|
|
104
|
+
_, output = self.ssh.run_shell_command(StringCommand("docker", "commit", container_id))
|
|
105
|
+
|
|
106
|
+
# Last line is the image ID, get sha256:[XXXXXXXXXX]...
|
|
107
|
+
image_id = output.stdout_lines[-1][7:19]
|
|
108
|
+
|
|
109
|
+
with progress_spinner({"docker rm"}):
|
|
110
|
+
self.ssh.run_shell_command(
|
|
111
|
+
StringCommand("docker", "rm", "-f", container_id),
|
|
112
|
+
)
|
|
117
113
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
114
|
+
logger.info(
|
|
115
|
+
"{0}docker build complete, image ID: {1}".format(
|
|
116
|
+
self.host.print_prefix,
|
|
117
|
+
click.style(image_id, bold=True),
|
|
118
|
+
),
|
|
123
119
|
)
|
|
124
120
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
host.print_prefix,
|
|
128
|
-
click.style(image_id, bold=True),
|
|
129
|
-
),
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def run_shell_command(
|
|
134
|
-
state: "State",
|
|
135
|
-
host: "Host",
|
|
136
|
-
command,
|
|
137
|
-
get_pty: bool = False,
|
|
138
|
-
timeout=None,
|
|
139
|
-
stdin=None,
|
|
140
|
-
success_exit_codes=None,
|
|
141
|
-
print_output: bool = False,
|
|
142
|
-
print_input: bool = False,
|
|
143
|
-
return_combined_output=False,
|
|
144
|
-
**command_kwargs,
|
|
145
|
-
):
|
|
146
|
-
container_id = host.host_data["docker_container_id"]
|
|
147
|
-
|
|
148
|
-
# Don't sudo/su in Docker - is this the right thing to do? Makes deploys that
|
|
149
|
-
# target SSH systems work w/Docker out of the box (ie most docker commands
|
|
150
|
-
# are run as root).
|
|
151
|
-
for key in ("sudo", "su_user"):
|
|
152
|
-
command_kwargs.pop(key, None)
|
|
153
|
-
|
|
154
|
-
command = make_unix_command_for_host(state, host, command, **command_kwargs)
|
|
155
|
-
command = QuoteString(command)
|
|
156
|
-
|
|
157
|
-
docker_flags = "-it" if get_pty else "-i"
|
|
158
|
-
docker_command = StringCommand(
|
|
159
|
-
"docker",
|
|
160
|
-
"exec",
|
|
161
|
-
docker_flags,
|
|
162
|
-
container_id,
|
|
163
|
-
"sh",
|
|
164
|
-
"-c",
|
|
121
|
+
def run_shell_command(
|
|
122
|
+
self,
|
|
165
123
|
command,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
remote_temp_filename=None,
|
|
187
|
-
print_output: bool = False,
|
|
188
|
-
print_input: bool = False,
|
|
189
|
-
**kwargs, # ignored (sudo/etc)
|
|
190
|
-
):
|
|
191
|
-
"""
|
|
192
|
-
Upload a file/IO object to the target Docker container by copying it to a
|
|
193
|
-
temporary location and then uploading it into the container using ``docker cp``.
|
|
194
|
-
"""
|
|
195
|
-
|
|
196
|
-
fd, local_temp_filename = mkstemp()
|
|
197
|
-
remote_temp_filename = remote_temp_filename or state.get_temp_filename(local_temp_filename)
|
|
198
|
-
|
|
199
|
-
# Load our file or IO object and write it to the temporary file
|
|
200
|
-
with get_file_io(filename_or_io) as file_io:
|
|
201
|
-
with open(local_temp_filename, "wb") as temp_f:
|
|
202
|
-
data = file_io.read()
|
|
203
|
-
|
|
204
|
-
if isinstance(data, str):
|
|
205
|
-
data = data.encode()
|
|
206
|
-
|
|
207
|
-
temp_f.write(data)
|
|
208
|
-
|
|
209
|
-
# upload file to remote server
|
|
210
|
-
ssh_status = ssh.put_file(state, host, local_temp_filename, remote_temp_filename)
|
|
211
|
-
if not ssh_status:
|
|
212
|
-
raise IOError("Failed to copy file over ssh")
|
|
213
|
-
|
|
214
|
-
try:
|
|
215
|
-
docker_id = host.host_data["docker_container_id"]
|
|
216
|
-
docker_command = "docker cp {0} {1}:{2}".format(
|
|
217
|
-
remote_temp_filename,
|
|
218
|
-
docker_id,
|
|
219
|
-
remote_filename,
|
|
124
|
+
print_output: bool = False,
|
|
125
|
+
print_input: bool = False,
|
|
126
|
+
**arguments: Unpack["ConnectorArguments"],
|
|
127
|
+
):
|
|
128
|
+
local_arguments = extract_control_arguments(arguments)
|
|
129
|
+
|
|
130
|
+
container_id = self.host.host_data["docker_container_id"]
|
|
131
|
+
|
|
132
|
+
command = make_unix_command_for_host(self.state, self.host, command, **arguments)
|
|
133
|
+
command = QuoteString(command)
|
|
134
|
+
|
|
135
|
+
docker_flags = "-it" if local_arguments.get("_get_pty") else "-i"
|
|
136
|
+
docker_command = StringCommand(
|
|
137
|
+
"docker",
|
|
138
|
+
"exec",
|
|
139
|
+
docker_flags,
|
|
140
|
+
container_id,
|
|
141
|
+
"sh",
|
|
142
|
+
"-c",
|
|
143
|
+
command,
|
|
220
144
|
)
|
|
221
145
|
|
|
222
|
-
|
|
223
|
-
state,
|
|
224
|
-
host,
|
|
146
|
+
return self.ssh.run_shell_command(
|
|
225
147
|
docker_command,
|
|
226
148
|
print_output=print_output,
|
|
227
149
|
print_input=print_input,
|
|
150
|
+
**local_arguments,
|
|
228
151
|
)
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
152
|
+
|
|
153
|
+
def put_file(
|
|
154
|
+
self,
|
|
155
|
+
filename_or_io,
|
|
156
|
+
remote_filename,
|
|
157
|
+
remote_temp_filename=None,
|
|
158
|
+
print_output: bool = False,
|
|
159
|
+
print_input: bool = False,
|
|
160
|
+
**kwargs, # ignored (sudo/etc)
|
|
161
|
+
):
|
|
162
|
+
"""
|
|
163
|
+
Upload a file/IO object to the target Docker container by copying it to a
|
|
164
|
+
temporary location and then uploading it into the container using ``docker cp``.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
fd, local_temp_filename = mkstemp()
|
|
168
|
+
remote_temp_filename = remote_temp_filename or self.host.get_temp_filename(
|
|
169
|
+
local_temp_filename
|
|
238
170
|
)
|
|
239
171
|
|
|
240
|
-
|
|
241
|
-
|
|
172
|
+
# Load our file or IO object and write it to the temporary file
|
|
173
|
+
with get_file_io(filename_or_io) as file_io:
|
|
174
|
+
with open(local_temp_filename, "wb") as temp_f:
|
|
175
|
+
data = file_io.read()
|
|
242
176
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
"{0}file uploaded to container: {1}".format(
|
|
246
|
-
host.print_prefix,
|
|
247
|
-
remote_filename,
|
|
248
|
-
),
|
|
249
|
-
err=True,
|
|
250
|
-
)
|
|
177
|
+
if isinstance(data, str):
|
|
178
|
+
data = data.encode()
|
|
251
179
|
|
|
252
|
-
|
|
180
|
+
temp_f.write(data)
|
|
253
181
|
|
|
182
|
+
# upload file to remote server
|
|
183
|
+
ssh_status = self.ssh.put_file(local_temp_filename, remote_temp_filename)
|
|
184
|
+
if not ssh_status:
|
|
185
|
+
raise IOError("Failed to copy file over ssh")
|
|
254
186
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
**kwargs, # ignored (sudo/etc)
|
|
264
|
-
):
|
|
265
|
-
"""
|
|
266
|
-
Download a file from the target Docker container by copying it to a temporary
|
|
267
|
-
location and then reading that into our final file/IO object.
|
|
268
|
-
"""
|
|
187
|
+
try:
|
|
188
|
+
docker_id = self.host.host_data["docker_container_id"]
|
|
189
|
+
docker_command = StringCommand(
|
|
190
|
+
"docker",
|
|
191
|
+
"cp",
|
|
192
|
+
remote_temp_filename,
|
|
193
|
+
f"{docker_id}:{remote_filename}",
|
|
194
|
+
)
|
|
269
195
|
|
|
270
|
-
|
|
196
|
+
status, output = self.ssh.run_shell_command(
|
|
197
|
+
docker_command,
|
|
198
|
+
print_output=print_output,
|
|
199
|
+
print_input=print_input,
|
|
200
|
+
)
|
|
201
|
+
finally:
|
|
202
|
+
os.close(fd)
|
|
203
|
+
os.remove(local_temp_filename)
|
|
204
|
+
self.remote_remove(
|
|
205
|
+
local_temp_filename,
|
|
206
|
+
print_output=print_output,
|
|
207
|
+
print_input=print_input,
|
|
208
|
+
)
|
|
271
209
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
docker_command = "docker cp {0}:{1} {2}".format(
|
|
275
|
-
docker_id,
|
|
276
|
-
remote_filename,
|
|
277
|
-
remote_temp_filename,
|
|
278
|
-
)
|
|
210
|
+
if not status:
|
|
211
|
+
raise IOError(output.stderr)
|
|
279
212
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
213
|
+
if print_output:
|
|
214
|
+
click.echo(
|
|
215
|
+
"{0}file uploaded to container: {1}".format(
|
|
216
|
+
self.host.print_prefix,
|
|
217
|
+
remote_filename,
|
|
218
|
+
),
|
|
219
|
+
err=True,
|
|
220
|
+
)
|
|
287
221
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
222
|
+
return status
|
|
223
|
+
|
|
224
|
+
def get_file(
|
|
225
|
+
self,
|
|
226
|
+
remote_filename,
|
|
227
|
+
filename_or_io,
|
|
228
|
+
remote_temp_filename=None,
|
|
229
|
+
print_output: bool = False,
|
|
230
|
+
print_input: bool = False,
|
|
231
|
+
**kwargs, # ignored (sudo/etc)
|
|
232
|
+
):
|
|
233
|
+
"""
|
|
234
|
+
Download a file from the target Docker container by copying it to a temporary
|
|
235
|
+
location and then reading that into our final file/IO object.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
remote_temp_filename = remote_temp_filename or self.host.get_temp_filename(remote_filename)
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
docker_id = self.host.host_data["docker_container_id"]
|
|
242
|
+
docker_command = StringCommand(
|
|
243
|
+
"docker",
|
|
244
|
+
"cp",
|
|
245
|
+
f"{docker_id}:{remote_filename}",
|
|
246
|
+
remote_temp_filename,
|
|
247
|
+
)
|
|
297
248
|
|
|
298
|
-
|
|
299
|
-
|
|
249
|
+
status, output = self.ssh.run_shell_command(
|
|
250
|
+
docker_command,
|
|
251
|
+
print_output=print_output,
|
|
252
|
+
print_input=print_input,
|
|
253
|
+
)
|
|
300
254
|
|
|
301
|
-
|
|
302
|
-
|
|
255
|
+
ssh_status = self.ssh.get_file(remote_temp_filename, filename_or_io)
|
|
256
|
+
finally:
|
|
257
|
+
self.remote_remove(
|
|
258
|
+
remote_temp_filename,
|
|
259
|
+
print_output=print_output,
|
|
260
|
+
print_input=print_input,
|
|
261
|
+
)
|
|
303
262
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
263
|
+
if not ssh_status:
|
|
264
|
+
raise IOError("failed to copy file over ssh")
|
|
265
|
+
|
|
266
|
+
if not status:
|
|
267
|
+
raise IOError(output.stderr)
|
|
268
|
+
|
|
269
|
+
if print_output:
|
|
270
|
+
click.echo(
|
|
271
|
+
"{0}file downloaded from container: {1}".format(
|
|
272
|
+
self.host.print_prefix,
|
|
273
|
+
remote_filename,
|
|
274
|
+
),
|
|
275
|
+
err=True,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return status
|
|
279
|
+
|
|
280
|
+
def remote_remove(self, filename, print_output: bool = False, print_input: bool = False):
|
|
281
|
+
"""
|
|
282
|
+
Deletes a file on a remote machine over ssh.
|
|
283
|
+
"""
|
|
284
|
+
remove_status, output = self.ssh.run_shell_command(
|
|
285
|
+
StringCommand("rm", "-f", filename),
|
|
286
|
+
print_output=print_output,
|
|
287
|
+
print_input=print_input,
|
|
311
288
|
)
|
|
312
289
|
|
|
313
|
-
|
|
290
|
+
if not remove_status:
|
|
291
|
+
raise IOError(output.stderr)
|