pyinfra 0.11.dev3__py3-none-any.whl → 3.5.1__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.
Files changed (203) hide show
  1. pyinfra/__init__.py +9 -12
  2. pyinfra/__main__.py +4 -0
  3. pyinfra/api/__init__.py +18 -3
  4. pyinfra/api/arguments.py +406 -0
  5. pyinfra/api/arguments_typed.py +79 -0
  6. pyinfra/api/command.py +274 -0
  7. pyinfra/api/config.py +222 -28
  8. pyinfra/api/connect.py +33 -13
  9. pyinfra/api/connectors.py +27 -0
  10. pyinfra/api/deploy.py +65 -66
  11. pyinfra/api/exceptions.py +67 -18
  12. pyinfra/api/facts.py +253 -202
  13. pyinfra/api/host.py +413 -50
  14. pyinfra/api/inventory.py +121 -160
  15. pyinfra/api/operation.py +432 -262
  16. pyinfra/api/operations.py +273 -260
  17. pyinfra/api/state.py +302 -248
  18. pyinfra/api/util.py +291 -368
  19. pyinfra/connectors/base.py +173 -0
  20. pyinfra/connectors/chroot.py +212 -0
  21. pyinfra/connectors/docker.py +381 -0
  22. pyinfra/connectors/dockerssh.py +297 -0
  23. pyinfra/connectors/local.py +238 -0
  24. pyinfra/connectors/scp/__init__.py +1 -0
  25. pyinfra/connectors/scp/client.py +204 -0
  26. pyinfra/connectors/ssh.py +670 -0
  27. pyinfra/connectors/ssh_util.py +114 -0
  28. pyinfra/connectors/sshuserclient/client.py +309 -0
  29. pyinfra/connectors/sshuserclient/config.py +102 -0
  30. pyinfra/connectors/terraform.py +135 -0
  31. pyinfra/connectors/util.py +410 -0
  32. pyinfra/connectors/vagrant.py +183 -0
  33. pyinfra/context.py +145 -0
  34. pyinfra/facts/__init__.py +7 -6
  35. pyinfra/facts/apk.py +22 -7
  36. pyinfra/facts/apt.py +117 -60
  37. pyinfra/facts/brew.py +100 -15
  38. pyinfra/facts/bsdinit.py +23 -0
  39. pyinfra/facts/cargo.py +37 -0
  40. pyinfra/facts/choco.py +47 -0
  41. pyinfra/facts/crontab.py +195 -0
  42. pyinfra/facts/deb.py +94 -0
  43. pyinfra/facts/dnf.py +48 -0
  44. pyinfra/facts/docker.py +96 -23
  45. pyinfra/facts/efibootmgr.py +113 -0
  46. pyinfra/facts/files.py +630 -58
  47. pyinfra/facts/flatpak.py +77 -0
  48. pyinfra/facts/freebsd.py +70 -0
  49. pyinfra/facts/gem.py +19 -6
  50. pyinfra/facts/git.py +59 -14
  51. pyinfra/facts/gpg.py +150 -0
  52. pyinfra/facts/hardware.py +313 -167
  53. pyinfra/facts/iptables.py +72 -62
  54. pyinfra/facts/launchd.py +44 -0
  55. pyinfra/facts/lxd.py +17 -4
  56. pyinfra/facts/mysql.py +122 -86
  57. pyinfra/facts/npm.py +17 -9
  58. pyinfra/facts/openrc.py +71 -0
  59. pyinfra/facts/opkg.py +246 -0
  60. pyinfra/facts/pacman.py +50 -7
  61. pyinfra/facts/pip.py +24 -7
  62. pyinfra/facts/pipx.py +82 -0
  63. pyinfra/facts/pkg.py +15 -6
  64. pyinfra/facts/pkgin.py +35 -0
  65. pyinfra/facts/podman.py +54 -0
  66. pyinfra/facts/postgres.py +178 -0
  67. pyinfra/facts/postgresql.py +6 -147
  68. pyinfra/facts/rpm.py +105 -0
  69. pyinfra/facts/runit.py +77 -0
  70. pyinfra/facts/selinux.py +161 -0
  71. pyinfra/facts/server.py +746 -285
  72. pyinfra/facts/snap.py +88 -0
  73. pyinfra/facts/systemd.py +139 -0
  74. pyinfra/facts/sysvinit.py +59 -0
  75. pyinfra/facts/upstart.py +35 -0
  76. pyinfra/facts/util/__init__.py +17 -0
  77. pyinfra/facts/util/databases.py +4 -6
  78. pyinfra/facts/util/packaging.py +37 -6
  79. pyinfra/facts/util/units.py +30 -0
  80. pyinfra/facts/util/win_files.py +99 -0
  81. pyinfra/facts/vzctl.py +20 -13
  82. pyinfra/facts/xbps.py +35 -0
  83. pyinfra/facts/yum.py +34 -40
  84. pyinfra/facts/zfs.py +77 -0
  85. pyinfra/facts/zypper.py +42 -0
  86. pyinfra/local.py +45 -83
  87. pyinfra/operations/__init__.py +12 -0
  88. pyinfra/operations/apk.py +98 -0
  89. pyinfra/operations/apt.py +488 -0
  90. pyinfra/operations/brew.py +231 -0
  91. pyinfra/operations/bsdinit.py +59 -0
  92. pyinfra/operations/cargo.py +45 -0
  93. pyinfra/operations/choco.py +61 -0
  94. pyinfra/operations/crontab.py +191 -0
  95. pyinfra/operations/dnf.py +210 -0
  96. pyinfra/operations/docker.py +446 -0
  97. pyinfra/operations/files.py +1939 -0
  98. pyinfra/operations/flatpak.py +94 -0
  99. pyinfra/operations/freebsd/__init__.py +12 -0
  100. pyinfra/operations/freebsd/freebsd_update.py +70 -0
  101. pyinfra/operations/freebsd/pkg.py +219 -0
  102. pyinfra/operations/freebsd/service.py +116 -0
  103. pyinfra/operations/freebsd/sysrc.py +92 -0
  104. pyinfra/operations/gem.py +47 -0
  105. pyinfra/operations/git.py +419 -0
  106. pyinfra/operations/iptables.py +311 -0
  107. pyinfra/operations/launchd.py +45 -0
  108. pyinfra/operations/lxd.py +68 -0
  109. pyinfra/operations/mysql.py +609 -0
  110. pyinfra/operations/npm.py +57 -0
  111. pyinfra/operations/openrc.py +63 -0
  112. pyinfra/operations/opkg.py +88 -0
  113. pyinfra/operations/pacman.py +81 -0
  114. pyinfra/operations/pip.py +205 -0
  115. pyinfra/operations/pipx.py +102 -0
  116. pyinfra/operations/pkg.py +70 -0
  117. pyinfra/operations/pkgin.py +91 -0
  118. pyinfra/operations/postgres.py +436 -0
  119. pyinfra/operations/postgresql.py +30 -0
  120. pyinfra/operations/puppet.py +40 -0
  121. pyinfra/operations/python.py +72 -0
  122. pyinfra/operations/runit.py +184 -0
  123. pyinfra/operations/selinux.py +189 -0
  124. pyinfra/operations/server.py +1099 -0
  125. pyinfra/operations/snap.py +117 -0
  126. pyinfra/operations/ssh.py +216 -0
  127. pyinfra/operations/systemd.py +149 -0
  128. pyinfra/operations/sysvinit.py +141 -0
  129. pyinfra/operations/upstart.py +68 -0
  130. pyinfra/operations/util/__init__.py +12 -0
  131. pyinfra/operations/util/docker.py +251 -0
  132. pyinfra/operations/util/files.py +247 -0
  133. pyinfra/operations/util/packaging.py +336 -0
  134. pyinfra/operations/util/service.py +46 -0
  135. pyinfra/operations/vzctl.py +137 -0
  136. pyinfra/operations/xbps.py +77 -0
  137. pyinfra/operations/yum.py +210 -0
  138. pyinfra/operations/zfs.py +175 -0
  139. pyinfra/operations/zypper.py +192 -0
  140. pyinfra/progress.py +44 -32
  141. pyinfra/py.typed +0 -0
  142. pyinfra/version.py +9 -1
  143. pyinfra-3.5.1.dist-info/METADATA +141 -0
  144. pyinfra-3.5.1.dist-info/RECORD +159 -0
  145. {pyinfra-0.11.dev3.dist-info → pyinfra-3.5.1.dist-info}/WHEEL +1 -2
  146. pyinfra-3.5.1.dist-info/entry_points.txt +12 -0
  147. {pyinfra-0.11.dev3.dist-info → pyinfra-3.5.1.dist-info/licenses}/LICENSE.md +1 -1
  148. pyinfra_cli/__init__.py +1 -0
  149. pyinfra_cli/cli.py +780 -0
  150. pyinfra_cli/commands.py +66 -0
  151. pyinfra_cli/exceptions.py +155 -65
  152. pyinfra_cli/inventory.py +233 -89
  153. pyinfra_cli/log.py +39 -43
  154. pyinfra_cli/main.py +26 -495
  155. pyinfra_cli/prints.py +215 -156
  156. pyinfra_cli/util.py +172 -105
  157. pyinfra_cli/virtualenv.py +25 -20
  158. pyinfra/api/connectors/__init__.py +0 -21
  159. pyinfra/api/connectors/ansible.py +0 -99
  160. pyinfra/api/connectors/docker.py +0 -178
  161. pyinfra/api/connectors/local.py +0 -169
  162. pyinfra/api/connectors/ssh.py +0 -402
  163. pyinfra/api/connectors/sshuserclient/client.py +0 -105
  164. pyinfra/api/connectors/sshuserclient/config.py +0 -90
  165. pyinfra/api/connectors/util.py +0 -63
  166. pyinfra/api/connectors/vagrant.py +0 -155
  167. pyinfra/facts/init.py +0 -176
  168. pyinfra/facts/util/files.py +0 -102
  169. pyinfra/hook.py +0 -41
  170. pyinfra/modules/__init__.py +0 -11
  171. pyinfra/modules/apk.py +0 -64
  172. pyinfra/modules/apt.py +0 -272
  173. pyinfra/modules/brew.py +0 -122
  174. pyinfra/modules/files.py +0 -711
  175. pyinfra/modules/gem.py +0 -30
  176. pyinfra/modules/git.py +0 -115
  177. pyinfra/modules/init.py +0 -344
  178. pyinfra/modules/iptables.py +0 -271
  179. pyinfra/modules/lxd.py +0 -45
  180. pyinfra/modules/mysql.py +0 -347
  181. pyinfra/modules/npm.py +0 -47
  182. pyinfra/modules/pacman.py +0 -60
  183. pyinfra/modules/pip.py +0 -99
  184. pyinfra/modules/pkg.py +0 -43
  185. pyinfra/modules/postgresql.py +0 -245
  186. pyinfra/modules/puppet.py +0 -20
  187. pyinfra/modules/python.py +0 -37
  188. pyinfra/modules/server.py +0 -524
  189. pyinfra/modules/ssh.py +0 -150
  190. pyinfra/modules/util/files.py +0 -52
  191. pyinfra/modules/util/packaging.py +0 -118
  192. pyinfra/modules/vzctl.py +0 -133
  193. pyinfra/modules/yum.py +0 -171
  194. pyinfra/pseudo_modules.py +0 -64
  195. pyinfra-0.11.dev3.dist-info/.DS_Store +0 -0
  196. pyinfra-0.11.dev3.dist-info/METADATA +0 -135
  197. pyinfra-0.11.dev3.dist-info/RECORD +0 -95
  198. pyinfra-0.11.dev3.dist-info/entry_points.txt +0 -3
  199. pyinfra-0.11.dev3.dist-info/top_level.txt +0 -2
  200. pyinfra_cli/__main__.py +0 -40
  201. pyinfra_cli/config.py +0 -92
  202. /pyinfra/{modules/util → connectors}/__init__.py +0 -0
  203. /pyinfra/{api/connectors → connectors}/sshuserclient/__init__.py +0 -0
