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/docker.py
CHANGED
|
@@ -1,25 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
The ``@docker`` connector allows you to build Docker images, or modify running
|
|
3
|
-
Docker containers, using ``pyinfra``. You can pass either an image name or
|
|
4
|
-
existing container ID:
|
|
5
|
-
|
|
6
|
-
+ Image - will create a container from the image, execute operations and save
|
|
7
|
-
into a new image
|
|
8
|
-
+ Existing container ID - will simply execute operations against the container,
|
|
9
|
-
leaving it up afterwards
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
.. code:: shell
|
|
13
|
-
|
|
14
|
-
# A Docker base image must be provided
|
|
15
|
-
pyinfra @docker/alpine:3.8 ...
|
|
16
|
-
|
|
17
|
-
# pyinfra can run on multiple Docker images in parallel
|
|
18
|
-
pyinfra @docker/alpine:3.8,@docker/ubuntu:bionic ...
|
|
19
|
-
|
|
20
|
-
# Execute against a running container
|
|
21
|
-
pyinfra @docker/2beb8c15a1b1 ...
|
|
22
|
-
"""
|
|
1
|
+
from __future__ import annotations
|
|
23
2
|
|
|
24
3
|
import json
|
|
25
4
|
import os
|
|
@@ -27,51 +6,42 @@ from tempfile import mkstemp
|
|
|
27
6
|
from typing import TYPE_CHECKING
|
|
28
7
|
|
|
29
8
|
import click
|
|
9
|
+
from typing_extensions import TypedDict, Unpack
|
|
30
10
|
|
|
31
11
|
from pyinfra import local, logger
|
|
32
12
|
from pyinfra.api import QuoteString, StringCommand
|
|
33
|
-
from pyinfra.api.connectors import BaseConnectorMeta
|
|
34
13
|
from pyinfra.api.exceptions import ConnectError, InventoryError, PyinfraError
|
|
35
14
|
from pyinfra.api.util import get_file_io
|
|
36
15
|
from pyinfra.progress import progress_spinner
|
|
37
16
|
|
|
38
|
-
from .
|
|
39
|
-
from .
|
|
17
|
+
from .base import BaseConnector, DataMeta
|
|
18
|
+
from .local import LocalConnector
|
|
19
|
+
from .util import CommandOutput, extract_control_arguments, make_unix_command_for_host
|
|
40
20
|
|
|
41
21
|
if TYPE_CHECKING:
|
|
22
|
+
from pyinfra.api.arguments import ConnectorArguments
|
|
42
23
|
from pyinfra.api.host import Host
|
|
43
24
|
from pyinfra.api.state import State
|
|
44
25
|
|
|
45
26
|
|
|
46
|
-
class
|
|
47
|
-
|
|
48
|
-
keys_prefix: str = "docker"
|
|
49
|
-
|
|
50
|
-
class DataKeys:
|
|
51
|
-
identifier = "ID of container or image to target"
|
|
52
|
-
container_id = "ID of container to target, overrides ``docker_identifier``"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
DATA_KEYS = Meta.keys()
|
|
56
|
-
|
|
27
|
+
class ConnectorData(TypedDict):
|
|
28
|
+
docker_identifier: str
|
|
57
29
|
|
|
58
|
-
def make_names_data(identifier=None):
|
|
59
|
-
if not identifier:
|
|
60
|
-
raise InventoryError("No docker base ID provided!")
|
|
61
30
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
["@docker"],
|
|
66
|
-
)
|
|
31
|
+
connector_data_meta: dict[str, DataMeta] = {
|
|
32
|
+
"docker_identifier": DataMeta("ID of container or image to start from"),
|
|
33
|
+
}
|
|
67
34
|
|
|
68
35
|
|
|
69
|
-
def _find_start_docker_container(container_id):
|
|
36
|
+
def _find_start_docker_container(container_id) -> tuple[str, bool]:
|
|
70
37
|
docker_info = local.shell("docker container inspect {0}".format(container_id))
|
|
38
|
+
assert isinstance(docker_info, str)
|
|
71
39
|
docker_info = json.loads(docker_info)[0]
|
|
72
40
|
if docker_info["State"]["Running"] is False:
|
|
73
41
|
logger.info("Starting stopped container: {0}".format(container_id))
|
|
74
42
|
local.shell("docker container start {0}".format(container_id))
|
|
43
|
+
return container_id, False
|
|
44
|
+
return container_id, True
|
|
75
45
|
|
|
76
46
|
|
|
77
47
|
def _start_docker_image(image_name):
|
|
@@ -86,220 +56,233 @@ def _start_docker_image(image_name):
|
|
|
86
56
|
raise ConnectError(e.args[0])
|
|
87
57
|
|
|
88
58
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
host.connector_data["docker_container_id"] = docker_container_id
|
|
94
|
-
return True
|
|
59
|
+
class DockerConnector(BaseConnector):
|
|
60
|
+
"""
|
|
61
|
+
The docker connector allows you to build Docker images or modify running
|
|
62
|
+
Docker containers. You can pass either an image name or existing container ID:
|
|
95
63
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
_find_start_docker_container(docker_identifier)
|
|
101
|
-
except PyinfraError:
|
|
102
|
-
container_id = _start_docker_image(docker_identifier)
|
|
103
|
-
else:
|
|
104
|
-
container_id = docker_identifier
|
|
105
|
-
host.connector_data["docker_container_no_disconnect"] = True
|
|
64
|
+
+ Image - will create a new container from the image, execute operations \
|
|
65
|
+
against it, save into a new Docker image and remove the container
|
|
66
|
+
+ Existing container ID - will execute operations against the running \
|
|
67
|
+
container, leaving it running
|
|
106
68
|
|
|
107
|
-
|
|
108
|
-
return True
|
|
69
|
+
.. code:: shell
|
|
109
70
|
|
|
71
|
+
# A Docker base image must be provided
|
|
72
|
+
pyinfra @docker/alpine:3.8 ...
|
|
110
73
|
|
|
111
|
-
|
|
112
|
-
|
|
74
|
+
# pyinfra can run on multiple Docker images in parallel
|
|
75
|
+
pyinfra @docker/alpine:3.8,@docker/ubuntu:bionic ...
|
|
113
76
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
host.print_prefix,
|
|
118
|
-
click.style(container_id, bold=True),
|
|
119
|
-
),
|
|
120
|
-
)
|
|
121
|
-
return
|
|
77
|
+
# Execute against a running container
|
|
78
|
+
pyinfra @docker/2beb8c15a1b1 ...
|
|
79
|
+
"""
|
|
122
80
|
|
|
123
|
-
|
|
124
|
-
image_id = local.shell("docker commit {0}".format(container_id), splitlines=True)[-1][
|
|
125
|
-
7:19
|
|
126
|
-
] # last line is the image ID, get sha256:[XXXXXXXXXX]...
|
|
81
|
+
handles_execution = True
|
|
127
82
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
)
|
|
83
|
+
data_cls = ConnectorData
|
|
84
|
+
data_meta = connector_data_meta
|
|
85
|
+
data: ConnectorData
|
|
132
86
|
|
|
133
|
-
|
|
134
|
-
"{0}docker build complete, image ID: {1}".format(
|
|
135
|
-
host.print_prefix,
|
|
136
|
-
click.style(image_id, bold=True),
|
|
137
|
-
),
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def run_shell_command(
|
|
142
|
-
state: "State",
|
|
143
|
-
host: "Host",
|
|
144
|
-
command,
|
|
145
|
-
get_pty=False,
|
|
146
|
-
timeout=None,
|
|
147
|
-
stdin=None,
|
|
148
|
-
success_exit_codes=None,
|
|
149
|
-
print_output=False,
|
|
150
|
-
print_input=False,
|
|
151
|
-
return_combined_output=False,
|
|
152
|
-
**command_kwargs,
|
|
153
|
-
):
|
|
154
|
-
container_id = host.connector_data["docker_container_id"]
|
|
155
|
-
|
|
156
|
-
command = make_unix_command_for_host(state, host, command, **command_kwargs)
|
|
157
|
-
command = QuoteString(command)
|
|
158
|
-
|
|
159
|
-
docker_flags = "-it" if get_pty else "-i"
|
|
160
|
-
docker_command = StringCommand(
|
|
161
|
-
"docker",
|
|
162
|
-
"exec",
|
|
163
|
-
docker_flags,
|
|
164
|
-
container_id,
|
|
165
|
-
"sh",
|
|
166
|
-
"-c",
|
|
167
|
-
command,
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
return run_local_shell_command(
|
|
171
|
-
state,
|
|
172
|
-
host,
|
|
173
|
-
docker_command,
|
|
174
|
-
timeout=timeout,
|
|
175
|
-
stdin=stdin,
|
|
176
|
-
success_exit_codes=success_exit_codes,
|
|
177
|
-
print_output=print_output,
|
|
178
|
-
print_input=print_input,
|
|
179
|
-
return_combined_output=return_combined_output,
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def put_file(
|
|
184
|
-
state: "State",
|
|
185
|
-
host: "Host",
|
|
186
|
-
filename_or_io,
|
|
187
|
-
remote_filename,
|
|
188
|
-
remote_temp_filename=None, # ignored
|
|
189
|
-
print_output=False,
|
|
190
|
-
print_input=False,
|
|
191
|
-
**kwargs, # ignored (sudo/etc)
|
|
192
|
-
):
|
|
193
|
-
"""
|
|
194
|
-
Upload a file/IO object to the target Docker container by copying it to a
|
|
195
|
-
temporary location and then uploading it into the container using ``docker cp``.
|
|
196
|
-
"""
|
|
87
|
+
local: LocalConnector
|
|
197
88
|
|
|
198
|
-
|
|
89
|
+
container_id: str
|
|
90
|
+
no_stop: bool = False
|
|
199
91
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
with open(temp_filename, "wb") as temp_f:
|
|
204
|
-
data = file_io.read()
|
|
92
|
+
def __init__(self, state: "State", host: "Host"):
|
|
93
|
+
super().__init__(state, host)
|
|
94
|
+
self.local = LocalConnector(state, host)
|
|
205
95
|
|
|
206
|
-
|
|
207
|
-
|
|
96
|
+
@staticmethod
|
|
97
|
+
def make_names_data(name=None):
|
|
98
|
+
if not name:
|
|
99
|
+
raise InventoryError("No docker base ID provided!")
|
|
208
100
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
temp_filename,
|
|
214
|
-
docker_id,
|
|
215
|
-
remote_filename,
|
|
101
|
+
yield (
|
|
102
|
+
"@docker/{0}".format(name),
|
|
103
|
+
{"docker_identifier": name},
|
|
104
|
+
["@docker"],
|
|
216
105
|
)
|
|
217
106
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
107
|
+
def connect(self) -> None:
|
|
108
|
+
self.local.connect()
|
|
109
|
+
|
|
110
|
+
docker_identifier = self.data["docker_identifier"]
|
|
111
|
+
with progress_spinner({"prepare docker container"}):
|
|
112
|
+
try:
|
|
113
|
+
self.container_id, was_running = _find_start_docker_container(docker_identifier)
|
|
114
|
+
if was_running:
|
|
115
|
+
self.no_stop = True
|
|
116
|
+
except PyinfraError:
|
|
117
|
+
self.container_id = _start_docker_image(docker_identifier)
|
|
118
|
+
|
|
119
|
+
def disconnect(self):
|
|
120
|
+
container_id = self.container_id
|
|
121
|
+
|
|
122
|
+
if self.no_stop:
|
|
123
|
+
logger.info(
|
|
124
|
+
"{0}docker build complete, container left running: {1}".format(
|
|
125
|
+
self.host.print_prefix,
|
|
126
|
+
click.style(container_id, bold=True),
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
with progress_spinner({"docker commit"}):
|
|
132
|
+
image_id = local.shell("docker commit {0}".format(container_id), splitlines=True)[-1][
|
|
133
|
+
7:19
|
|
134
|
+
] # last line is the image ID, get sha256:[XXXXXXXXXX]...
|
|
135
|
+
|
|
136
|
+
with progress_spinner({"docker rm"}):
|
|
137
|
+
local.shell(
|
|
138
|
+
"docker rm -f {0}".format(container_id),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
logger.info(
|
|
142
|
+
"{0}docker build complete, image ID: {1}".format(
|
|
143
|
+
self.host.print_prefix,
|
|
144
|
+
click.style(image_id, bold=True),
|
|
237
145
|
),
|
|
238
|
-
err=True,
|
|
239
146
|
)
|
|
240
147
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
docker_id,
|
|
265
|
-
remote_filename,
|
|
266
|
-
temp_filename,
|
|
148
|
+
def run_shell_command(
|
|
149
|
+
self,
|
|
150
|
+
command: StringCommand,
|
|
151
|
+
print_output: bool = False,
|
|
152
|
+
print_input: bool = False,
|
|
153
|
+
**arguments: Unpack["ConnectorArguments"],
|
|
154
|
+
) -> tuple[bool, CommandOutput]:
|
|
155
|
+
local_arguments = extract_control_arguments(arguments)
|
|
156
|
+
|
|
157
|
+
container_id = self.container_id
|
|
158
|
+
|
|
159
|
+
command = make_unix_command_for_host(self.state, self.host, command, **arguments)
|
|
160
|
+
command = StringCommand(QuoteString(command))
|
|
161
|
+
|
|
162
|
+
docker_flags = "-it" if local_arguments.get("_get_pty") else "-i"
|
|
163
|
+
docker_command = StringCommand(
|
|
164
|
+
"docker",
|
|
165
|
+
"exec",
|
|
166
|
+
docker_flags,
|
|
167
|
+
container_id,
|
|
168
|
+
"sh",
|
|
169
|
+
"-c",
|
|
170
|
+
command,
|
|
267
171
|
)
|
|
268
172
|
|
|
269
|
-
|
|
270
|
-
state,
|
|
271
|
-
host,
|
|
173
|
+
return self.local.run_shell_command(
|
|
272
174
|
docker_command,
|
|
273
175
|
print_output=print_output,
|
|
274
176
|
print_input=print_input,
|
|
177
|
+
**local_arguments,
|
|
275
178
|
)
|
|
276
179
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
180
|
+
def put_file(
|
|
181
|
+
self,
|
|
182
|
+
filename_or_io,
|
|
183
|
+
remote_filename,
|
|
184
|
+
remote_temp_filename=None, # ignored
|
|
185
|
+
print_output=False,
|
|
186
|
+
print_input=False,
|
|
187
|
+
**kwargs, # ignored (sudo/etc)
|
|
188
|
+
) -> bool:
|
|
189
|
+
"""
|
|
190
|
+
Upload a file/IO object to the target Docker container by copying it to a
|
|
191
|
+
temporary location and then uploading it into the container using ``docker cp``.
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
fd, temp_filename = mkstemp()
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
# Load our file or IO object and write it to the temporary file
|
|
198
|
+
with get_file_io(filename_or_io) as file_io:
|
|
199
|
+
with open(temp_filename, "wb") as temp_f:
|
|
200
|
+
data = file_io.read()
|
|
201
|
+
|
|
202
|
+
if isinstance(data, str):
|
|
203
|
+
data = data.encode()
|
|
204
|
+
|
|
205
|
+
temp_f.write(data)
|
|
206
|
+
|
|
207
|
+
docker_command = StringCommand(
|
|
208
|
+
"docker",
|
|
209
|
+
"cp",
|
|
210
|
+
temp_filename,
|
|
211
|
+
f"{self.container_id}:{remote_filename}",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
status, output = self.local.run_shell_command(
|
|
215
|
+
docker_command,
|
|
216
|
+
print_output=print_output,
|
|
217
|
+
print_input=print_input,
|
|
218
|
+
)
|
|
219
|
+
finally:
|
|
220
|
+
os.close(fd)
|
|
221
|
+
os.remove(temp_filename)
|
|
222
|
+
|
|
223
|
+
if not status:
|
|
224
|
+
raise IOError(output.stderr)
|
|
225
|
+
|
|
226
|
+
if print_output:
|
|
227
|
+
click.echo(
|
|
228
|
+
"{0}file uploaded to container: {1}".format(
|
|
229
|
+
self.host.print_prefix,
|
|
230
|
+
remote_filename,
|
|
231
|
+
),
|
|
232
|
+
err=True,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
return status
|
|
236
|
+
|
|
237
|
+
def get_file(
|
|
238
|
+
self,
|
|
239
|
+
remote_filename,
|
|
240
|
+
filename_or_io,
|
|
241
|
+
remote_temp_filename=None, # ignored
|
|
242
|
+
print_output=False,
|
|
243
|
+
print_input=False,
|
|
244
|
+
**kwargs, # ignored (sudo/etc)
|
|
245
|
+
) -> bool:
|
|
246
|
+
"""
|
|
247
|
+
Download a file from the target Docker container by copying it to a temporary
|
|
248
|
+
location and then reading that into our final file/IO object.
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
fd, temp_filename = mkstemp()
|
|
304
252
|
|
|
305
|
-
|
|
253
|
+
try:
|
|
254
|
+
docker_command = StringCommand(
|
|
255
|
+
"docker",
|
|
256
|
+
"cp",
|
|
257
|
+
f"{self.container_id}:{remote_filename}",
|
|
258
|
+
temp_filename,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
status, output = self.local.run_shell_command(
|
|
262
|
+
docker_command,
|
|
263
|
+
print_output=print_output,
|
|
264
|
+
print_input=print_input,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Load the temporary file and write it to our file or IO object
|
|
268
|
+
with open(temp_filename, "rb") as temp_f:
|
|
269
|
+
with get_file_io(filename_or_io, "wb") as file_io:
|
|
270
|
+
data = temp_f.read()
|
|
271
|
+
file_io.write(data)
|
|
272
|
+
finally:
|
|
273
|
+
os.close(fd)
|
|
274
|
+
os.remove(temp_filename)
|
|
275
|
+
|
|
276
|
+
if not status:
|
|
277
|
+
raise IOError(output.stderr)
|
|
278
|
+
|
|
279
|
+
if print_output:
|
|
280
|
+
click.echo(
|
|
281
|
+
"{0}file downloaded from container: {1}".format(
|
|
282
|
+
self.host.print_prefix,
|
|
283
|
+
remote_filename,
|
|
284
|
+
),
|
|
285
|
+
err=True,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
return status
|