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
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
from io import IOBase
|
|
5
|
+
from typing import (
|
|
6
|
+
TYPE_CHECKING,
|
|
7
|
+
Any,
|
|
8
|
+
Iterable,
|
|
9
|
+
Iterator,
|
|
10
|
+
Optional,
|
|
11
|
+
Type,
|
|
12
|
+
TypeVar,
|
|
13
|
+
Union,
|
|
14
|
+
cast,
|
|
15
|
+
get_type_hints,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from typing_extensions import TypedDict, Unpack
|
|
19
|
+
|
|
20
|
+
from pyinfra.api.exceptions import ConnectorDataTypeError
|
|
21
|
+
from pyinfra.api.util import raise_if_bad_type
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from pyinfra.api.arguments import ConnectorArguments
|
|
25
|
+
from pyinfra.api.command import StringCommand
|
|
26
|
+
from pyinfra.api.host import Host, HostData
|
|
27
|
+
from pyinfra.api.state import State
|
|
28
|
+
|
|
29
|
+
from .util import CommandOutput
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
T = TypeVar("T")
|
|
33
|
+
default_sentinel = object()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def host_to_connector_data(
|
|
37
|
+
connector_data: Type[T],
|
|
38
|
+
connector_data_meta: dict[str, DataMeta],
|
|
39
|
+
host_data: "HostData",
|
|
40
|
+
) -> T:
|
|
41
|
+
data: T = cast(T, {})
|
|
42
|
+
for key, type_ in get_type_hints(connector_data).items():
|
|
43
|
+
value = host_data.get(key, default_sentinel)
|
|
44
|
+
if value is default_sentinel:
|
|
45
|
+
value = connector_data_meta[key].default
|
|
46
|
+
else:
|
|
47
|
+
raise_if_bad_type(
|
|
48
|
+
value,
|
|
49
|
+
type_,
|
|
50
|
+
ConnectorDataTypeError,
|
|
51
|
+
f"Invalid connector data `{key}`:",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
data[key] = value # type: ignore
|
|
55
|
+
return data
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class DataMeta:
|
|
59
|
+
description: str
|
|
60
|
+
default: Any
|
|
61
|
+
|
|
62
|
+
def __init__(self, description, default=None) -> None:
|
|
63
|
+
self.description = description
|
|
64
|
+
self.default = default
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ConnectorData(TypedDict, total=False):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class BaseConnector(abc.ABC):
|
|
72
|
+
state: "State"
|
|
73
|
+
host: "Host"
|
|
74
|
+
|
|
75
|
+
handles_execution = False
|
|
76
|
+
|
|
77
|
+
data_cls: Type = ConnectorData
|
|
78
|
+
data_meta: dict[str, DataMeta] = {}
|
|
79
|
+
|
|
80
|
+
def __init__(self, state: "State", host: "Host"):
|
|
81
|
+
self.state = state
|
|
82
|
+
self.host = host
|
|
83
|
+
self.data = host_to_connector_data(self.data_cls, self.data_meta, host.data)
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
@abc.abstractmethod
|
|
87
|
+
def make_names_data(name: str) -> Iterator[tuple[str, dict, list[str]]]:
|
|
88
|
+
"""
|
|
89
|
+
Generates hosts/data/groups information for inventory. This allows a
|
|
90
|
+
single connector reference to generate multiple target hosts.
|
|
91
|
+
"""
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
def connect(self) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Connect this connector instance.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def disconnect(self) -> None:
|
|
100
|
+
"""
|
|
101
|
+
Disconnect this connector instance.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
@abc.abstractmethod
|
|
105
|
+
def run_shell_command(
|
|
106
|
+
self,
|
|
107
|
+
command: "StringCommand",
|
|
108
|
+
print_output: bool,
|
|
109
|
+
print_input: bool,
|
|
110
|
+
**arguments: Unpack["ConnectorArguments"],
|
|
111
|
+
) -> tuple[bool, "CommandOutput"]: ...
|
|
112
|
+
|
|
113
|
+
@abc.abstractmethod
|
|
114
|
+
def put_file(
|
|
115
|
+
self,
|
|
116
|
+
filename_or_io: Union[str, IOBase],
|
|
117
|
+
remote_filename: str,
|
|
118
|
+
remote_temp_filename: Optional[str] = None,
|
|
119
|
+
print_output: bool = False,
|
|
120
|
+
print_input: bool = False,
|
|
121
|
+
**arguments: Unpack["ConnectorArguments"],
|
|
122
|
+
) -> bool: ...
|
|
123
|
+
|
|
124
|
+
@abc.abstractmethod
|
|
125
|
+
def get_file(
|
|
126
|
+
self,
|
|
127
|
+
remote_filename: str,
|
|
128
|
+
filename_or_io: Union[str, IOBase],
|
|
129
|
+
remote_temp_filename: Optional[str] = None,
|
|
130
|
+
print_output: bool = False,
|
|
131
|
+
print_input: bool = False,
|
|
132
|
+
**arguments: Unpack["ConnectorArguments"],
|
|
133
|
+
) -> bool: ...
|
|
134
|
+
|
|
135
|
+
def check_can_rsync(self):
|
|
136
|
+
raise NotImplementedError("This connector does not support rsync")
|
|
137
|
+
|
|
138
|
+
def rsync(
|
|
139
|
+
self,
|
|
140
|
+
src: str,
|
|
141
|
+
dest: str,
|
|
142
|
+
flags: Iterable[str],
|
|
143
|
+
print_output: bool = False,
|
|
144
|
+
print_input: bool = False,
|
|
145
|
+
**arguments: Unpack["ConnectorArguments"],
|
|
146
|
+
) -> bool:
|
|
147
|
+
raise NotImplementedError("This connector does not support rsync")
|
pyinfra/connectors/chroot.py
CHANGED
|
@@ -3,210 +3,201 @@ from tempfile import mkstemp
|
|
|
3
3
|
from typing import TYPE_CHECKING, Optional
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
|
+
from typing_extensions import Unpack
|
|
6
7
|
|
|
7
8
|
from pyinfra import local, logger
|
|
8
9
|
from pyinfra.api import QuoteString, StringCommand
|
|
9
|
-
from pyinfra.api.connectors import BaseConnectorMeta
|
|
10
10
|
from pyinfra.api.exceptions import ConnectError, InventoryError, PyinfraError
|
|
11
11
|
from pyinfra.api.util import get_file_io, memoize
|
|
12
12
|
from pyinfra.progress import progress_spinner
|
|
13
13
|
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
14
|
+
from .base import BaseConnector
|
|
15
|
+
from .local import LocalConnector
|
|
16
|
+
from .util import extract_control_arguments, make_unix_command_for_host
|
|
16
17
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
19
|
+
from pyinfra.api.arguments import ConnectorArguments
|
|
18
20
|
from pyinfra.api.host import Host
|
|
19
21
|
from pyinfra.api.state import State
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
class Meta(BaseConnectorMeta):
|
|
23
|
-
handles_execution = True
|
|
24
|
-
|
|
25
|
-
|
|
26
24
|
@memoize
|
|
27
25
|
def show_warning():
|
|
28
26
|
logger.warning("The @chroot connector is in beta!")
|
|
29
27
|
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
class ChrootConnector(BaseConnector):
|
|
30
|
+
"""
|
|
31
|
+
The chroot connector allows you to execute operations within another root.
|
|
32
|
+
"""
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
handles_execution = True
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
"chroot_directory": "/{0}".format(directory.lstrip("/")),
|
|
39
|
-
}, ["@chroot"]
|
|
36
|
+
local: LocalConnector
|
|
40
37
|
|
|
38
|
+
def __init__(self, state: "State", host: "Host"):
|
|
39
|
+
super().__init__(state, host)
|
|
40
|
+
self.local = LocalConnector(state, host)
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
@staticmethod
|
|
43
|
+
def make_names_data(name: Optional[str] = None):
|
|
44
|
+
if not name:
|
|
45
|
+
raise InventoryError("No directory provided!")
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
with progress_spinner({"chroot run"}):
|
|
47
|
-
local.shell(
|
|
48
|
-
"chroot {0} ls".format(chroot_directory),
|
|
49
|
-
splitlines=True,
|
|
50
|
-
)
|
|
51
|
-
except PyinfraError as e:
|
|
52
|
-
raise ConnectError(e.args[0])
|
|
53
|
-
|
|
54
|
-
host.connector_data["chroot_directory"] = chroot_directory
|
|
55
|
-
return True
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def run_shell_command(
|
|
59
|
-
state: "State",
|
|
60
|
-
host: "Host",
|
|
61
|
-
command,
|
|
62
|
-
get_pty: bool = False,
|
|
63
|
-
timeout=None,
|
|
64
|
-
stdin=None,
|
|
65
|
-
success_exit_codes=None,
|
|
66
|
-
print_output: bool = False,
|
|
67
|
-
print_input: bool = False,
|
|
68
|
-
return_combined_output: bool = False,
|
|
69
|
-
**command_kwargs,
|
|
70
|
-
):
|
|
71
|
-
chroot_directory = host.connector_data["chroot_directory"]
|
|
72
|
-
|
|
73
|
-
command = make_unix_command_for_host(state, host, command, **command_kwargs)
|
|
74
|
-
command = QuoteString(command)
|
|
75
|
-
|
|
76
|
-
logger.debug("--> Running chroot command on (%s): %s", chroot_directory, command)
|
|
77
|
-
|
|
78
|
-
chroot_command = StringCommand(
|
|
79
|
-
"chroot",
|
|
80
|
-
chroot_directory,
|
|
81
|
-
"sh",
|
|
82
|
-
"-c",
|
|
83
|
-
command,
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
return run_local_shell_command(
|
|
87
|
-
state,
|
|
88
|
-
host,
|
|
89
|
-
chroot_command,
|
|
90
|
-
timeout=timeout,
|
|
91
|
-
stdin=stdin,
|
|
92
|
-
success_exit_codes=success_exit_codes,
|
|
93
|
-
print_output=print_output,
|
|
94
|
-
print_input=print_input,
|
|
95
|
-
return_combined_output=return_combined_output,
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def put_file(
|
|
100
|
-
state: "State",
|
|
101
|
-
host: "Host",
|
|
102
|
-
filename_or_io,
|
|
103
|
-
remote_filename,
|
|
104
|
-
remote_temp_filename=None, # ignored
|
|
105
|
-
print_output: bool = False,
|
|
106
|
-
print_input: bool = False,
|
|
107
|
-
**kwargs, # ignored (sudo/etc)
|
|
108
|
-
):
|
|
109
|
-
|
|
110
|
-
_, temp_filename = mkstemp()
|
|
111
|
-
|
|
112
|
-
try:
|
|
113
|
-
# Load our file or IO object and write it to the temporary file
|
|
114
|
-
with get_file_io(filename_or_io) as file_io:
|
|
115
|
-
with open(temp_filename, "wb") as temp_f:
|
|
116
|
-
data = file_io.read()
|
|
117
|
-
|
|
118
|
-
if isinstance(data, str):
|
|
119
|
-
data = data.encode()
|
|
120
|
-
|
|
121
|
-
temp_f.write(data)
|
|
122
|
-
|
|
123
|
-
chroot_directory = host.connector_data["chroot_directory"]
|
|
124
|
-
|
|
125
|
-
chroot_command = "cp {0} {1}/{2}".format(
|
|
126
|
-
temp_filename,
|
|
127
|
-
chroot_directory,
|
|
128
|
-
remote_filename,
|
|
129
|
-
)
|
|
47
|
+
show_warning()
|
|
130
48
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
)
|
|
138
|
-
finally:
|
|
139
|
-
os.remove(temp_filename)
|
|
140
|
-
|
|
141
|
-
if not status:
|
|
142
|
-
raise IOError("\n".join(stderr))
|
|
143
|
-
|
|
144
|
-
if print_output:
|
|
145
|
-
click.echo(
|
|
146
|
-
"{0}file uploaded to chroot: {1}".format(
|
|
147
|
-
host.print_prefix,
|
|
148
|
-
remote_filename,
|
|
149
|
-
),
|
|
150
|
-
err=True,
|
|
151
|
-
)
|
|
49
|
+
yield "@chroot/{0}".format(name), {
|
|
50
|
+
"chroot_directory": "/{0}".format(name.lstrip("/")),
|
|
51
|
+
}, ["@chroot"]
|
|
52
|
+
|
|
53
|
+
def connect(self) -> None:
|
|
54
|
+
self.local.connect()
|
|
152
55
|
|
|
153
|
-
|
|
56
|
+
chroot_directory = self.host.data.chroot_directory
|
|
154
57
|
|
|
58
|
+
try:
|
|
59
|
+
with progress_spinner({"chroot run"}):
|
|
60
|
+
local.shell(
|
|
61
|
+
"chroot {0} ls".format(chroot_directory),
|
|
62
|
+
splitlines=True,
|
|
63
|
+
)
|
|
64
|
+
except PyinfraError as e:
|
|
65
|
+
raise ConnectError(e.args[0])
|
|
155
66
|
|
|
156
|
-
|
|
157
|
-
state: "State",
|
|
158
|
-
host: "Host",
|
|
159
|
-
remote_filename,
|
|
160
|
-
filename_or_io,
|
|
161
|
-
remote_temp_filename=None, # ignored
|
|
162
|
-
print_output: bool = False,
|
|
163
|
-
print_input: bool = False,
|
|
164
|
-
**kwargs, # ignored (sudo/etc)
|
|
165
|
-
):
|
|
67
|
+
self.host.connector_data["chroot_directory"] = chroot_directory
|
|
166
68
|
|
|
167
|
-
|
|
69
|
+
def run_shell_command(
|
|
70
|
+
self,
|
|
71
|
+
command,
|
|
72
|
+
print_output: bool = False,
|
|
73
|
+
print_input: bool = False,
|
|
74
|
+
**command_arguments: Unpack["ConnectorArguments"],
|
|
75
|
+
):
|
|
76
|
+
local_arguments = extract_control_arguments(command_arguments)
|
|
168
77
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
78
|
+
chroot_directory = self.host.connector_data["chroot_directory"]
|
|
79
|
+
|
|
80
|
+
command = make_unix_command_for_host(self.state, self.host, command, **command_arguments)
|
|
81
|
+
command = QuoteString(command)
|
|
82
|
+
|
|
83
|
+
logger.debug("--> Running chroot command on (%s): %s", chroot_directory, command)
|
|
84
|
+
|
|
85
|
+
chroot_command = StringCommand(
|
|
86
|
+
"chroot",
|
|
172
87
|
chroot_directory,
|
|
173
|
-
|
|
174
|
-
|
|
88
|
+
"sh",
|
|
89
|
+
"-c",
|
|
90
|
+
command,
|
|
175
91
|
)
|
|
176
92
|
|
|
177
|
-
|
|
178
|
-
state,
|
|
179
|
-
host,
|
|
93
|
+
return self.local.run_shell_command(
|
|
180
94
|
chroot_command,
|
|
181
95
|
print_output=print_output,
|
|
182
96
|
print_input=print_input,
|
|
97
|
+
**local_arguments,
|
|
183
98
|
)
|
|
184
99
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
100
|
+
def put_file(
|
|
101
|
+
self,
|
|
102
|
+
filename_or_io,
|
|
103
|
+
remote_filename,
|
|
104
|
+
remote_temp_filename=None, # ignored
|
|
105
|
+
print_output: bool = False,
|
|
106
|
+
print_input: bool = False,
|
|
107
|
+
**kwargs, # ignored (sudo/etc)
|
|
108
|
+
):
|
|
109
|
+
_, temp_filename = mkstemp()
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
# Load our file or IO object and write it to the temporary file
|
|
113
|
+
with get_file_io(filename_or_io) as file_io:
|
|
114
|
+
with open(temp_filename, "wb") as temp_f:
|
|
115
|
+
data = file_io.read()
|
|
116
|
+
|
|
117
|
+
if isinstance(data, str):
|
|
118
|
+
data = data.encode()
|
|
119
|
+
|
|
120
|
+
temp_f.write(data)
|
|
121
|
+
|
|
122
|
+
chroot_directory = self.host.connector_data["chroot_directory"]
|
|
123
|
+
chroot_command = StringCommand(
|
|
124
|
+
"cp",
|
|
125
|
+
temp_filename,
|
|
126
|
+
f"{chroot_directory}/{remote_filename}",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
status, output = self.local.run_shell_command(
|
|
130
|
+
chroot_command,
|
|
131
|
+
print_output=print_output,
|
|
132
|
+
print_input=print_input,
|
|
133
|
+
)
|
|
134
|
+
finally:
|
|
135
|
+
os.remove(temp_filename)
|
|
136
|
+
|
|
137
|
+
if not status:
|
|
138
|
+
raise IOError(output.stderr)
|
|
139
|
+
|
|
140
|
+
if print_output:
|
|
141
|
+
click.echo(
|
|
142
|
+
"{0}file uploaded to chroot: {1}".format(
|
|
143
|
+
self.host.print_prefix,
|
|
144
|
+
remote_filename,
|
|
145
|
+
),
|
|
146
|
+
err=True,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return status
|
|
150
|
+
|
|
151
|
+
def get_file(
|
|
152
|
+
self,
|
|
153
|
+
remote_filename,
|
|
154
|
+
filename_or_io,
|
|
155
|
+
remote_temp_filename=None, # ignored
|
|
156
|
+
print_output: bool = False,
|
|
157
|
+
print_input: bool = False,
|
|
158
|
+
**kwargs, # ignored (sudo/etc)
|
|
159
|
+
):
|
|
160
|
+
_, temp_filename = mkstemp()
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
chroot_directory = self.host.connector_data["chroot_directory"]
|
|
164
|
+
chroot_command = StringCommand(
|
|
165
|
+
"cp",
|
|
166
|
+
f"{chroot_directory}/{remote_filename}",
|
|
167
|
+
temp_filename,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
status, output = self.local.run_shell_command(
|
|
171
|
+
chroot_command,
|
|
172
|
+
print_output=print_output,
|
|
173
|
+
print_input=print_input,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Load the temporary file and write it to our file or IO object
|
|
177
|
+
with open(temp_filename, encoding="utf-8") as temp_f:
|
|
178
|
+
with get_file_io(filename_or_io, "wb") as file_io:
|
|
179
|
+
data = temp_f.read()
|
|
180
|
+
data_bytes: bytes
|
|
181
|
+
|
|
182
|
+
if isinstance(data, str):
|
|
183
|
+
data_bytes = data.encode()
|
|
184
|
+
else:
|
|
185
|
+
data_bytes = data
|
|
186
|
+
|
|
187
|
+
file_io.write(data_bytes)
|
|
188
|
+
finally:
|
|
189
|
+
os.remove(temp_filename)
|
|
190
|
+
|
|
191
|
+
if not status:
|
|
192
|
+
raise IOError(output.stderr)
|
|
193
|
+
|
|
194
|
+
if print_output:
|
|
195
|
+
click.echo(
|
|
196
|
+
"{0}file downloaded from chroot: {1}".format(
|
|
197
|
+
self.host.print_prefix,
|
|
198
|
+
remote_filename,
|
|
199
|
+
),
|
|
200
|
+
err=True,
|
|
201
|
+
)
|
|
211
202
|
|
|
212
|
-
|
|
203
|
+
return status
|