pyinfra 3.2__py2.py3-none-any.whl → 3.3.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/arguments_typed.py +4 -5
- pyinfra/api/command.py +22 -3
- pyinfra/api/config.py +5 -2
- pyinfra/api/facts.py +3 -0
- pyinfra/api/host.py +10 -4
- pyinfra/api/operation.py +2 -1
- pyinfra/api/state.py +1 -1
- pyinfra/connectors/base.py +34 -8
- pyinfra/connectors/chroot.py +7 -2
- pyinfra/connectors/docker.py +7 -2
- pyinfra/connectors/dockerssh.py +7 -2
- pyinfra/connectors/local.py +7 -2
- pyinfra/connectors/ssh.py +9 -2
- pyinfra/connectors/sshuserclient/client.py +18 -2
- pyinfra/connectors/sshuserclient/config.py +2 -0
- pyinfra/connectors/terraform.py +1 -1
- pyinfra/connectors/util.py +13 -9
- pyinfra/context.py +9 -2
- pyinfra/facts/apk.py +5 -0
- pyinfra/facts/apt.py +9 -1
- pyinfra/facts/brew.py +13 -0
- pyinfra/facts/bsdinit.py +3 -0
- pyinfra/facts/cargo.py +5 -0
- pyinfra/facts/choco.py +6 -0
- pyinfra/facts/crontab.py +6 -1
- pyinfra/facts/deb.py +10 -0
- pyinfra/facts/dnf.py +5 -0
- pyinfra/facts/docker.py +10 -0
- pyinfra/facts/efibootmgr.py +5 -0
- pyinfra/facts/files.py +19 -1
- pyinfra/facts/flatpak.py +7 -0
- pyinfra/facts/freebsd.py +75 -0
- pyinfra/facts/gem.py +5 -0
- pyinfra/facts/git.py +9 -0
- pyinfra/facts/gpg.py +7 -0
- pyinfra/facts/hardware.py +13 -0
- pyinfra/facts/iptables.py +9 -1
- pyinfra/facts/launchd.py +5 -0
- pyinfra/facts/lxd.py +5 -0
- pyinfra/facts/mysql.py +8 -0
- pyinfra/facts/npm.py +5 -0
- pyinfra/facts/openrc.py +8 -0
- pyinfra/facts/opkg.py +12 -0
- pyinfra/facts/pacman.py +9 -1
- pyinfra/facts/pip.py +5 -0
- pyinfra/facts/pipx.py +8 -0
- pyinfra/facts/pkg.py +4 -0
- pyinfra/facts/pkgin.py +5 -0
- pyinfra/facts/podman.py +7 -0
- pyinfra/facts/postgres.py +8 -2
- pyinfra/facts/rpm.py +11 -0
- pyinfra/facts/runit.py +7 -0
- pyinfra/facts/selinux.py +16 -0
- pyinfra/facts/server.py +49 -3
- pyinfra/facts/snap.py +7 -0
- pyinfra/facts/systemd.py +5 -0
- pyinfra/facts/sysvinit.py +4 -0
- pyinfra/facts/upstart.py +5 -0
- pyinfra/facts/util/__init__.py +4 -1
- pyinfra/facts/vzctl.py +5 -0
- pyinfra/facts/xbps.py +6 -1
- pyinfra/facts/yum.py +5 -0
- pyinfra/facts/zfs.py +19 -2
- pyinfra/facts/zypper.py +5 -0
- pyinfra/operations/apt.py +10 -3
- pyinfra/operations/docker.py +48 -44
- pyinfra/operations/files.py +47 -1
- pyinfra/operations/freebsd/__init__.py +12 -0
- pyinfra/operations/freebsd/freebsd_update.py +70 -0
- pyinfra/operations/freebsd/pkg.py +219 -0
- pyinfra/operations/freebsd/service.py +116 -0
- pyinfra/operations/freebsd/sysrc.py +92 -0
- pyinfra/operations/opkg.py +5 -5
- pyinfra/operations/postgres.py +99 -16
- pyinfra/operations/server.py +6 -4
- pyinfra/operations/util/docker.py +44 -22
- {pyinfra-3.2.dist-info → pyinfra-3.3.1.dist-info}/LICENSE.md +1 -1
- {pyinfra-3.2.dist-info → pyinfra-3.3.1.dist-info}/METADATA +25 -24
- {pyinfra-3.2.dist-info → pyinfra-3.3.1.dist-info}/RECORD +89 -83
- pyinfra_cli/exceptions.py +5 -0
- pyinfra_cli/log.py +3 -0
- pyinfra_cli/main.py +9 -8
- pyinfra_cli/prints.py +1 -1
- pyinfra_cli/virtualenv.py +1 -1
- tests/test_connectors/test_ssh.py +302 -182
- tests/test_connectors/test_sshuserclient.py +10 -5
- {pyinfra-3.2.dist-info → pyinfra-3.3.1.dist-info}/WHEEL +0 -0
- {pyinfra-3.2.dist-info → pyinfra-3.3.1.dist-info}/entry_points.txt +0 -0
- {pyinfra-3.2.dist-info → pyinfra-3.3.1.dist-info}/top_level.txt +0 -0
|
@@ -15,6 +15,7 @@ from paramiko import (
|
|
|
15
15
|
)
|
|
16
16
|
from paramiko.agent import AgentRequestHandler
|
|
17
17
|
from paramiko.hostkeys import HostKeyEntry
|
|
18
|
+
from typing_extensions import override
|
|
18
19
|
|
|
19
20
|
from pyinfra import logger
|
|
20
21
|
from pyinfra.api.util import memoize
|
|
@@ -25,6 +26,7 @@ HOST_KEYS_LOCK = BoundedSemaphore()
|
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
class StrictPolicy(MissingHostKeyPolicy):
|
|
29
|
+
@override
|
|
28
30
|
def missing_host_key(self, client, hostname, key):
|
|
29
31
|
logger.error("No host key for {0} found in known_hosts".format(hostname))
|
|
30
32
|
raise SSHException(
|
|
@@ -55,6 +57,7 @@ def append_hostkey(client, hostname, key):
|
|
|
55
57
|
|
|
56
58
|
|
|
57
59
|
class AcceptNewPolicy(MissingHostKeyPolicy):
|
|
60
|
+
@override
|
|
58
61
|
def missing_host_key(self, client, hostname, key):
|
|
59
62
|
logger.warning(
|
|
60
63
|
(
|
|
@@ -68,6 +71,7 @@ class AcceptNewPolicy(MissingHostKeyPolicy):
|
|
|
68
71
|
|
|
69
72
|
|
|
70
73
|
class AskPolicy(MissingHostKeyPolicy):
|
|
74
|
+
@override
|
|
71
75
|
def missing_host_key(self, client, hostname, key):
|
|
72
76
|
should_continue = input(
|
|
73
77
|
"No host key for {0} found in known_hosts, do you want to continue [y/n] ".format(
|
|
@@ -84,6 +88,7 @@ class AskPolicy(MissingHostKeyPolicy):
|
|
|
84
88
|
|
|
85
89
|
|
|
86
90
|
class WarningPolicy(MissingHostKeyPolicy):
|
|
91
|
+
@override
|
|
87
92
|
def missing_host_key(self, client, hostname, key):
|
|
88
93
|
logger.warning("No host key for {0} found in known_hosts".format(hostname))
|
|
89
94
|
|
|
@@ -136,6 +141,7 @@ class SSHClient(ParamikoClient):
|
|
|
136
141
|
original idea at http://bitprophet.org/blog/2012/11/05/gateway-solutions/.
|
|
137
142
|
"""
|
|
138
143
|
|
|
144
|
+
@override
|
|
139
145
|
def connect( # type: ignore[override]
|
|
140
146
|
self,
|
|
141
147
|
hostname,
|
|
@@ -152,6 +158,7 @@ class SSHClient(ParamikoClient):
|
|
|
152
158
|
forward_agent,
|
|
153
159
|
missing_host_key_policy,
|
|
154
160
|
host_keys_file,
|
|
161
|
+
keep_alive,
|
|
155
162
|
) = self.parse_config(
|
|
156
163
|
hostname,
|
|
157
164
|
kwargs,
|
|
@@ -177,6 +184,11 @@ class SSHClient(ParamikoClient):
|
|
|
177
184
|
if _pyinfra_ssh_forward_agent is not None:
|
|
178
185
|
forward_agent = _pyinfra_ssh_forward_agent
|
|
179
186
|
|
|
187
|
+
if keep_alive:
|
|
188
|
+
transport = self.get_transport()
|
|
189
|
+
assert transport is not None, "No transport"
|
|
190
|
+
transport.set_keepalive(keep_alive)
|
|
191
|
+
|
|
180
192
|
if forward_agent:
|
|
181
193
|
transport = self.get_transport()
|
|
182
194
|
assert transport is not None, "No transport"
|
|
@@ -202,13 +214,14 @@ class SSHClient(ParamikoClient):
|
|
|
202
214
|
cfg: dict = {"port": 22}
|
|
203
215
|
cfg.update(initial_cfg or {})
|
|
204
216
|
|
|
217
|
+
keep_alive = 0
|
|
205
218
|
forward_agent = False
|
|
206
219
|
missing_host_key_policy = get_missing_host_key_policy(strict_host_key_checking)
|
|
207
220
|
host_keys_file = path.expanduser("~/.ssh/known_hosts") # OpenSSH default
|
|
208
221
|
|
|
209
222
|
ssh_config = get_ssh_config(ssh_config_file)
|
|
210
223
|
if not ssh_config:
|
|
211
|
-
return hostname, cfg, forward_agent, missing_host_key_policy, host_keys_file
|
|
224
|
+
return hostname, cfg, forward_agent, missing_host_key_policy, host_keys_file, keep_alive
|
|
212
225
|
|
|
213
226
|
host_config = ssh_config.lookup(hostname)
|
|
214
227
|
forward_agent = host_config.get("forwardagent") == "yes"
|
|
@@ -234,6 +247,9 @@ class SSHClient(ParamikoClient):
|
|
|
234
247
|
if "port" in host_config:
|
|
235
248
|
cfg["port"] = int(host_config["port"])
|
|
236
249
|
|
|
250
|
+
if "serveraliveinterval" in host_config:
|
|
251
|
+
keep_alive = int(host_config["serveraliveinterval"])
|
|
252
|
+
|
|
237
253
|
if "proxycommand" in host_config:
|
|
238
254
|
cfg["sock"] = ProxyCommand(host_config["proxycommand"])
|
|
239
255
|
|
|
@@ -259,7 +275,7 @@ class SSHClient(ParamikoClient):
|
|
|
259
275
|
sock = c.gateway(hostname, cfg["port"], target, target_config["port"])
|
|
260
276
|
cfg["sock"] = sock
|
|
261
277
|
|
|
262
|
-
return hostname, cfg, forward_agent, missing_host_key_policy, host_keys_file
|
|
278
|
+
return hostname, cfg, forward_agent, missing_host_key_policy, host_keys_file, keep_alive
|
|
263
279
|
|
|
264
280
|
@staticmethod
|
|
265
281
|
def derive_shorthand(ssh_config, host_string):
|
|
@@ -10,6 +10,7 @@ from os import environ, path
|
|
|
10
10
|
import paramiko.config
|
|
11
11
|
from gevent.subprocess import CalledProcessError, check_call
|
|
12
12
|
from paramiko import SSHConfig as ParamikoSSHConfig
|
|
13
|
+
from typing_extensions import override
|
|
13
14
|
|
|
14
15
|
from pyinfra import logger
|
|
15
16
|
|
|
@@ -95,6 +96,7 @@ class SSHConfig(ParamikoSSHConfig):
|
|
|
95
96
|
https://github.com/paramiko/paramiko/pull/1194
|
|
96
97
|
"""
|
|
97
98
|
|
|
99
|
+
@override
|
|
98
100
|
def parse(self, file_obj):
|
|
99
101
|
file_obj = _expand_include_statements(file_obj)
|
|
100
102
|
return super().parse(file_obj)
|
pyinfra/connectors/terraform.py
CHANGED
pyinfra/connectors/util.py
CHANGED
|
@@ -198,14 +198,18 @@ def execute_command_with_sudo_retry(
|
|
|
198
198
|
) -> tuple[int, CommandOutput]:
|
|
199
199
|
return_code, output = execute_command()
|
|
200
200
|
|
|
201
|
+
# If we failed look for a sudo password prompt line and re-submit using the sudo password. Look
|
|
202
|
+
# at all lines here in case anything else gets printed, eg in:
|
|
203
|
+
# https://github.com/pyinfra-dev/pyinfra/issues/1292
|
|
201
204
|
if return_code != 0 and output and output.combined_lines:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
for line in reversed(output.combined_lines):
|
|
206
|
+
if line.line.strip() == "sudo: a password is required":
|
|
207
|
+
# If we need a password, ask the user for it and attach to the host
|
|
208
|
+
# internal connector data for use when executing future commands.
|
|
209
|
+
sudo_password = getpass("{0}sudo password: ".format(host.print_prefix))
|
|
210
|
+
host.connector_data["prompted_sudo_password"] = sudo_password
|
|
211
|
+
return_code, output = execute_command()
|
|
212
|
+
break
|
|
209
213
|
|
|
210
214
|
return return_code, output
|
|
211
215
|
|
|
@@ -232,7 +236,7 @@ def remove_any_sudo_askpass_file(host) -> None:
|
|
|
232
236
|
|
|
233
237
|
|
|
234
238
|
@memoize
|
|
235
|
-
def _show_use_su_login_warning():
|
|
239
|
+
def _show_use_su_login_warning() -> None:
|
|
236
240
|
logger.warning(
|
|
237
241
|
(
|
|
238
242
|
"Using `use_su_login` may not work: "
|
|
@@ -304,7 +308,7 @@ def make_unix_command(
|
|
|
304
308
|
_sudo=False,
|
|
305
309
|
_sudo_user=None,
|
|
306
310
|
_use_sudo_login=False,
|
|
307
|
-
_sudo_password=
|
|
311
|
+
_sudo_password="",
|
|
308
312
|
_sudo_askpass_path=None,
|
|
309
313
|
_preserve_sudo_env=False,
|
|
310
314
|
# Doas config
|
pyinfra/context.py
CHANGED
|
@@ -10,6 +10,7 @@ from types import ModuleType
|
|
|
10
10
|
from typing import TYPE_CHECKING
|
|
11
11
|
|
|
12
12
|
from gevent.local import local
|
|
13
|
+
from typing_extensions import override
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
15
16
|
from pyinfra.api.config import Config
|
|
@@ -26,22 +27,25 @@ class ContextObject:
|
|
|
26
27
|
_container_cls = container
|
|
27
28
|
_base_cls: ModuleType
|
|
28
29
|
|
|
29
|
-
def __init__(self):
|
|
30
|
+
def __init__(self) -> None:
|
|
30
31
|
self._container = self._container_cls()
|
|
31
32
|
self._container.module = None
|
|
32
33
|
|
|
33
34
|
def _get_module(self):
|
|
34
35
|
return self._container.module
|
|
35
36
|
|
|
37
|
+
@override
|
|
36
38
|
def __repr__(self):
|
|
37
39
|
return "ContextObject({0}):{1}".format(
|
|
38
40
|
self._base_cls.__name__,
|
|
39
41
|
repr(self._get_module()),
|
|
40
42
|
)
|
|
41
43
|
|
|
44
|
+
@override
|
|
42
45
|
def __str__(self):
|
|
43
46
|
return str(self._get_module())
|
|
44
47
|
|
|
48
|
+
@override
|
|
45
49
|
def __dir__(self):
|
|
46
50
|
return dir(self._base_cls)
|
|
47
51
|
|
|
@@ -50,6 +54,7 @@ class ContextObject:
|
|
|
50
54
|
return getattr(self._base_cls, key)
|
|
51
55
|
return getattr(self._get_module(), key)
|
|
52
56
|
|
|
57
|
+
@override
|
|
53
58
|
def __setattr__(self, key, value):
|
|
54
59
|
if key in ("_container", "_base_cls"):
|
|
55
60
|
return super().__setattr__(key, value)
|
|
@@ -65,9 +70,11 @@ class ContextObject:
|
|
|
65
70
|
def __len__(self):
|
|
66
71
|
return len(self._get_module())
|
|
67
72
|
|
|
73
|
+
@override
|
|
68
74
|
def __eq__(self, other):
|
|
69
75
|
return self._get_module() == other
|
|
70
76
|
|
|
77
|
+
@override
|
|
71
78
|
def __hash__(self):
|
|
72
79
|
return hash(self._get_module())
|
|
73
80
|
|
|
@@ -89,7 +96,7 @@ class ContextManager:
|
|
|
89
96
|
def set_base(self, module):
|
|
90
97
|
self.context._base_cls = module
|
|
91
98
|
|
|
92
|
-
def reset(self):
|
|
99
|
+
def reset(self) -> None:
|
|
93
100
|
self.context._container.module = None
|
|
94
101
|
|
|
95
102
|
def isset(self):
|
pyinfra/facts/apk.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
3
5
|
from pyinfra.api import FactBase
|
|
4
6
|
|
|
5
7
|
from .util.packaging import parse_packages
|
|
@@ -20,13 +22,16 @@ class ApkPackages(FactBase):
|
|
|
20
22
|
}
|
|
21
23
|
"""
|
|
22
24
|
|
|
25
|
+
@override
|
|
23
26
|
def command(self) -> str:
|
|
24
27
|
return "apk list --installed"
|
|
25
28
|
|
|
29
|
+
@override
|
|
26
30
|
def requires_command(self) -> str:
|
|
27
31
|
return "apk"
|
|
28
32
|
|
|
29
33
|
default = dict
|
|
30
34
|
|
|
35
|
+
@override
|
|
31
36
|
def process(self, output):
|
|
32
37
|
return parse_packages(APK_REGEX, output)
|
pyinfra/facts/apt.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
-
from typing_extensions import TypedDict
|
|
5
|
+
from typing_extensions import TypedDict, override
|
|
6
6
|
|
|
7
7
|
from pyinfra.api import FactBase
|
|
8
8
|
|
|
@@ -76,17 +76,20 @@ class AptSources(FactBase):
|
|
|
76
76
|
]
|
|
77
77
|
"""
|
|
78
78
|
|
|
79
|
+
@override
|
|
79
80
|
def command(self) -> str:
|
|
80
81
|
return make_cat_files_command(
|
|
81
82
|
"/etc/apt/sources.list",
|
|
82
83
|
"/etc/apt/sources.list.d/*.list",
|
|
83
84
|
)
|
|
84
85
|
|
|
86
|
+
@override
|
|
85
87
|
def requires_command(self) -> str:
|
|
86
88
|
return "apt" # if apt installed, above should exist
|
|
87
89
|
|
|
88
90
|
default = list
|
|
89
91
|
|
|
92
|
+
@override
|
|
90
93
|
def process(self, output):
|
|
91
94
|
repos = []
|
|
92
95
|
|
|
@@ -113,9 +116,11 @@ class AptKeys(GpgFactBase):
|
|
|
113
116
|
"""
|
|
114
117
|
|
|
115
118
|
# This requires both apt-key *and* apt-key itself requires gpg
|
|
119
|
+
@override
|
|
116
120
|
def command(self) -> str:
|
|
117
121
|
return "! command -v gpg || apt-key list --with-colons"
|
|
118
122
|
|
|
123
|
+
@override
|
|
119
124
|
def requires_command(self) -> str:
|
|
120
125
|
return "apt-key"
|
|
121
126
|
|
|
@@ -132,13 +137,16 @@ class SimulateOperationWillChange(FactBase[AptSimulationDict]):
|
|
|
132
137
|
Simulate an 'apt-get' operation and try to detect if any changes would be performed.
|
|
133
138
|
"""
|
|
134
139
|
|
|
140
|
+
@override
|
|
135
141
|
def command(self, command: str) -> str:
|
|
136
142
|
# LC_ALL=C: Ensure the output is in english, as we want to parse it
|
|
137
143
|
return "LC_ALL=C " + noninteractive_apt(f"{command} --dry-run")
|
|
138
144
|
|
|
145
|
+
@override
|
|
139
146
|
def requires_command(self, command: str) -> str:
|
|
140
147
|
return "apt-get"
|
|
141
148
|
|
|
149
|
+
@override
|
|
142
150
|
def process(self, output) -> AptSimulationDict:
|
|
143
151
|
# We are looking for a line similar to
|
|
144
152
|
# "3 upgraded, 0 newly installed, 0 to remove and 0 not upgraded."
|
pyinfra/facts/brew.py
CHANGED
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
5
7
|
from pyinfra import logger
|
|
6
8
|
from pyinfra.api import FactBase
|
|
7
9
|
|
|
@@ -37,9 +39,11 @@ class BrewVersion(FactBase):
|
|
|
37
39
|
|
|
38
40
|
"""
|
|
39
41
|
|
|
42
|
+
@override
|
|
40
43
|
def command(self) -> str:
|
|
41
44
|
return "brew --version"
|
|
42
45
|
|
|
46
|
+
@override
|
|
43
47
|
def requires_command(self) -> str:
|
|
44
48
|
return "brew"
|
|
45
49
|
|
|
@@ -47,6 +51,7 @@ class BrewVersion(FactBase):
|
|
|
47
51
|
def default():
|
|
48
52
|
return [0, 0, 0]
|
|
49
53
|
|
|
54
|
+
@override
|
|
50
55
|
def process(self, output):
|
|
51
56
|
out = list(output)[0]
|
|
52
57
|
m = VERSION_MATCHER.match(out)
|
|
@@ -67,14 +72,17 @@ class BrewPackages(FactBase):
|
|
|
67
72
|
}
|
|
68
73
|
"""
|
|
69
74
|
|
|
75
|
+
@override
|
|
70
76
|
def command(self) -> str:
|
|
71
77
|
return "brew list --versions"
|
|
72
78
|
|
|
79
|
+
@override
|
|
73
80
|
def requires_command(self) -> str:
|
|
74
81
|
return "brew"
|
|
75
82
|
|
|
76
83
|
default = dict
|
|
77
84
|
|
|
85
|
+
@override
|
|
78
86
|
def process(self, output):
|
|
79
87
|
return parse_packages(BREW_REGEX, output)
|
|
80
88
|
|
|
@@ -90,12 +98,14 @@ class BrewCasks(BrewPackages):
|
|
|
90
98
|
}
|
|
91
99
|
"""
|
|
92
100
|
|
|
101
|
+
@override
|
|
93
102
|
def command(self) -> str:
|
|
94
103
|
return (
|
|
95
104
|
r'if brew --version | grep -q -e "Homebrew\ +(1\.|2\.[0-5]).*" 1>/dev/null;'
|
|
96
105
|
r"then brew cask list --versions; else brew list --cask --versions; fi"
|
|
97
106
|
)
|
|
98
107
|
|
|
108
|
+
@override
|
|
99
109
|
def requires_command(self) -> str:
|
|
100
110
|
return "brew"
|
|
101
111
|
|
|
@@ -105,13 +115,16 @@ class BrewTaps(FactBase):
|
|
|
105
115
|
Returns a list of brew taps.
|
|
106
116
|
"""
|
|
107
117
|
|
|
118
|
+
@override
|
|
108
119
|
def command(self) -> str:
|
|
109
120
|
return "brew tap"
|
|
110
121
|
|
|
122
|
+
@override
|
|
111
123
|
def requires_command(self) -> str:
|
|
112
124
|
return "brew"
|
|
113
125
|
|
|
114
126
|
default = list
|
|
115
127
|
|
|
128
|
+
@override
|
|
116
129
|
def process(self, output):
|
|
117
130
|
return output
|
pyinfra/facts/bsdinit.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
3
5
|
from .sysvinit import InitdStatus
|
|
4
6
|
|
|
5
7
|
|
|
@@ -9,6 +11,7 @@ class RcdStatus(InitdStatus):
|
|
|
9
11
|
BSD init scripts are well behaved and as such their output can be trusted.
|
|
10
12
|
"""
|
|
11
13
|
|
|
14
|
+
@override
|
|
12
15
|
def command(self) -> str:
|
|
13
16
|
return """
|
|
14
17
|
for SERVICE in `find /etc/rc.d /usr/local/etc/rc.d -type f`; do
|
pyinfra/facts/cargo.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
5
7
|
from pyinfra.api import FactBase
|
|
6
8
|
|
|
7
9
|
from .util.packaging import parse_packages
|
|
@@ -22,11 +24,14 @@ class CargoPackages(FactBase):
|
|
|
22
24
|
|
|
23
25
|
default = dict
|
|
24
26
|
|
|
27
|
+
@override
|
|
25
28
|
def command(self) -> str:
|
|
26
29
|
return "cargo install --list"
|
|
27
30
|
|
|
31
|
+
@override
|
|
28
32
|
def requires_command(self) -> str:
|
|
29
33
|
return "cargo"
|
|
30
34
|
|
|
35
|
+
@override
|
|
31
36
|
def process(self, output):
|
|
32
37
|
return parse_packages(CARGO_REGEX, output)
|
pyinfra/facts/choco.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
3
5
|
from pyinfra.api import FactBase
|
|
4
6
|
|
|
5
7
|
from .util.packaging import parse_packages
|
|
@@ -18,6 +20,7 @@ class ChocoPackages(FactBase):
|
|
|
18
20
|
}
|
|
19
21
|
"""
|
|
20
22
|
|
|
23
|
+
@override
|
|
21
24
|
def command(self) -> str:
|
|
22
25
|
return "choco list"
|
|
23
26
|
|
|
@@ -25,6 +28,7 @@ class ChocoPackages(FactBase):
|
|
|
25
28
|
|
|
26
29
|
default = dict
|
|
27
30
|
|
|
31
|
+
@override
|
|
28
32
|
def process(self, output):
|
|
29
33
|
return parse_packages(CHOCO_REGEX, output)
|
|
30
34
|
|
|
@@ -34,8 +38,10 @@ class ChocoVersion(FactBase):
|
|
|
34
38
|
Returns the choco (Chocolatey) version.
|
|
35
39
|
"""
|
|
36
40
|
|
|
41
|
+
@override
|
|
37
42
|
def command(self) -> str:
|
|
38
43
|
return "choco --version"
|
|
39
44
|
|
|
45
|
+
@override
|
|
40
46
|
def process(self, output):
|
|
41
47
|
return "".join(output).replace("\n", "")
|
pyinfra/facts/crontab.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from typing import Dict, List, Optional, TypedDict, Union
|
|
3
3
|
|
|
4
|
-
from typing_extensions import NotRequired
|
|
4
|
+
from typing_extensions import NotRequired, override
|
|
5
5
|
|
|
6
6
|
from pyinfra.api import FactBase
|
|
7
7
|
from pyinfra.api.util import try_int
|
|
@@ -70,6 +70,7 @@ class CrontabFile:
|
|
|
70
70
|
def __getitem__(self, item) -> Optional[CrontabDict]:
|
|
71
71
|
return self.get(item)
|
|
72
72
|
|
|
73
|
+
@override
|
|
73
74
|
def __repr__(self):
|
|
74
75
|
return f"CrontabResult({self.commands})"
|
|
75
76
|
|
|
@@ -91,6 +92,7 @@ class CrontabFile:
|
|
|
91
92
|
)
|
|
92
93
|
return "\n".join(lines)
|
|
93
94
|
|
|
95
|
+
@override
|
|
94
96
|
def __str__(self):
|
|
95
97
|
return "\n".join(self.format_item(item) for item in self.commands)
|
|
96
98
|
|
|
@@ -139,14 +141,17 @@ class Crontab(FactBase[CrontabFile]):
|
|
|
139
141
|
|
|
140
142
|
default = CrontabFile
|
|
141
143
|
|
|
144
|
+
@override
|
|
142
145
|
def requires_command(self, user=None) -> str:
|
|
143
146
|
return "crontab"
|
|
144
147
|
|
|
148
|
+
@override
|
|
145
149
|
def command(self, user=None):
|
|
146
150
|
if user:
|
|
147
151
|
return "crontab -l -u {0} || true".format(user)
|
|
148
152
|
return "crontab -l || true"
|
|
149
153
|
|
|
154
|
+
@override
|
|
150
155
|
def process(self, output):
|
|
151
156
|
crons = CrontabFile()
|
|
152
157
|
current_comments = []
|
pyinfra/facts/deb.py
CHANGED
|
@@ -3,6 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import re
|
|
4
4
|
import shlex
|
|
5
5
|
|
|
6
|
+
from typing_extensions import override
|
|
7
|
+
|
|
6
8
|
from pyinfra.api import FactBase
|
|
7
9
|
|
|
8
10
|
from .util.packaging import parse_packages
|
|
@@ -16,9 +18,11 @@ class DebArch(FactBase):
|
|
|
16
18
|
Returns the architecture string used in apt repository sources, eg ``amd64``.
|
|
17
19
|
"""
|
|
18
20
|
|
|
21
|
+
@override
|
|
19
22
|
def command(self) -> str:
|
|
20
23
|
return "dpkg --print-architecture"
|
|
21
24
|
|
|
25
|
+
@override
|
|
22
26
|
def requires_command(self) -> str:
|
|
23
27
|
return "dpkg"
|
|
24
28
|
|
|
@@ -34,9 +38,11 @@ class DebPackages(FactBase):
|
|
|
34
38
|
}
|
|
35
39
|
"""
|
|
36
40
|
|
|
41
|
+
@override
|
|
37
42
|
def command(self) -> str:
|
|
38
43
|
return "dpkg -l"
|
|
39
44
|
|
|
45
|
+
@override
|
|
40
46
|
def requires_command(self) -> str:
|
|
41
47
|
return "dpkg"
|
|
42
48
|
|
|
@@ -47,6 +53,7 @@ class DebPackages(FactBase):
|
|
|
47
53
|
DEB_PACKAGE_VERSION_REGEX,
|
|
48
54
|
)
|
|
49
55
|
|
|
56
|
+
@override
|
|
50
57
|
def process(self, output):
|
|
51
58
|
return parse_packages(self.regex, output)
|
|
52
59
|
|
|
@@ -61,14 +68,17 @@ class DebPackage(FactBase):
|
|
|
61
68
|
"version": r"^Version:\s+({0})$".format(DEB_PACKAGE_VERSION_REGEX),
|
|
62
69
|
}
|
|
63
70
|
|
|
71
|
+
@override
|
|
64
72
|
def requires_command(self, package) -> str:
|
|
65
73
|
return "dpkg"
|
|
66
74
|
|
|
75
|
+
@override
|
|
67
76
|
def command(self, package):
|
|
68
77
|
return "! test -e {0} && (dpkg -s {0} 2>/dev/null || true) || dpkg -I {0}".format(
|
|
69
78
|
shlex.quote(package)
|
|
70
79
|
)
|
|
71
80
|
|
|
81
|
+
@override
|
|
72
82
|
def process(self, output):
|
|
73
83
|
data = {}
|
|
74
84
|
|
pyinfra/facts/dnf.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
3
5
|
from pyinfra.api import FactBase
|
|
4
6
|
|
|
5
7
|
from .util import make_cat_files_command
|
|
@@ -23,6 +25,7 @@ class DnfRepositories(FactBase):
|
|
|
23
25
|
]
|
|
24
26
|
"""
|
|
25
27
|
|
|
28
|
+
@override
|
|
26
29
|
def command(self) -> str:
|
|
27
30
|
return make_cat_files_command(
|
|
28
31
|
"/etc/dnf.conf",
|
|
@@ -30,10 +33,12 @@ class DnfRepositories(FactBase):
|
|
|
30
33
|
"/etc/yum.repos.d/*.repo",
|
|
31
34
|
)
|
|
32
35
|
|
|
36
|
+
@override
|
|
33
37
|
def requires_command(self) -> str:
|
|
34
38
|
return "dnf"
|
|
35
39
|
|
|
36
40
|
default = list
|
|
37
41
|
|
|
42
|
+
@override
|
|
38
43
|
def process(self, output):
|
|
39
44
|
return parse_yum_repositories(output)
|
pyinfra/facts/docker.py
CHANGED
|
@@ -8,6 +8,8 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
import json
|
|
10
10
|
|
|
11
|
+
from typing_extensions import override
|
|
12
|
+
|
|
11
13
|
from pyinfra.api import FactBase
|
|
12
14
|
|
|
13
15
|
|
|
@@ -16,9 +18,11 @@ class DockerFactBase(FactBase):
|
|
|
16
18
|
|
|
17
19
|
docker_type: str
|
|
18
20
|
|
|
21
|
+
@override
|
|
19
22
|
def requires_command(self, *args, **kwargs) -> str:
|
|
20
23
|
return "docker"
|
|
21
24
|
|
|
25
|
+
@override
|
|
22
26
|
def process(self, output):
|
|
23
27
|
output = "".join(output)
|
|
24
28
|
return json.loads(output)
|
|
@@ -29,6 +33,7 @@ class DockerSystemInfo(DockerFactBase):
|
|
|
29
33
|
Returns ``docker system info`` output in JSON format.
|
|
30
34
|
"""
|
|
31
35
|
|
|
36
|
+
@override
|
|
32
37
|
def command(self) -> str:
|
|
33
38
|
return 'docker system info --format="{{json .}}"'
|
|
34
39
|
|
|
@@ -42,6 +47,7 @@ class DockerContainers(DockerFactBase):
|
|
|
42
47
|
Returns ``docker inspect`` output for all Docker containers.
|
|
43
48
|
"""
|
|
44
49
|
|
|
50
|
+
@override
|
|
45
51
|
def command(self) -> str:
|
|
46
52
|
return "docker container inspect `docker ps -qa`"
|
|
47
53
|
|
|
@@ -51,6 +57,7 @@ class DockerImages(DockerFactBase):
|
|
|
51
57
|
Returns ``docker inspect`` output for all Docker images.
|
|
52
58
|
"""
|
|
53
59
|
|
|
60
|
+
@override
|
|
54
61
|
def command(self) -> str:
|
|
55
62
|
return "docker image inspect `docker images -q`"
|
|
56
63
|
|
|
@@ -60,6 +67,7 @@ class DockerNetworks(DockerFactBase):
|
|
|
60
67
|
Returns ``docker inspect`` output for all Docker networks.
|
|
61
68
|
"""
|
|
62
69
|
|
|
70
|
+
@override
|
|
63
71
|
def command(self) -> str:
|
|
64
72
|
return "docker network inspect `docker network ls -q`"
|
|
65
73
|
|
|
@@ -69,6 +77,7 @@ class DockerNetworks(DockerFactBase):
|
|
|
69
77
|
|
|
70
78
|
|
|
71
79
|
class DockerSingleMixin(DockerFactBase):
|
|
80
|
+
@override
|
|
72
81
|
def command(self, object_id):
|
|
73
82
|
return "docker {0} inspect {1} 2>&- || true".format(
|
|
74
83
|
self.docker_type,
|
|
@@ -105,6 +114,7 @@ class DockerVolumes(DockerFactBase):
|
|
|
105
114
|
Returns ``docker inspect`` output for all Docker volumes.
|
|
106
115
|
"""
|
|
107
116
|
|
|
117
|
+
@override
|
|
108
118
|
def command(self) -> str:
|
|
109
119
|
return "docker volume inspect `docker volume ls -q`"
|
|
110
120
|
|
pyinfra/facts/efibootmgr.py
CHANGED
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, Iterable, List, Optional, Tuple, TypedDict
|
|
4
4
|
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
5
7
|
from pyinfra.api import FactBase
|
|
6
8
|
|
|
7
9
|
BootEntry = Tuple[bool, str]
|
|
@@ -37,14 +39,17 @@ class EFIBootMgr(FactBase[Optional[EFIBootMgrInfoDict]]):
|
|
|
37
39
|
}
|
|
38
40
|
"""
|
|
39
41
|
|
|
42
|
+
@override
|
|
40
43
|
def requires_command(self, *args: Any, **kwargs: Any) -> str:
|
|
41
44
|
return "efibootmgr"
|
|
42
45
|
|
|
46
|
+
@override
|
|
43
47
|
def command(self) -> str:
|
|
44
48
|
# FIXME: Use '|| true' to properly handle the case where
|
|
45
49
|
# 'efibootmgr' is run on a non-UEFI system
|
|
46
50
|
return "efibootmgr || true"
|
|
47
51
|
|
|
52
|
+
@override
|
|
48
53
|
def process(self, output: Iterable[str]) -> Optional[EFIBootMgrInfoDict]:
|
|
49
54
|
# This parsing code closely follows the printing code of efibootmgr
|
|
50
55
|
# at <https://github.com/rhboot/efibootmgr/blob/main/src/efibootmgr.c#L2020-L2048>
|