@@ -0,0 +1,173 @@
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
+ Generate inventory targets. This is a staticmethod because each yield will become a new host
90
+ object with a new (ie not this) instance of the connector.
91
+ """
92
+
93
+ def connect(self) -> None:
94
+ """
95
+ Connect this connector instance. Should raise ConnectError exceptions to indicate failure.
96
+ """
97
+
98
+ def disconnect(self) -> None:
99
+ """
100
+ Disconnect this connector instance.
101
+ """
102
+
103
+ @abc.abstractmethod
104
+ def run_shell_command(
105
+ self,
106
+ command: "StringCommand",
107
+ print_output: bool,
108
+ print_input: bool,
109
+ **arguments: Unpack["ConnectorArguments"],
110
+ ) -> tuple[bool, "CommandOutput"]:
111
+ """
112
+ Execute a command.
113
+
114
+ Args:
115
+ command (StringCommand): actual command to execute
116
+ print_output (bool): whether to print command output
117
+ print_input (bool): whether to print command input
118
+ arguments: (ConnectorArguments): connector global arguments
119
+
120
+ Returns:
121
+ tuple: (bool, CommandOutput)
122
+ Bool indicating success and CommandOutput with stdout/stderr lines.
123
+ """
124
+
125
+ @abc.abstractmethod
126
+ def put_file(
127
+ self,
128
+ filename_or_io: Union[str, IOBase],
129
+ remote_filename: str,
130
+ remote_temp_filename: Optional[str] = None,
131
+ print_output: bool = False,
132
+ print_input: bool = False,
133
+ **arguments: Unpack["ConnectorArguments"],
134
+ ) -> bool:
135
+ """
136
+ Upload a local file or IO object by copying it to a temporary directory
137
+ and then writing it to the upload location.
138
+
139
+ Returns:
140
+ bool: indicating success or failure.
141
+ """
142
+
143
+ @abc.abstractmethod
144
+ def get_file(
145
+ self,
146
+ remote_filename: str,
147
+ filename_or_io: Union[str, IOBase],
148
+ remote_temp_filename: Optional[str] = None,
149
+ print_output: bool = False,
150
+ print_input: bool = False,
151
+ **arguments: Unpack["ConnectorArguments"],
152
+ ) -> bool:
153
+ """
154
+ Download a local file by copying it to a temporary location and then writing
155
+ it to our filename or IO object.
156
+
157
+ Returns:
158
+ bool: indicating success or failure.
159
+ """
160
+
161
+ def check_can_rsync(self) -> None:
162
+ raise NotImplementedError("This connector does not support rsync")
163
+
164
+ def rsync(
165
+ self,
166
+ src: str,
167
+ dest: str,
168
+ flags: Iterable[str],
169
+ print_output: bool = False,
170
+ print_input: bool = False,
171
+ **arguments: Unpack["ConnectorArguments"],
172
+ ) -> bool:
173
+ raise NotImplementedError("This connector does not support rsync")
@@ -0,0 +1,212 @@
1
+ import os
2
+ from tempfile import mkstemp
3
+ from typing import TYPE_CHECKING, Optional
4
+
5
+ import click
6
+ from typing_extensions import Unpack, override
7
+
8
+ from pyinfra import local, logger
9
+ from pyinfra.api import QuoteString, StringCommand
10
+ from pyinfra.api.exceptions import ConnectError, InventoryError, PyinfraError
11
+ from pyinfra.api.util import get_file_io, memoize
12
+ from pyinfra.progress import progress_spinner
13
+
14
+ from .base import BaseConnector
15
+ from .local import LocalConnector
16
+ from .util import extract_control_arguments, make_unix_command_for_host
17
+
18
+ if TYPE_CHECKING:
19
+ from pyinfra.api.arguments import ConnectorArguments
20
+ from pyinfra.api.host import Host
21
+ from pyinfra.api.state import State
22
+
23
+
24
+ @memoize
25
+ def show_warning() -> None:
26
+ logger.warning("The @chroot connector is in beta!")
27
+
28
+
29
+ class ChrootConnector(BaseConnector):
30
+ """
31
+ The chroot connector allows you to execute operations within another root.
32
+ """
33
+
34
+ handles_execution = True
35
+
36
+ local: LocalConnector
37
+
38
+ def __init__(self, state: "State", host: "Host"):
39
+ super().__init__(state, host)
40
+ self.local = LocalConnector(state, host)
41
+
42
+ @override
43
+ @staticmethod
44
+ def make_names_data(name: Optional[str] = None):
45
+ if not name:
46
+ raise InventoryError("No directory provided!")
47
+
48
+ show_warning()
49
+
50
+ yield (
51
+ "@chroot/{0}".format(name),
52
+ {
53
+ "chroot_directory": "/{0}".format(name.lstrip("/")),
54
+ },
55
+ ["@chroot"],
56
+ )
57
+
58
+ @override
59
+ def connect(self) -> None:
60
+ self.local.connect()
61
+
62
+ chroot_directory = self.host.data.chroot_directory
63
+
64
+ try:
65
+ with progress_spinner({"chroot run"}):
66
+ local.shell(
67
+ "chroot {0} ls".format(chroot_directory),
68
+ splitlines=True,
69
+ )
70
+ except PyinfraError as e:
71
+ raise ConnectError(e.args[0])
72
+
73
+ self.host.connector_data["chroot_directory"] = chroot_directory
74
+
75
+ @override
76
+ def run_shell_command(
77
+ self,
78
+ command,
79
+ print_output: bool = False,
80
+ print_input: bool = False,
81
+ **command_arguments: Unpack["ConnectorArguments"],
82
+ ):
83
+ local_arguments = extract_control_arguments(command_arguments)
84
+
85
+ chroot_directory = self.host.connector_data["chroot_directory"]
86
+
87
+ command = make_unix_command_for_host(self.state, self.host, command, **command_arguments)
88
+ command = QuoteString(command)
89
+
90
+ logger.debug("--> Running chroot command on (%s): %s", chroot_directory, command)
91
+
92
+ chroot_command = StringCommand(
93
+ "chroot",
94
+ chroot_directory,
95
+ "sh",
96
+ "-c",
97
+ command,
98
+ )
99
+
100
+ return self.local.run_shell_command(
101
+ chroot_command,
102
+ print_output=print_output,
103
+ print_input=print_input,
104
+ **local_arguments,
105
+ )
106
+
107
+ @override
108
+ def put_file(
109
+ self,
110
+ filename_or_io,
111
+ remote_filename,
112
+ remote_temp_filename=None, # ignored
113
+ print_output: bool = False,
114
+ print_input: bool = False,
115
+ **kwargs, # ignored (sudo/etc)
116
+ ):
117
+ _, temp_filename = mkstemp()
118
+
119
+ try:
120
+ # Load our file or IO object and write it to the temporary file
121
+ with get_file_io(filename_or_io) as file_io:
122
+ with open(temp_filename, "wb") as temp_f:
123
+ data = file_io.read()
124
+
125
+ if isinstance(data, str):
126
+ data = data.encode()
127
+
128
+ temp_f.write(data)
129
+
130
+ chroot_directory = self.host.connector_data["chroot_directory"]
131
+ chroot_command = StringCommand(
132
+ "cp",
133
+ temp_filename,
134
+ f"{chroot_directory}/{remote_filename}",
135
+ )
136
+
137
+ status, output = self.local.run_shell_command(
138
+ chroot_command,
139
+ print_output=print_output,
140
+ print_input=print_input,
141
+ )
142
+ finally:
143
+ os.remove(temp_filename)
144
+
145
+ if not status:
146
+ raise IOError(output.stderr)
147
+
148
+ if print_output:
149
+ click.echo(
150
+ "{0}file uploaded to chroot: {1}".format(
151
+ self.host.print_prefix,
152
+ remote_filename,
153
+ ),
154
+ err=True,
155
+ )
156
+
157
+ return status
158
+
159
+ @override
160
+ def get_file(
161
+ self,
162
+ remote_filename,
163
+ filename_or_io,
164
+ remote_temp_filename=None, # ignored
165
+ print_output: bool = False,
166
+ print_input: bool = False,
167
+ **kwargs, # ignored (sudo/etc)
168
+ ):
169
+ _, temp_filename = mkstemp()
170
+
171
+ try:
172
+ chroot_directory = self.host.connector_data["chroot_directory"]
173
+ chroot_command = StringCommand(
174
+ "cp",
175
+ f"{chroot_directory}/{remote_filename}",
176
+ temp_filename,
177
+ )
178
+
179
+ status, output = self.local.run_shell_command(
180
+ chroot_command,
181
+ print_output=print_output,
182
+ print_input=print_input,
183
+ )
184
+
185
+ # Load the temporary file and write it to our file or IO object
186
+ with open(temp_filename, "rb") as temp_f:
187
+ with get_file_io(filename_or_io, "wb") as file_io:
188
+ data = temp_f.read()
189
+ data_bytes: bytes
190
+
191
+ if isinstance(data, str):
192
+ data_bytes = data.encode()
193
+ else:
194
+ data_bytes = data
195
+
196
+ file_io.write(data_bytes)
197
+ finally:
198
+ os.remove(temp_filename)
199
+
200
+ if not status:
201
+ raise IOError(output.stderr)
202
+
203
+ if print_output:
204
+ click.echo(
205
+ "{0}file downloaded from chroot: {1}".format(
206
+ self.host.print_prefix,
207
+ remote_filename,
208
+ ),
209
+ err=True,
210
+ )
211
+
212
+ return status