pyinfra 3.0.dev0__py2.py3-none-any.whl → 3.0.1__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 +115 -97
- pyinfra/api/arguments_typed.py +80 -0
- pyinfra/api/command.py +5 -3
- pyinfra/api/config.py +139 -39
- pyinfra/api/connectors.py +5 -2
- pyinfra/api/deploy.py +19 -19
- pyinfra/api/exceptions.py +35 -4
- pyinfra/api/facts.py +62 -86
- pyinfra/api/host.py +102 -15
- pyinfra/api/inventory.py +4 -0
- pyinfra/api/operation.py +184 -118
- pyinfra/api/operations.py +66 -113
- pyinfra/api/state.py +53 -34
- pyinfra/api/util.py +64 -33
- pyinfra/connectors/base.py +65 -20
- pyinfra/connectors/chroot.py +15 -13
- pyinfra/connectors/docker.py +62 -72
- pyinfra/connectors/dockerssh.py +20 -19
- pyinfra/connectors/local.py +32 -22
- pyinfra/connectors/ssh.py +162 -86
- pyinfra/connectors/sshuserclient/client.py +1 -1
- pyinfra/connectors/terraform.py +57 -39
- pyinfra/connectors/util.py +26 -27
- pyinfra/connectors/vagrant.py +27 -26
- pyinfra/context.py +1 -0
- pyinfra/facts/apk.py +7 -2
- pyinfra/facts/apt.py +15 -7
- pyinfra/facts/brew.py +28 -13
- pyinfra/facts/bsdinit.py +9 -6
- pyinfra/facts/cargo.py +6 -3
- pyinfra/facts/choco.py +8 -4
- pyinfra/facts/deb.py +21 -9
- pyinfra/facts/dnf.py +11 -6
- pyinfra/facts/docker.py +30 -5
- pyinfra/facts/files.py +49 -33
- pyinfra/facts/gem.py +7 -2
- pyinfra/facts/git.py +14 -21
- pyinfra/facts/gpg.py +4 -1
- pyinfra/facts/hardware.py +186 -138
- pyinfra/facts/launchd.py +7 -2
- pyinfra/facts/lxd.py +8 -2
- pyinfra/facts/mysql.py +19 -12
- pyinfra/facts/npm.py +3 -1
- pyinfra/facts/openrc.py +8 -2
- pyinfra/facts/pacman.py +13 -5
- pyinfra/facts/pip.py +2 -0
- pyinfra/facts/pkg.py +5 -1
- pyinfra/facts/pkgin.py +7 -2
- pyinfra/facts/postgres.py +170 -0
- pyinfra/facts/postgresql.py +5 -162
- pyinfra/facts/rpm.py +21 -15
- pyinfra/facts/runit.py +70 -0
- pyinfra/facts/selinux.py +12 -4
- pyinfra/facts/server.py +240 -82
- pyinfra/facts/snap.py +8 -2
- pyinfra/facts/systemd.py +37 -13
- pyinfra/facts/sysvinit.py +7 -4
- pyinfra/facts/upstart.py +7 -2
- pyinfra/facts/util/packaging.py +3 -2
- pyinfra/facts/vzctl.py +8 -4
- pyinfra/facts/xbps.py +7 -2
- pyinfra/facts/yum.py +10 -5
- pyinfra/facts/zypper.py +9 -4
- pyinfra/operations/apk.py +5 -3
- pyinfra/operations/apt.py +28 -25
- pyinfra/operations/brew.py +60 -29
- pyinfra/operations/bsdinit.py +6 -4
- pyinfra/operations/cargo.py +3 -1
- pyinfra/operations/choco.py +3 -1
- pyinfra/operations/dnf.py +16 -20
- pyinfra/operations/docker.py +339 -0
- pyinfra/operations/files.py +187 -168
- pyinfra/operations/gem.py +3 -1
- pyinfra/operations/git.py +23 -25
- pyinfra/operations/iptables.py +33 -25
- pyinfra/operations/launchd.py +5 -6
- pyinfra/operations/lxd.py +7 -4
- pyinfra/operations/mysql.py +59 -55
- pyinfra/operations/npm.py +8 -1
- pyinfra/operations/openrc.py +5 -3
- pyinfra/operations/pacman.py +6 -7
- pyinfra/operations/pip.py +19 -12
- pyinfra/operations/pkg.py +3 -1
- pyinfra/operations/pkgin.py +5 -3
- pyinfra/operations/postgres.py +349 -0
- pyinfra/operations/postgresql.py +18 -335
- pyinfra/operations/puppet.py +3 -1
- pyinfra/operations/python.py +8 -19
- pyinfra/operations/runit.py +182 -0
- pyinfra/operations/selinux.py +47 -29
- pyinfra/operations/server.py +138 -67
- pyinfra/operations/snap.py +3 -1
- pyinfra/operations/ssh.py +18 -16
- pyinfra/operations/systemd.py +18 -12
- pyinfra/operations/sysvinit.py +7 -5
- pyinfra/operations/upstart.py +7 -5
- 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 +54 -38
- pyinfra/operations/util/service.py +39 -47
- pyinfra/operations/vzctl.py +12 -10
- pyinfra/operations/xbps.py +5 -3
- pyinfra/operations/yum.py +15 -19
- pyinfra/operations/zypper.py +9 -10
- pyinfra/version.py +5 -2
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/METADATA +51 -58
- pyinfra-3.0.1.dist-info/RECORD +168 -0
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/WHEEL +1 -1
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/entry_points.txt +0 -3
- pyinfra_cli/__main__.py +4 -3
- pyinfra_cli/commands.py +3 -2
- pyinfra_cli/exceptions.py +75 -43
- pyinfra_cli/inventory.py +52 -31
- pyinfra_cli/log.py +10 -2
- pyinfra_cli/main.py +88 -65
- pyinfra_cli/prints.py +37 -109
- pyinfra_cli/util.py +15 -10
- tests/test_api/test_api.py +2 -0
- tests/test_api/test_api_arguments.py +9 -9
- tests/test_api/test_api_deploys.py +15 -19
- tests/test_api/test_api_facts.py +4 -5
- tests/test_api/test_api_operations.py +18 -20
- tests/test_api/test_api_util.py +41 -2
- tests/test_cli/test_cli.py +14 -50
- tests/test_cli/test_cli_deploy.py +10 -12
- tests/test_cli/test_cli_exceptions.py +50 -19
- tests/test_cli/test_cli_inventory.py +66 -0
- tests/test_cli/util.py +1 -1
- tests/test_connectors/test_dockerssh.py +11 -8
- tests/test_connectors/test_ssh.py +88 -23
- tests/test_connectors/test_sshuserclient.py +1 -1
- tests/test_connectors/test_terraform.py +11 -8
- tests/test_connectors/test_vagrant.py +6 -6
- pyinfra/connectors/ansible.py +0 -175
- pyinfra/connectors/mech.py +0 -189
- pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
- pyinfra/connectors/winrm.py +0 -312
- 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 -538
- pyinfra-3.0.dev0.dist-info/RECORD +0 -170
- tests/test_connectors/test_ansible.py +0 -64
- tests/test_connectors/test_mech.py +0 -126
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/LICENSE.md +0 -0
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/top_level.txt +0 -0
pyinfra/api/__init__.py
CHANGED
|
@@ -11,6 +11,9 @@ from .config import Config # noqa: F401 # pragma: no cover
|
|
|
11
11
|
from .deploy import deploy # noqa: F401 # pragma: no cover
|
|
12
12
|
from .exceptions import DeployError # noqa: F401 # pragma: no cover
|
|
13
13
|
from .exceptions import ( # noqa: F401
|
|
14
|
+
FactError,
|
|
15
|
+
FactTypeError,
|
|
16
|
+
FactValueError,
|
|
14
17
|
InventoryError,
|
|
15
18
|
OperationError,
|
|
16
19
|
OperationTypeError,
|
pyinfra/api/arguments.py
CHANGED
|
@@ -1,31 +1,42 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import (
|
|
4
|
+
TYPE_CHECKING,
|
|
5
|
+
Any,
|
|
6
|
+
Callable,
|
|
7
|
+
Generic,
|
|
8
|
+
Iterable,
|
|
9
|
+
List,
|
|
10
|
+
Mapping,
|
|
11
|
+
Optional,
|
|
12
|
+
Type,
|
|
13
|
+
TypeVar,
|
|
14
|
+
Union,
|
|
15
|
+
cast,
|
|
16
|
+
get_type_hints,
|
|
17
|
+
)
|
|
4
18
|
|
|
5
19
|
from typing_extensions import TypedDict
|
|
6
20
|
|
|
7
21
|
from pyinfra import context
|
|
22
|
+
from pyinfra.api.exceptions import ArgumentTypeError
|
|
8
23
|
from pyinfra.api.state import State
|
|
9
|
-
|
|
10
|
-
from .util import memoize
|
|
24
|
+
from pyinfra.api.util import raise_if_bad_type
|
|
11
25
|
|
|
12
26
|
if TYPE_CHECKING:
|
|
13
27
|
from pyinfra.api.config import Config
|
|
14
28
|
from pyinfra.api.host import Host
|
|
15
29
|
|
|
16
|
-
|
|
17
30
|
T = TypeVar("T")
|
|
18
|
-
|
|
19
|
-
|
|
20
31
|
default_sentinel = object()
|
|
21
32
|
|
|
22
33
|
|
|
23
|
-
class ArgumentMeta:
|
|
34
|
+
class ArgumentMeta(Generic[T]):
|
|
24
35
|
description: str
|
|
25
36
|
default: Callable[["Config"], T]
|
|
26
|
-
handler: Callable[["Config", T], T]
|
|
37
|
+
handler: Optional[Callable[["Config", T], T]]
|
|
27
38
|
|
|
28
|
-
def __init__(self, description, default, handler=
|
|
39
|
+
def __init__(self, description, default, handler=None) -> None:
|
|
29
40
|
self.description = description
|
|
30
41
|
self.default = default
|
|
31
42
|
self.handler = handler
|
|
@@ -36,12 +47,14 @@ class ArgumentMeta:
|
|
|
36
47
|
# API to read/write external systems.
|
|
37
48
|
|
|
38
49
|
|
|
50
|
+
# Note: ConnectorArguments is specifically not total as it's used to type many
|
|
51
|
+
# functions via Unpack and we don't want to specify every kwarg.
|
|
39
52
|
class ConnectorArguments(TypedDict, total=False):
|
|
40
53
|
# Auth arguments
|
|
41
54
|
_sudo: bool
|
|
42
55
|
_sudo_user: str
|
|
43
56
|
_use_sudo_login: bool
|
|
44
|
-
|
|
57
|
+
_sudo_password: str
|
|
45
58
|
_preserve_sudo_env: bool
|
|
46
59
|
_su_user: str
|
|
47
60
|
_use_su_login: bool
|
|
@@ -59,7 +72,7 @@ class ConnectorArguments(TypedDict, total=False):
|
|
|
59
72
|
_success_exit_codes: Iterable[int]
|
|
60
73
|
_timeout: int
|
|
61
74
|
_get_pty: bool
|
|
62
|
-
_stdin: Union[str,
|
|
75
|
+
_stdin: Union[str, Iterable[str]]
|
|
63
76
|
|
|
64
77
|
|
|
65
78
|
def generate_env(config: "Config", value: dict) -> dict:
|
|
@@ -70,82 +83,82 @@ def generate_env(config: "Config", value: dict) -> dict:
|
|
|
70
83
|
|
|
71
84
|
auth_argument_meta: dict[str, ArgumentMeta] = {
|
|
72
85
|
"_sudo": ArgumentMeta(
|
|
73
|
-
"Execute/apply any changes with
|
|
86
|
+
"Execute/apply any changes with sudo.",
|
|
74
87
|
default=lambda config: config.SUDO,
|
|
75
88
|
),
|
|
76
89
|
"_sudo_user": ArgumentMeta(
|
|
77
|
-
"Execute/apply any changes with
|
|
90
|
+
"Execute/apply any changes with sudo as a non-root user.",
|
|
78
91
|
default=lambda config: config.SUDO_USER,
|
|
79
92
|
),
|
|
80
93
|
"_use_sudo_login": ArgumentMeta(
|
|
81
|
-
"Execute
|
|
94
|
+
"Execute sudo with a login shell.",
|
|
82
95
|
default=lambda config: config.USE_SUDO_LOGIN,
|
|
83
96
|
),
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
default=lambda config: config.
|
|
97
|
+
"_sudo_password": ArgumentMeta(
|
|
98
|
+
"Password to sudo with. If needed and not specified pyinfra will prompt for it.",
|
|
99
|
+
default=lambda config: config.SUDO_PASSWORD,
|
|
87
100
|
),
|
|
88
101
|
"_preserve_sudo_env": ArgumentMeta(
|
|
89
|
-
"Preserve the shell environment when using
|
|
102
|
+
"Preserve the shell environment of the connecting user when using sudo.",
|
|
90
103
|
default=lambda config: config.PRESERVE_SUDO_ENV,
|
|
91
104
|
),
|
|
92
105
|
"_su_user": ArgumentMeta(
|
|
93
|
-
"Execute/apply any changes with this user using
|
|
106
|
+
"Execute/apply any changes with this user using su.",
|
|
94
107
|
default=lambda config: config.SU_USER,
|
|
95
108
|
),
|
|
96
109
|
"_use_su_login": ArgumentMeta(
|
|
97
|
-
"Execute
|
|
110
|
+
"Execute su with a login shell.",
|
|
98
111
|
default=lambda config: config.USE_SU_LOGIN,
|
|
99
112
|
),
|
|
100
113
|
"_preserve_su_env": ArgumentMeta(
|
|
101
|
-
"Preserve the shell environment when using
|
|
114
|
+
"Preserve the shell environment of the connecting user when using su.",
|
|
102
115
|
default=lambda config: config.PRESERVE_SU_ENV,
|
|
103
116
|
),
|
|
104
117
|
"_su_shell": ArgumentMeta(
|
|
105
|
-
"Use this shell (instead of user login shell) when using ``
|
|
106
|
-
"Only available under Linux, for use when using `su` with a user that "
|
|
107
|
-
"has nologin/similar as their login shell.",
|
|
118
|
+
"Use this shell (instead of user login shell) when using ``_su``). "
|
|
119
|
+
+ "Only available under Linux, for use when using `su` with a user that "
|
|
120
|
+
+ "has nologin/similar as their login shell.",
|
|
108
121
|
default=lambda config: config.SU_SHELL,
|
|
109
122
|
),
|
|
110
123
|
"_doas": ArgumentMeta(
|
|
111
|
-
"Execute/apply any changes with
|
|
124
|
+
"Execute/apply any changes with doas.",
|
|
112
125
|
default=lambda config: config.DOAS,
|
|
113
126
|
),
|
|
114
127
|
"_doas_user": ArgumentMeta(
|
|
115
|
-
"Execute/apply any changes with
|
|
128
|
+
"Execute/apply any changes with doas as a non-root user.",
|
|
116
129
|
default=lambda config: config.DOAS_USER,
|
|
117
130
|
),
|
|
118
131
|
}
|
|
119
132
|
|
|
120
133
|
shell_argument_meta: dict[str, ArgumentMeta] = {
|
|
121
134
|
"_shell_executable": ArgumentMeta(
|
|
122
|
-
"The shell to use
|
|
135
|
+
"The shell executable to use for executing commands.",
|
|
123
136
|
default=lambda config: config.SHELL,
|
|
124
137
|
),
|
|
125
138
|
"_chdir": ArgumentMeta(
|
|
126
139
|
"Directory to switch to before executing the command.",
|
|
127
|
-
default=lambda
|
|
140
|
+
default=lambda _: "",
|
|
128
141
|
),
|
|
129
142
|
"_env": ArgumentMeta(
|
|
130
143
|
"Dictionary of environment variables to set.",
|
|
131
|
-
default=lambda
|
|
144
|
+
default=lambda _: {},
|
|
132
145
|
handler=generate_env,
|
|
133
146
|
),
|
|
134
147
|
"_success_exit_codes": ArgumentMeta(
|
|
135
148
|
"List of exit codes to consider a success.",
|
|
136
|
-
default=lambda
|
|
149
|
+
default=lambda _: [0],
|
|
137
150
|
),
|
|
138
151
|
"_timeout": ArgumentMeta(
|
|
139
152
|
"Timeout for *each* command executed during the operation.",
|
|
140
|
-
default=lambda
|
|
153
|
+
default=lambda _: None,
|
|
141
154
|
),
|
|
142
155
|
"_get_pty": ArgumentMeta(
|
|
143
156
|
"Whether to get a pseudoTTY when executing any commands.",
|
|
144
|
-
default=lambda
|
|
157
|
+
default=lambda _: False,
|
|
145
158
|
),
|
|
146
159
|
"_stdin": ArgumentMeta(
|
|
147
160
|
"String or buffer to send to the stdin of any commands.",
|
|
148
|
-
default=lambda
|
|
161
|
+
default=lambda _: None,
|
|
149
162
|
),
|
|
150
163
|
}
|
|
151
164
|
|
|
@@ -154,21 +167,18 @@ shell_argument_meta: dict[str, ArgumentMeta] = {
|
|
|
154
167
|
# These provide/extend additional operation metadata
|
|
155
168
|
|
|
156
169
|
|
|
157
|
-
class MetaArguments(TypedDict
|
|
170
|
+
class MetaArguments(TypedDict):
|
|
158
171
|
name: str
|
|
159
172
|
_ignore_errors: bool
|
|
160
173
|
_continue_on_error: bool
|
|
161
|
-
|
|
162
|
-
_postcondition: str
|
|
163
|
-
_on_success: Callable[[State, "Host", str], None]
|
|
164
|
-
_on_error: Callable[[State, "Host", str], None]
|
|
174
|
+
_if: Union[List[Callable[[], bool]], Callable[[], bool], None]
|
|
165
175
|
|
|
166
176
|
|
|
167
177
|
meta_argument_meta: dict[str, ArgumentMeta] = {
|
|
168
178
|
# NOTE: name is the only non-_-prefixed argument
|
|
169
179
|
"name": ArgumentMeta(
|
|
170
180
|
"Name of the operation.",
|
|
171
|
-
default=lambda
|
|
181
|
+
default=lambda _: None,
|
|
172
182
|
),
|
|
173
183
|
"_ignore_errors": ArgumentMeta(
|
|
174
184
|
"Ignore errors when executing the operation.",
|
|
@@ -179,24 +189,11 @@ meta_argument_meta: dict[str, ArgumentMeta] = {
|
|
|
179
189
|
"Continue executing operation commands after error. "
|
|
180
190
|
"Only applies when ``_ignore_errors`` is true."
|
|
181
191
|
),
|
|
182
|
-
default=lambda
|
|
183
|
-
),
|
|
184
|
-
"_precondition": ArgumentMeta(
|
|
185
|
-
"Command to execute & check before the operation commands begin.",
|
|
186
|
-
default=lambda config: None,
|
|
192
|
+
default=lambda _: False,
|
|
187
193
|
),
|
|
188
|
-
"
|
|
189
|
-
"
|
|
190
|
-
default=lambda
|
|
191
|
-
),
|
|
192
|
-
# Lambda on the next two are to workaround a circular import
|
|
193
|
-
"_on_success": ArgumentMeta(
|
|
194
|
-
"Callback function to execute on success.",
|
|
195
|
-
default=lambda config: None,
|
|
196
|
-
),
|
|
197
|
-
"_on_error": ArgumentMeta(
|
|
198
|
-
"Callback function to execute on error.",
|
|
199
|
-
default=lambda config: None,
|
|
194
|
+
"_if": ArgumentMeta(
|
|
195
|
+
"Only run this operation if these functions return True",
|
|
196
|
+
default=lambda _: [],
|
|
200
197
|
),
|
|
201
198
|
}
|
|
202
199
|
|
|
@@ -206,7 +203,7 @@ meta_argument_meta: dict[str, ArgumentMeta] = {
|
|
|
206
203
|
# over every target host for the same operation.
|
|
207
204
|
|
|
208
205
|
|
|
209
|
-
class ExecutionArguments(TypedDict
|
|
206
|
+
class ExecutionArguments(TypedDict):
|
|
210
207
|
_parallel: int
|
|
211
208
|
_run_once: bool
|
|
212
209
|
_serial: bool
|
|
@@ -219,11 +216,11 @@ execution_argument_meta: dict[str, ArgumentMeta] = {
|
|
|
219
216
|
),
|
|
220
217
|
"_run_once": ArgumentMeta(
|
|
221
218
|
"Only execute this operation once, on the first host to see it.",
|
|
222
|
-
default=lambda
|
|
219
|
+
default=lambda _: False,
|
|
223
220
|
),
|
|
224
221
|
"_serial": ArgumentMeta(
|
|
225
222
|
"Run this operation host by host, rather than in parallel.",
|
|
226
|
-
default=lambda
|
|
223
|
+
default=lambda _: False,
|
|
227
224
|
),
|
|
228
225
|
}
|
|
229
226
|
|
|
@@ -232,6 +229,11 @@ class AllArguments(ConnectorArguments, MetaArguments, ExecutionArguments):
|
|
|
232
229
|
pass
|
|
233
230
|
|
|
234
231
|
|
|
232
|
+
def all_global_arguments() -> List[tuple[str, Type]]:
|
|
233
|
+
"""Return all global arguments and their types."""
|
|
234
|
+
return list(get_type_hints(AllArguments).items())
|
|
235
|
+
|
|
236
|
+
|
|
235
237
|
all_argument_meta: dict[str, ArgumentMeta] = {
|
|
236
238
|
**auth_argument_meta,
|
|
237
239
|
**shell_argument_meta,
|
|
@@ -239,29 +241,47 @@ all_argument_meta: dict[str, ArgumentMeta] = {
|
|
|
239
241
|
**execution_argument_meta,
|
|
240
242
|
}
|
|
241
243
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
"
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
244
|
+
EXECUTION_KWARG_KEYS = list(ExecutionArguments.__annotations__.keys())
|
|
245
|
+
CONNECTOR_ARGUMENT_KEYS = list(ConnectorArguments.__annotations__.keys())
|
|
246
|
+
|
|
247
|
+
__argument_docs__ = {
|
|
248
|
+
"Privilege & user escalation": (
|
|
249
|
+
auth_argument_meta,
|
|
250
|
+
"""
|
|
251
|
+
.. code:: python
|
|
252
|
+
|
|
253
|
+
# Execute a command with sudo
|
|
254
|
+
server.user(
|
|
255
|
+
name="Create pyinfra user using sudo",
|
|
256
|
+
user="pyinfra",
|
|
257
|
+
_sudo=True,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Execute a command with a specific sudo password
|
|
261
|
+
server.user(
|
|
262
|
+
name="Create pyinfra user using sudo",
|
|
263
|
+
user="pyinfra",
|
|
264
|
+
_sudo=True,
|
|
265
|
+
_sudo_password="my-secret-password",
|
|
266
|
+
)
|
|
267
|
+
""",
|
|
252
268
|
),
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
269
|
+
"Shell control & features": (
|
|
270
|
+
shell_argument_meta,
|
|
271
|
+
"""
|
|
272
|
+
.. code:: python
|
|
273
|
+
|
|
274
|
+
# Execute from a specific directory
|
|
275
|
+
server.shell(
|
|
276
|
+
name="Bootstrap nginx params",
|
|
277
|
+
commands=["openssl dhparam -out dhparam.pem 4096"],
|
|
278
|
+
_chdir="/etc/ssl/certs",
|
|
279
|
+
)
|
|
280
|
+
""",
|
|
281
|
+
),
|
|
282
|
+
"Operation meta & callbacks": (meta_argument_meta, ""),
|
|
283
|
+
"Execution strategy": (execution_argument_meta, ""),
|
|
284
|
+
}
|
|
265
285
|
|
|
266
286
|
|
|
267
287
|
def pop_global_arguments(
|
|
@@ -269,7 +289,7 @@ def pop_global_arguments(
|
|
|
269
289
|
state: Optional["State"] = None,
|
|
270
290
|
host: Optional["Host"] = None,
|
|
271
291
|
keys_to_check=None,
|
|
272
|
-
) ->
|
|
292
|
+
) -> tuple[AllArguments, list[str]]:
|
|
273
293
|
"""
|
|
274
294
|
Pop and return operation global keyword arguments, in preferred order:
|
|
275
295
|
|
|
@@ -277,16 +297,6 @@ def pop_global_arguments(
|
|
|
277
297
|
+ From any current @deploy context (deploy kwargs)
|
|
278
298
|
+ From the host data variables
|
|
279
299
|
+ From the config variables
|
|
280
|
-
|
|
281
|
-
Note this function is only called directly in the @operation & @deploy decorator
|
|
282
|
-
wrappers which the user should pass global arguments prefixed "_". This is to
|
|
283
|
-
avoid any clashes with operation and deploy functions both internal and third
|
|
284
|
-
party.
|
|
285
|
-
|
|
286
|
-
This is a bit strange because internally pyinfra uses non-_-prefixed arguments,
|
|
287
|
-
and this function is responsible for the translation between the two.
|
|
288
|
-
|
|
289
|
-
TODO: is this weird-ness acceptable? Is it worth updating internal use to _prefix?
|
|
290
300
|
"""
|
|
291
301
|
|
|
292
302
|
state = state or context.state
|
|
@@ -296,12 +306,12 @@ def pop_global_arguments(
|
|
|
296
306
|
if context.ctx_config.isset():
|
|
297
307
|
config = context.config
|
|
298
308
|
|
|
299
|
-
meta_kwargs = host.current_deploy_kwargs or {}
|
|
309
|
+
meta_kwargs: dict[str, Any] = host.current_deploy_kwargs or {} # type: ignore[assignment]
|
|
300
310
|
|
|
301
|
-
arguments:
|
|
311
|
+
arguments: dict[str, Any] = {}
|
|
302
312
|
found_keys: list[str] = []
|
|
303
313
|
|
|
304
|
-
for key, type_ in
|
|
314
|
+
for key, type_ in all_global_arguments():
|
|
305
315
|
if keys_to_check and key not in keys_to_check:
|
|
306
316
|
continue
|
|
307
317
|
|
|
@@ -319,9 +329,17 @@ def pop_global_arguments(
|
|
|
319
329
|
else:
|
|
320
330
|
value = meta_kwargs.get(key, default)
|
|
321
331
|
|
|
322
|
-
if handler
|
|
332
|
+
if handler:
|
|
323
333
|
value = handler(config, value)
|
|
324
334
|
|
|
335
|
+
if value != default:
|
|
336
|
+
raise_if_bad_type(
|
|
337
|
+
value,
|
|
338
|
+
type_,
|
|
339
|
+
ArgumentTypeError,
|
|
340
|
+
f"Invalid argument `{key}`:",
|
|
341
|
+
)
|
|
342
|
+
|
|
325
343
|
# TODO: why is type failing here?
|
|
326
344
|
arguments[key] = value # type: ignore
|
|
327
|
-
return arguments, found_keys
|
|
345
|
+
return cast(AllArguments, arguments), found_keys
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import (
|
|
4
|
+
TYPE_CHECKING,
|
|
5
|
+
Callable,
|
|
6
|
+
Generator,
|
|
7
|
+
Generic,
|
|
8
|
+
Iterable,
|
|
9
|
+
List,
|
|
10
|
+
Mapping,
|
|
11
|
+
Optional,
|
|
12
|
+
Union,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from typing_extensions import ParamSpec, Protocol
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from pyinfra.api.operation import OperationMeta
|
|
19
|
+
|
|
20
|
+
P = ParamSpec("P")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Unfortunately we have to re-type out all of the global arguments here because
|
|
24
|
+
# Python typing doesn't (yet) support merging kwargs. This acts as the operation
|
|
25
|
+
# decorator output function which merges actual operation args/kwargs in paramspec
|
|
26
|
+
# with the global arguments available in all operations.
|
|
27
|
+
# The nature of "global arguments" is somewhat opposed to static typing as it's
|
|
28
|
+
# indirect and somewhat magic, but we are where we are.
|
|
29
|
+
class PyinfraOperation(Generic[P], Protocol):
|
|
30
|
+
_inner: Callable[P, Generator]
|
|
31
|
+
|
|
32
|
+
def __call__(
|
|
33
|
+
self,
|
|
34
|
+
#
|
|
35
|
+
# op args
|
|
36
|
+
# needs to be first
|
|
37
|
+
#
|
|
38
|
+
*args: P.args,
|
|
39
|
+
#
|
|
40
|
+
# ConnectorArguments
|
|
41
|
+
#
|
|
42
|
+
# Auth
|
|
43
|
+
_sudo: bool = False,
|
|
44
|
+
_sudo_user: Optional[str] = None,
|
|
45
|
+
_use_sudo_login: bool = False,
|
|
46
|
+
_sudo_password: Optional[str] = None,
|
|
47
|
+
_preserve_sudo_env: bool = False,
|
|
48
|
+
_su_user: Optional[str] = None,
|
|
49
|
+
_use_su_login: bool = False,
|
|
50
|
+
_preserve_su_env: bool = False,
|
|
51
|
+
_su_shell: Optional[str] = None,
|
|
52
|
+
_doas: bool = False,
|
|
53
|
+
_doas_user: Optional[str] = None,
|
|
54
|
+
# Shell arguments
|
|
55
|
+
_shell_executable: Optional[str] = None,
|
|
56
|
+
_chdir: Optional[str] = None,
|
|
57
|
+
_env: Optional[Mapping[str, str]] = None,
|
|
58
|
+
# Connector control
|
|
59
|
+
_success_exit_codes: Iterable[int] = (0,),
|
|
60
|
+
_timeout: Optional[int] = None,
|
|
61
|
+
_get_pty: bool = False,
|
|
62
|
+
_stdin: Union[None, str, list[str], tuple[str, ...]] = None,
|
|
63
|
+
#
|
|
64
|
+
# MetaArguments
|
|
65
|
+
#
|
|
66
|
+
name: Optional[str] = None,
|
|
67
|
+
_ignore_errors: bool = False,
|
|
68
|
+
_continue_on_error: bool = False,
|
|
69
|
+
_if: Union[List[Callable[[], bool]], Callable[[], bool], None] = None,
|
|
70
|
+
#
|
|
71
|
+
# ExecutionArguments
|
|
72
|
+
#
|
|
73
|
+
_parallel: Optional[int] = None,
|
|
74
|
+
_run_once: bool = False,
|
|
75
|
+
_serial: bool = False,
|
|
76
|
+
#
|
|
77
|
+
# op kwargs
|
|
78
|
+
#
|
|
79
|
+
**kwargs: P.kwargs,
|
|
80
|
+
) -> "OperationMeta": ...
|
pyinfra/api/command.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import shlex
|
|
2
4
|
from inspect import getfullargspec
|
|
3
5
|
from string import Formatter
|
|
4
|
-
from typing import TYPE_CHECKING, Callable, Union
|
|
6
|
+
from typing import IO, TYPE_CHECKING, Callable, Union
|
|
5
7
|
|
|
6
8
|
import gevent
|
|
7
9
|
from typing_extensions import Unpack
|
|
@@ -143,7 +145,7 @@ class StringCommand(PyinfraCommand):
|
|
|
143
145
|
class FileUploadCommand(PyinfraCommand):
|
|
144
146
|
def __init__(
|
|
145
147
|
self,
|
|
146
|
-
src: str,
|
|
148
|
+
src: str | IO,
|
|
147
149
|
dest: str,
|
|
148
150
|
remote_temp_filename=None,
|
|
149
151
|
**kwargs: Unpack[ConnectorArguments],
|
|
@@ -173,7 +175,7 @@ class FileDownloadCommand(PyinfraCommand):
|
|
|
173
175
|
def __init__(
|
|
174
176
|
self,
|
|
175
177
|
src: str,
|
|
176
|
-
dest: str,
|
|
178
|
+
dest: str | IO,
|
|
177
179
|
remote_temp_filename=None,
|
|
178
180
|
**kwargs: Unpack[ConnectorArguments],
|
|
179
181
|
):
|