pyinfra 2.9.2__py2.py3-none-any.whl → 3.0b1__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 +261 -255
- pyinfra/api/arguments_typed.py +77 -0
- pyinfra/api/command.py +66 -53
- pyinfra/api/config.py +27 -22
- pyinfra/api/connect.py +1 -1
- pyinfra/api/connectors.py +2 -24
- pyinfra/api/deploy.py +21 -52
- pyinfra/api/exceptions.py +33 -8
- pyinfra/api/facts.py +77 -113
- pyinfra/api/host.py +150 -82
- pyinfra/api/inventory.py +17 -25
- pyinfra/api/operation.py +232 -198
- pyinfra/api/operations.py +102 -148
- pyinfra/api/state.py +137 -79
- pyinfra/api/util.py +55 -70
- pyinfra/connectors/base.py +150 -0
- pyinfra/connectors/chroot.py +160 -169
- pyinfra/connectors/docker.py +227 -237
- pyinfra/connectors/dockerssh.py +231 -253
- pyinfra/connectors/local.py +195 -207
- pyinfra/connectors/ssh.py +528 -615
- pyinfra/connectors/ssh_util.py +114 -0
- pyinfra/connectors/sshuserclient/client.py +5 -3
- pyinfra/connectors/terraform.py +86 -65
- pyinfra/connectors/util.py +212 -137
- pyinfra/connectors/vagrant.py +55 -48
- pyinfra/context.py +3 -2
- pyinfra/facts/docker.py +1 -0
- pyinfra/facts/files.py +45 -32
- pyinfra/facts/git.py +3 -1
- pyinfra/facts/gpg.py +1 -1
- pyinfra/facts/hardware.py +4 -2
- pyinfra/facts/iptables.py +5 -3
- pyinfra/facts/mysql.py +1 -0
- pyinfra/facts/postgres.py +168 -0
- pyinfra/facts/postgresql.py +5 -161
- pyinfra/facts/selinux.py +3 -1
- pyinfra/facts/server.py +77 -30
- pyinfra/facts/systemd.py +29 -12
- pyinfra/facts/sysvinit.py +10 -10
- pyinfra/facts/util/packaging.py +4 -2
- pyinfra/local.py +4 -5
- pyinfra/operations/apk.py +3 -3
- pyinfra/operations/apt.py +25 -47
- pyinfra/operations/brew.py +7 -14
- pyinfra/operations/bsdinit.py +4 -4
- pyinfra/operations/cargo.py +1 -1
- pyinfra/operations/choco.py +1 -1
- pyinfra/operations/dnf.py +4 -4
- pyinfra/operations/files.py +108 -321
- pyinfra/operations/gem.py +1 -1
- pyinfra/operations/git.py +6 -37
- pyinfra/operations/iptables.py +2 -10
- pyinfra/operations/launchd.py +1 -1
- pyinfra/operations/lxd.py +1 -9
- pyinfra/operations/mysql.py +5 -28
- pyinfra/operations/npm.py +1 -1
- pyinfra/operations/openrc.py +1 -1
- pyinfra/operations/pacman.py +3 -3
- pyinfra/operations/pip.py +14 -15
- pyinfra/operations/pkg.py +1 -1
- pyinfra/operations/pkgin.py +3 -3
- pyinfra/operations/postgres.py +347 -0
- pyinfra/operations/postgresql.py +17 -380
- pyinfra/operations/python.py +2 -17
- pyinfra/operations/selinux.py +5 -28
- pyinfra/operations/server.py +59 -84
- pyinfra/operations/snap.py +1 -3
- pyinfra/operations/ssh.py +8 -23
- pyinfra/operations/systemd.py +7 -7
- pyinfra/operations/sysvinit.py +3 -12
- pyinfra/operations/upstart.py +4 -4
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/files.py +2 -2
- pyinfra/operations/util/packaging.py +6 -24
- pyinfra/operations/util/service.py +18 -37
- pyinfra/operations/vzctl.py +2 -2
- pyinfra/operations/xbps.py +3 -3
- pyinfra/operations/yum.py +4 -4
- pyinfra/operations/zypper.py +4 -4
- {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/METADATA +19 -22
- pyinfra-3.0b1.dist-info/RECORD +163 -0
- pyinfra-3.0b1.dist-info/entry_points.txt +11 -0
- pyinfra_cli/__main__.py +2 -0
- pyinfra_cli/commands.py +7 -2
- pyinfra_cli/exceptions.py +83 -42
- pyinfra_cli/inventory.py +19 -4
- pyinfra_cli/log.py +17 -3
- pyinfra_cli/main.py +133 -90
- pyinfra_cli/prints.py +93 -129
- pyinfra_cli/util.py +60 -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 +100 -200
- 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 +66 -107
- tests/test_connectors/test_terraform.py +9 -15
- tests/test_connectors/test_util.py +24 -46
- tests/test_connectors/test_vagrant.py +4 -4
- 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.0b1.dist-info}/LICENSE.md +0 -0
- {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/WHEEL +0 -0
- {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/top_level.txt +0 -0
pyinfra/api/command.py
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import shlex
|
|
2
2
|
from inspect import getfullargspec
|
|
3
3
|
from string import Formatter
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING, Callable, Union
|
|
5
5
|
|
|
6
6
|
import gevent
|
|
7
|
+
from typing_extensions import Unpack
|
|
7
8
|
|
|
8
9
|
from pyinfra.context import ctx_config, ctx_host
|
|
9
10
|
|
|
10
|
-
from .arguments import
|
|
11
|
+
from .arguments import ConnectorArguments
|
|
11
12
|
|
|
12
13
|
if TYPE_CHECKING:
|
|
13
14
|
from pyinfra.api.host import Host
|
|
14
15
|
from pyinfra.api.state import State
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
def make_formatted_string_command(string: str, *args, **kwargs):
|
|
18
|
+
def make_formatted_string_command(string: str, *args, **kwargs) -> "StringCommand":
|
|
18
19
|
"""
|
|
19
20
|
Helper function that takes a shell command or script as a string, splits it
|
|
20
21
|
using ``shlex.split`` and then formats each bit, returning a ``StringCommand``
|
|
@@ -50,39 +51,46 @@ class MaskString(str):
|
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
class QuoteString:
|
|
53
|
-
|
|
54
|
-
self.object = obj
|
|
54
|
+
obj: Union[str, "StringCommand"]
|
|
55
55
|
|
|
56
|
-
def
|
|
57
|
-
|
|
56
|
+
def __init__(self, obj: Union[str, "StringCommand"]):
|
|
57
|
+
self.obj = obj
|
|
58
|
+
|
|
59
|
+
def __repr__(self) -> str:
|
|
60
|
+
return f"QuoteString({self.obj})"
|
|
58
61
|
|
|
59
62
|
|
|
60
63
|
class PyinfraCommand:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
connector_arguments: ConnectorArguments
|
|
65
|
+
|
|
66
|
+
def __init__(self, **arguments: Unpack[ConnectorArguments]):
|
|
67
|
+
self.connector_arguments = arguments
|
|
65
68
|
|
|
66
|
-
def __eq__(self, other):
|
|
69
|
+
def __eq__(self, other) -> bool:
|
|
67
70
|
if isinstance(other, self.__class__) and repr(self) == repr(other):
|
|
68
71
|
return True
|
|
69
72
|
return False
|
|
70
73
|
|
|
71
|
-
def execute(self, state: "State", host: "Host",
|
|
74
|
+
def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
|
|
72
75
|
raise NotImplementedError
|
|
73
76
|
|
|
74
77
|
|
|
75
78
|
class StringCommand(PyinfraCommand):
|
|
76
|
-
def __init__(
|
|
77
|
-
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
*bits,
|
|
82
|
+
_separator=" ",
|
|
83
|
+
**arguments: Unpack[ConnectorArguments],
|
|
84
|
+
):
|
|
85
|
+
super().__init__(**arguments)
|
|
78
86
|
self.bits = bits
|
|
79
|
-
self.separator =
|
|
87
|
+
self.separator = _separator
|
|
80
88
|
|
|
81
|
-
def __str__(self):
|
|
89
|
+
def __str__(self) -> str:
|
|
82
90
|
return self.get_masked_value()
|
|
83
91
|
|
|
84
|
-
def __repr__(self):
|
|
85
|
-
return "StringCommand({
|
|
92
|
+
def __repr__(self) -> str:
|
|
93
|
+
return f"StringCommand({self.get_masked_value()})"
|
|
86
94
|
|
|
87
95
|
def _get_all_bits(self, bit_accessor):
|
|
88
96
|
all_bits = []
|
|
@@ -91,7 +99,7 @@ class StringCommand(PyinfraCommand):
|
|
|
91
99
|
quote = False
|
|
92
100
|
if isinstance(bit, QuoteString):
|
|
93
101
|
quote = True
|
|
94
|
-
bit = bit.
|
|
102
|
+
bit = bit.obj
|
|
95
103
|
|
|
96
104
|
if isinstance(bit, StringCommand):
|
|
97
105
|
bit = bit_accessor(bit)
|
|
@@ -106,14 +114,14 @@ class StringCommand(PyinfraCommand):
|
|
|
106
114
|
|
|
107
115
|
return all_bits
|
|
108
116
|
|
|
109
|
-
def get_raw_value(self):
|
|
117
|
+
def get_raw_value(self) -> str:
|
|
110
118
|
return self.separator.join(
|
|
111
119
|
self._get_all_bits(
|
|
112
120
|
lambda bit: bit.get_raw_value(),
|
|
113
121
|
),
|
|
114
122
|
)
|
|
115
123
|
|
|
116
|
-
def get_masked_value(self):
|
|
124
|
+
def get_masked_value(self) -> str:
|
|
117
125
|
return self.separator.join(
|
|
118
126
|
[
|
|
119
127
|
"***" if isinstance(bit, MaskString) else bit
|
|
@@ -121,20 +129,25 @@ class StringCommand(PyinfraCommand):
|
|
|
121
129
|
],
|
|
122
130
|
)
|
|
123
131
|
|
|
124
|
-
def execute(self, state: "State", host: "Host",
|
|
125
|
-
|
|
132
|
+
def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
|
|
133
|
+
connector_arguments.update(self.connector_arguments)
|
|
126
134
|
|
|
127
135
|
return host.run_shell_command(
|
|
128
136
|
self,
|
|
129
137
|
print_output=state.print_output,
|
|
130
138
|
print_input=state.print_input,
|
|
131
|
-
|
|
132
|
-
**executor_kwargs,
|
|
139
|
+
**connector_arguments,
|
|
133
140
|
)
|
|
134
141
|
|
|
135
142
|
|
|
136
143
|
class FileUploadCommand(PyinfraCommand):
|
|
137
|
-
def __init__(
|
|
144
|
+
def __init__(
|
|
145
|
+
self,
|
|
146
|
+
src: str,
|
|
147
|
+
dest: str,
|
|
148
|
+
remote_temp_filename=None,
|
|
149
|
+
**kwargs: Unpack[ConnectorArguments],
|
|
150
|
+
):
|
|
138
151
|
super().__init__(**kwargs)
|
|
139
152
|
self.src = src
|
|
140
153
|
self.dest = dest
|
|
@@ -143,8 +156,8 @@ class FileUploadCommand(PyinfraCommand):
|
|
|
143
156
|
def __repr__(self):
|
|
144
157
|
return "FileUploadCommand({0}, {1})".format(self.src, self.dest)
|
|
145
158
|
|
|
146
|
-
def execute(self, state: "State", host: "Host",
|
|
147
|
-
|
|
159
|
+
def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
|
|
160
|
+
connector_arguments.update(self.connector_arguments)
|
|
148
161
|
|
|
149
162
|
return host.put_file(
|
|
150
163
|
self.src,
|
|
@@ -152,12 +165,18 @@ class FileUploadCommand(PyinfraCommand):
|
|
|
152
165
|
remote_temp_filename=self.remote_temp_filename,
|
|
153
166
|
print_output=state.print_output,
|
|
154
167
|
print_input=state.print_input,
|
|
155
|
-
**
|
|
168
|
+
**connector_arguments,
|
|
156
169
|
)
|
|
157
170
|
|
|
158
171
|
|
|
159
172
|
class FileDownloadCommand(PyinfraCommand):
|
|
160
|
-
def __init__(
|
|
173
|
+
def __init__(
|
|
174
|
+
self,
|
|
175
|
+
src: str,
|
|
176
|
+
dest: str,
|
|
177
|
+
remote_temp_filename=None,
|
|
178
|
+
**kwargs: Unpack[ConnectorArguments],
|
|
179
|
+
):
|
|
161
180
|
super().__init__(**kwargs)
|
|
162
181
|
self.src = src
|
|
163
182
|
self.dest = dest
|
|
@@ -166,8 +185,8 @@ class FileDownloadCommand(PyinfraCommand):
|
|
|
166
185
|
def __repr__(self):
|
|
167
186
|
return "FileDownloadCommand({0}, {1})".format(self.src, self.dest)
|
|
168
187
|
|
|
169
|
-
def execute(self, state: "State", host: "Host",
|
|
170
|
-
|
|
188
|
+
def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
|
|
189
|
+
connector_arguments.update(self.connector_arguments)
|
|
171
190
|
|
|
172
191
|
return host.get_file(
|
|
173
192
|
self.src,
|
|
@@ -175,12 +194,18 @@ class FileDownloadCommand(PyinfraCommand):
|
|
|
175
194
|
remote_temp_filename=self.remote_temp_filename,
|
|
176
195
|
print_output=state.print_output,
|
|
177
196
|
print_input=state.print_input,
|
|
178
|
-
**
|
|
197
|
+
**connector_arguments,
|
|
179
198
|
)
|
|
180
199
|
|
|
181
200
|
|
|
182
201
|
class FunctionCommand(PyinfraCommand):
|
|
183
|
-
def __init__(
|
|
202
|
+
def __init__(
|
|
203
|
+
self,
|
|
204
|
+
function: Callable,
|
|
205
|
+
args,
|
|
206
|
+
func_kwargs,
|
|
207
|
+
**kwargs: Unpack[ConnectorArguments],
|
|
208
|
+
):
|
|
184
209
|
super().__init__(**kwargs)
|
|
185
210
|
self.function = function
|
|
186
211
|
self.args = args
|
|
@@ -193,34 +218,22 @@ class FunctionCommand(PyinfraCommand):
|
|
|
193
218
|
self.kwargs,
|
|
194
219
|
)
|
|
195
220
|
|
|
196
|
-
def execute(self, state: "State", host: "Host",
|
|
221
|
+
def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
|
|
197
222
|
argspec = getfullargspec(self.function)
|
|
198
223
|
if "state" in argspec.args and "host" in argspec.args:
|
|
199
224
|
return self.function(state, host, *self.args, **self.kwargs)
|
|
200
225
|
|
|
201
|
-
# Note: we use try/except here to catch any exception inside the greenlet
|
|
202
|
-
# and bubble that back out as a return value before re-raising outside.
|
|
203
|
-
# This is because gevent dumps stack traces when raised within a greenlet
|
|
204
|
-
# and we want to handle that ourselves.
|
|
205
226
|
def execute_function():
|
|
206
227
|
with ctx_config.use(state.config.copy()):
|
|
207
228
|
with ctx_host.use(host):
|
|
208
|
-
|
|
209
|
-
self.function(*self.args, **self.kwargs)
|
|
210
|
-
except Exception as e:
|
|
211
|
-
return e
|
|
212
|
-
else:
|
|
213
|
-
return True
|
|
229
|
+
self.function(*self.args, **self.kwargs)
|
|
214
230
|
|
|
215
231
|
greenlet = gevent.spawn(execute_function)
|
|
216
|
-
|
|
217
|
-
if isinstance(res, Exception):
|
|
218
|
-
raise res
|
|
219
|
-
return res
|
|
232
|
+
return greenlet.get()
|
|
220
233
|
|
|
221
234
|
|
|
222
235
|
class RsyncCommand(PyinfraCommand):
|
|
223
|
-
def __init__(self, src: str, dest: str, flags, **kwargs):
|
|
236
|
+
def __init__(self, src: str, dest: str, flags, **kwargs: Unpack[ConnectorArguments]):
|
|
224
237
|
super().__init__(**kwargs)
|
|
225
238
|
self.src = src
|
|
226
239
|
self.dest = dest
|
|
@@ -229,12 +242,12 @@ class RsyncCommand(PyinfraCommand):
|
|
|
229
242
|
def __repr__(self):
|
|
230
243
|
return "RsyncCommand({0}, {1}, {2})".format(self.src, self.dest, self.flags)
|
|
231
244
|
|
|
232
|
-
def execute(self, state: "State", host: "Host",
|
|
245
|
+
def execute(self, state: "State", host: "Host", connector_arguments: ConnectorArguments):
|
|
233
246
|
return host.rsync(
|
|
234
247
|
self.src,
|
|
235
248
|
self.dest,
|
|
236
249
|
self.flags,
|
|
237
250
|
print_output=state.print_output,
|
|
238
251
|
print_input=state.print_input,
|
|
239
|
-
**
|
|
252
|
+
**connector_arguments,
|
|
240
253
|
)
|
pyinfra/api/config.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from os import path
|
|
2
|
+
from typing import Optional
|
|
2
3
|
|
|
4
|
+
# TODO: move to importlib.resources
|
|
3
5
|
from pkg_resources import Requirement, ResolutionError, parse_version, require
|
|
4
6
|
|
|
5
7
|
from pyinfra import __version__, state
|
|
@@ -9,37 +11,40 @@ from .exceptions import PyinfraError
|
|
|
9
11
|
|
|
10
12
|
class ConfigDefaults:
|
|
11
13
|
# % of hosts which have to fail for all operations to stop
|
|
12
|
-
FAIL_PERCENT = None
|
|
14
|
+
FAIL_PERCENT: Optional[int] = None
|
|
13
15
|
# Seconds to timeout SSH connections
|
|
14
|
-
CONNECT_TIMEOUT = 10
|
|
15
|
-
# Temporary directory (on the remote side) to use for caching any files/downloads
|
|
16
|
-
|
|
16
|
+
CONNECT_TIMEOUT: int = 10
|
|
17
|
+
# Temporary directory (on the remote side) to use for caching any files/downloads, the default
|
|
18
|
+
# None value first tries to load the hosts' temporary directory configured via "TMPDIR" env
|
|
19
|
+
# variable, falling back to DEFAULT_TEMP_DIR if not set.
|
|
20
|
+
TEMP_DIR: Optional[str] = None
|
|
21
|
+
DEFAULT_TEMP_DIR: str = "/tmp"
|
|
17
22
|
# Gevent pool size (defaults to #of target hosts)
|
|
18
|
-
PARALLEL = 0
|
|
23
|
+
PARALLEL: int = 0
|
|
19
24
|
# Specify the required pyinfra version (using PEP 440 setuptools specifier)
|
|
20
|
-
REQUIRE_PYINFRA_VERSION = None
|
|
25
|
+
REQUIRE_PYINFRA_VERSION: Optional[str] = None
|
|
21
26
|
# Specify any required packages (either using PEP 440 or a requirements file)
|
|
22
27
|
# Note: this can also include pyinfra potentially replacing REQUIRE_PYINFRA_VERSION
|
|
23
|
-
REQUIRE_PACKAGES = None
|
|
28
|
+
REQUIRE_PACKAGES: Optional[str] = None
|
|
24
29
|
# All these can be overridden inside individual operation calls:
|
|
25
30
|
# Switch to this user (from ssh_user) using su before executing operations
|
|
26
|
-
SU_USER = None
|
|
27
|
-
USE_SU_LOGIN = False
|
|
28
|
-
SU_SHELL =
|
|
29
|
-
PRESERVE_SU_ENV = False
|
|
31
|
+
SU_USER: Optional[str] = None
|
|
32
|
+
USE_SU_LOGIN: bool = False
|
|
33
|
+
SU_SHELL: bool = False
|
|
34
|
+
PRESERVE_SU_ENV: bool = False
|
|
30
35
|
# Use sudo and optional user
|
|
31
|
-
SUDO = False
|
|
32
|
-
SUDO_USER = None
|
|
33
|
-
PRESERVE_SUDO_ENV = False
|
|
34
|
-
USE_SUDO_LOGIN = False
|
|
35
|
-
|
|
36
|
+
SUDO: bool = False
|
|
37
|
+
SUDO_USER: Optional[str] = None
|
|
38
|
+
PRESERVE_SUDO_ENV: bool = False
|
|
39
|
+
USE_SUDO_LOGIN: bool = False
|
|
40
|
+
SUDO_PASSWORD: Optional[str] = None
|
|
36
41
|
# Use doas and optional user
|
|
37
|
-
DOAS = False
|
|
38
|
-
DOAS_USER = None
|
|
42
|
+
DOAS: bool = False
|
|
43
|
+
DOAS_USER: Optional[str] = None
|
|
39
44
|
# Only show errors but don't count as failure
|
|
40
|
-
IGNORE_ERRORS = False
|
|
45
|
+
IGNORE_ERRORS: bool = False
|
|
41
46
|
# Shell to use to execute commands
|
|
42
|
-
SHELL = "sh"
|
|
47
|
+
SHELL: str = "sh"
|
|
43
48
|
|
|
44
49
|
|
|
45
50
|
config_defaults = {key: value for key, value in ConfigDefaults.__dict__.items() if key.isupper()}
|
|
@@ -51,7 +56,7 @@ def check_pyinfra_version(version: str):
|
|
|
51
56
|
running_version = parse_version(__version__)
|
|
52
57
|
required_versions = Requirement.parse("pyinfra{0}".format(version))
|
|
53
58
|
|
|
54
|
-
if not required_versions
|
|
59
|
+
if running_version not in required_versions: # type: ignore[operator]
|
|
55
60
|
raise PyinfraError(
|
|
56
61
|
f"pyinfra version requirement not met (requires {version}, running {__version__})"
|
|
57
62
|
)
|
|
@@ -64,7 +69,7 @@ def check_require_packages(requirements_config):
|
|
|
64
69
|
if isinstance(requirements_config, (list, tuple)):
|
|
65
70
|
requirements = requirements_config
|
|
66
71
|
else:
|
|
67
|
-
with open(path.join(state.cwd, requirements_config), encoding="utf-8") as f:
|
|
72
|
+
with open(path.join(state.cwd or "", requirements_config), encoding="utf-8") as f:
|
|
68
73
|
requirements = [line.split("#egg=")[-1] for line in f.read().splitlines()]
|
|
69
74
|
|
|
70
75
|
try:
|
pyinfra/api/connect.py
CHANGED
pyinfra/api/connectors.py
CHANGED
|
@@ -1,30 +1,8 @@
|
|
|
1
1
|
import pkg_resources
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class BaseConnectorMeta:
|
|
5
|
-
handles_execution = False
|
|
6
|
-
keys_prefix = ""
|
|
7
|
-
|
|
8
|
-
class DataKeys:
|
|
9
|
-
pass
|
|
10
|
-
|
|
11
|
-
@classmethod
|
|
12
|
-
def keys(cls):
|
|
13
|
-
class Keys:
|
|
14
|
-
pass
|
|
15
|
-
|
|
16
|
-
for key in cls.DataKeys.__dict__:
|
|
17
|
-
if not key.startswith("_"):
|
|
18
|
-
setattr(Keys, key, f"{cls.keys_prefix}_{key}")
|
|
19
|
-
|
|
20
|
-
return Keys
|
|
21
|
-
|
|
22
|
-
|
|
23
4
|
def _load_connector(entrypoint):
|
|
24
|
-
|
|
25
|
-
if not getattr(connector, "Meta", None):
|
|
26
|
-
connector.Meta = BaseConnectorMeta
|
|
27
|
-
return connector
|
|
5
|
+
return entrypoint.load()
|
|
28
6
|
|
|
29
7
|
|
|
30
8
|
def get_all_connectors():
|
|
@@ -38,7 +16,7 @@ def get_execution_connectors():
|
|
|
38
16
|
return {
|
|
39
17
|
connector: connector_mod
|
|
40
18
|
for connector, connector_mod in get_all_connectors().items()
|
|
41
|
-
if connector_mod.
|
|
19
|
+
if connector_mod.handles_execution
|
|
42
20
|
}
|
|
43
21
|
|
|
44
22
|
|
pyinfra/api/deploy.py
CHANGED
|
@@ -5,31 +5,24 @@ creation (eg pyinfra-openstack).
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from functools import wraps
|
|
8
|
-
from typing import TYPE_CHECKING, Any, Callable,
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, cast
|
|
9
|
+
|
|
10
|
+
from typing_extensions import ParamSpec
|
|
9
11
|
|
|
10
12
|
import pyinfra
|
|
11
|
-
from pyinfra import context
|
|
13
|
+
from pyinfra import context
|
|
12
14
|
from pyinfra.context import ctx_host, ctx_state
|
|
13
15
|
|
|
14
16
|
from .arguments import pop_global_arguments
|
|
17
|
+
from .arguments_typed import PyinfraOperation
|
|
15
18
|
from .exceptions import PyinfraError
|
|
16
19
|
from .host import Host
|
|
17
|
-
from .util import
|
|
20
|
+
from .util import get_call_location
|
|
18
21
|
|
|
19
22
|
if TYPE_CHECKING:
|
|
20
23
|
from pyinfra.api.state import State
|
|
21
24
|
|
|
22
25
|
|
|
23
|
-
@memoize
|
|
24
|
-
def show_state_host_arguments_warning(call_location):
|
|
25
|
-
logger.warning(
|
|
26
|
-
(
|
|
27
|
-
"{0}:\n\tLegacy deploy function detected! Deploys should no longer define "
|
|
28
|
-
"`state` and `host` arguments."
|
|
29
|
-
).format(call_location),
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
|
|
33
26
|
def add_deploy(state: "State", deploy_func: Callable[..., Any], *args, **kwargs):
|
|
34
27
|
"""
|
|
35
28
|
Prepare & add an deploy to pyinfra.state by executing it on all hosts.
|
|
@@ -58,61 +51,37 @@ def add_deploy(state: "State", deploy_func: Callable[..., Any], *args, **kwargs)
|
|
|
58
51
|
deploy_func(*args, **kwargs)
|
|
59
52
|
|
|
60
53
|
|
|
61
|
-
|
|
54
|
+
P = ParamSpec("P")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def deploy(name: Optional[str] = None, data_defaults=None):
|
|
62
58
|
"""
|
|
63
59
|
Decorator that takes a deploy function (normally from a pyinfra_* package)
|
|
64
60
|
and wraps any operations called inside with any deploy-wide kwargs/data.
|
|
65
61
|
"""
|
|
66
62
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
f.deploy_name = name
|
|
73
|
-
if data_defaults:
|
|
74
|
-
f.deploy_data = data_defaults
|
|
75
|
-
return deploy(f, _call_location=get_call_location())
|
|
76
|
-
|
|
77
|
-
return decorator
|
|
63
|
+
def decorator(func: Callable[P, Any]) -> PyinfraOperation[P]:
|
|
64
|
+
func.deploy_name = name or func.__name__ # type: ignore[attr-defined]
|
|
65
|
+
if data_defaults:
|
|
66
|
+
func.deploy_data = data_defaults # type: ignore[attr-defined]
|
|
67
|
+
return _wrap_deploy(func)
|
|
78
68
|
|
|
79
|
-
|
|
80
|
-
func = func_or_name
|
|
69
|
+
return decorator
|
|
81
70
|
|
|
82
|
-
# Check whether an operation is "legacy" - ie contains state=None, host=None kwargs
|
|
83
|
-
# TODO: remove this in v3
|
|
84
|
-
is_legacy = False
|
|
85
|
-
args, kwargs = get_args_kwargs_spec(func)
|
|
86
|
-
if all(key in kwargs and kwargs[key] is None for key in ("state", "host")):
|
|
87
|
-
show_state_host_arguments_warning(_call_location or get_call_location())
|
|
88
|
-
is_legacy = True
|
|
89
|
-
func.is_legacy = is_legacy # type: ignore
|
|
90
71
|
|
|
72
|
+
def _wrap_deploy(func: Callable[P, Any]) -> PyinfraOperation[P]:
|
|
91
73
|
@wraps(func)
|
|
92
|
-
def decorated_func(*args, **kwargs):
|
|
74
|
+
def decorated_func(*args: P.args, **kwargs: P.kwargs) -> Any:
|
|
93
75
|
deploy_kwargs, _ = pop_global_arguments(kwargs)
|
|
94
76
|
|
|
95
|
-
# If this is a legacy operation function (ie - state & host arg kwargs), ensure that state
|
|
96
|
-
# and host are included as kwargs.
|
|
97
|
-
if func.is_legacy:
|
|
98
|
-
if "state" not in kwargs:
|
|
99
|
-
kwargs["state"] = context.state
|
|
100
|
-
if "host" not in kwargs:
|
|
101
|
-
kwargs["host"] = context.host
|
|
102
|
-
# If not legacy, pop off any state/host kwargs that may come from legacy @deploy functions
|
|
103
|
-
else:
|
|
104
|
-
kwargs.pop("state", None)
|
|
105
|
-
kwargs.pop("host", None)
|
|
106
|
-
|
|
107
|
-
# Name the deploy
|
|
108
|
-
deploy_name = getattr(func, "deploy_name", func.__name__)
|
|
109
77
|
deploy_data = getattr(func, "deploy_data", None)
|
|
110
78
|
|
|
111
79
|
with context.host.deploy(
|
|
112
|
-
name=deploy_name,
|
|
80
|
+
name=func.deploy_name, # type: ignore[attr-defined]
|
|
113
81
|
kwargs=deploy_kwargs,
|
|
114
82
|
data=deploy_data,
|
|
115
83
|
):
|
|
116
84
|
return func(*args, **kwargs)
|
|
117
85
|
|
|
118
|
-
|
|
86
|
+
decorated_func._inner = func # type: ignore[attr-defined]
|
|
87
|
+
return cast(PyinfraOperation[P], decorated_func)
|
pyinfra/api/exceptions.py
CHANGED
|
@@ -4,15 +4,28 @@ class PyinfraError(Exception):
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class
|
|
7
|
+
class ConnectError(PyinfraError):
|
|
8
8
|
"""
|
|
9
|
-
Exception raised when
|
|
9
|
+
Exception raised when connecting fails.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class
|
|
13
|
+
class FactError(PyinfraError):
|
|
14
14
|
"""
|
|
15
|
-
Exception raised
|
|
15
|
+
Exception raised during fact gathering staging if a fact is unable to
|
|
16
|
+
generate output/change state.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FactTypeError(FactError, TypeError):
|
|
21
|
+
"""
|
|
22
|
+
Exception raised when a fact is passed invalid argument types.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FactValueError(FactError, ValueError):
|
|
27
|
+
"""
|
|
28
|
+
Exception raised when a fact is passed invalid argument values.
|
|
16
29
|
"""
|
|
17
30
|
|
|
18
31
|
|
|
@@ -47,19 +60,31 @@ class InventoryError(PyinfraError):
|
|
|
47
60
|
"""
|
|
48
61
|
|
|
49
62
|
|
|
50
|
-
class NoConnectorError(PyinfraError,
|
|
63
|
+
class NoConnectorError(PyinfraError, ValueError):
|
|
51
64
|
"""
|
|
52
65
|
Raised when a requested connector is missing.
|
|
53
66
|
"""
|
|
54
67
|
|
|
55
68
|
|
|
56
|
-
class NoHostError(PyinfraError,
|
|
69
|
+
class NoHostError(PyinfraError, KeyError):
|
|
57
70
|
"""
|
|
58
71
|
Raised when an inventory is missing a host.
|
|
59
72
|
"""
|
|
60
73
|
|
|
61
74
|
|
|
62
|
-
class NoGroupError(PyinfraError,
|
|
75
|
+
class NoGroupError(PyinfraError, KeyError):
|
|
76
|
+
"""
|
|
77
|
+
Raised when an inventory is missing a group.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ConnectorDataTypeError(PyinfraError, TypeError):
|
|
82
|
+
"""
|
|
83
|
+
Raised when host connector data has invalid types.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ArgumentTypeError(PyinfraError, TypeError):
|
|
63
88
|
"""
|
|
64
|
-
|
|
89
|
+
Raised when global arguments are passed with invalid types.
|
|
65
90
|
"""
|