pyinfra 3.0b1__py2.py3-none-any.whl → 3.0b3__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.py +9 -3
- pyinfra/api/arguments_typed.py +8 -5
- pyinfra/api/command.py +5 -3
- pyinfra/api/config.py +115 -13
- pyinfra/api/connectors.py +5 -2
- pyinfra/api/facts.py +33 -32
- pyinfra/api/host.py +5 -5
- pyinfra/api/inventory.py +4 -0
- pyinfra/api/operation.py +22 -14
- pyinfra/api/util.py +24 -16
- pyinfra/connectors/base.py +3 -6
- pyinfra/connectors/docker.py +2 -9
- pyinfra/connectors/local.py +2 -2
- pyinfra/connectors/ssh.py +2 -2
- pyinfra/connectors/util.py +6 -7
- pyinfra/connectors/vagrant.py +5 -5
- pyinfra/context.py +1 -0
- pyinfra/facts/apk.py +2 -0
- pyinfra/facts/apt.py +2 -0
- pyinfra/facts/brew.py +2 -0
- pyinfra/facts/bsdinit.py +2 -0
- pyinfra/facts/cargo.py +2 -0
- pyinfra/facts/choco.py +2 -0
- pyinfra/facts/deb.py +7 -2
- pyinfra/facts/dnf.py +2 -0
- pyinfra/facts/docker.py +18 -0
- pyinfra/facts/files.py +2 -0
- pyinfra/facts/gem.py +2 -0
- pyinfra/facts/gpg.py +2 -0
- pyinfra/facts/hardware.py +30 -22
- pyinfra/facts/launchd.py +2 -0
- pyinfra/facts/lxd.py +2 -0
- pyinfra/facts/mysql.py +12 -6
- pyinfra/facts/npm.py +1 -0
- pyinfra/facts/openrc.py +2 -0
- pyinfra/facts/pacman.py +6 -2
- pyinfra/facts/pip.py +2 -0
- pyinfra/facts/pkg.py +2 -0
- pyinfra/facts/pkgin.py +2 -0
- pyinfra/facts/postgres.py +6 -6
- pyinfra/facts/postgresql.py +2 -0
- pyinfra/facts/rpm.py +12 -9
- pyinfra/facts/runit.py +68 -0
- pyinfra/facts/server.py +10 -13
- pyinfra/facts/snap.py +2 -0
- pyinfra/facts/systemd.py +2 -0
- pyinfra/facts/upstart.py +2 -0
- pyinfra/facts/util/packaging.py +3 -2
- pyinfra/facts/vzctl.py +2 -0
- pyinfra/facts/xbps.py +2 -0
- pyinfra/facts/yum.py +2 -0
- pyinfra/facts/zypper.py +2 -0
- pyinfra/operations/apk.py +3 -1
- pyinfra/operations/apt.py +16 -18
- pyinfra/operations/brew.py +10 -8
- pyinfra/operations/bsdinit.py +5 -3
- pyinfra/operations/cargo.py +3 -1
- pyinfra/operations/choco.py +3 -1
- pyinfra/operations/dnf.py +15 -19
- pyinfra/operations/docker.py +339 -0
- pyinfra/operations/files.py +81 -66
- pyinfra/operations/gem.py +3 -1
- pyinfra/operations/git.py +18 -16
- pyinfra/operations/iptables.py +27 -25
- pyinfra/operations/launchd.py +5 -6
- pyinfra/operations/lxd.py +7 -4
- pyinfra/operations/mysql.py +57 -53
- pyinfra/operations/npm.py +8 -1
- pyinfra/operations/openrc.py +5 -3
- pyinfra/operations/pacman.py +4 -5
- pyinfra/operations/pip.py +11 -9
- pyinfra/operations/pkg.py +3 -1
- pyinfra/operations/pkgin.py +3 -1
- pyinfra/operations/postgres.py +39 -37
- pyinfra/operations/postgresql.py +2 -0
- pyinfra/operations/puppet.py +3 -1
- pyinfra/operations/python.py +7 -3
- pyinfra/operations/runit.py +182 -0
- pyinfra/operations/selinux.py +42 -16
- pyinfra/operations/server.py +52 -43
- pyinfra/operations/snap.py +3 -1
- pyinfra/operations/ssh.py +12 -10
- pyinfra/operations/systemd.py +12 -8
- pyinfra/operations/sysvinit.py +6 -4
- pyinfra/operations/upstart.py +5 -3
- pyinfra/operations/util/docker.py +177 -0
- pyinfra/operations/util/files.py +24 -16
- pyinfra/operations/util/packaging.py +53 -37
- pyinfra/operations/util/service.py +25 -18
- pyinfra/operations/vzctl.py +12 -10
- pyinfra/operations/xbps.py +3 -1
- pyinfra/operations/yum.py +14 -18
- pyinfra/operations/zypper.py +8 -9
- pyinfra/version.py +5 -2
- {pyinfra-3.0b1.dist-info → pyinfra-3.0b3.dist-info}/METADATA +30 -28
- pyinfra-3.0b3.dist-info/RECORD +167 -0
- {pyinfra-3.0b1.dist-info → pyinfra-3.0b3.dist-info}/WHEEL +1 -1
- pyinfra_cli/exceptions.py +0 -5
- pyinfra_cli/inventory.py +38 -19
- pyinfra_cli/prints.py +15 -11
- pyinfra_cli/util.py +3 -1
- tests/test_api/test_api_operations.py +1 -1
- tests/test_connectors/test_ssh.py +66 -13
- tests/test_connectors/test_vagrant.py +3 -3
- pyinfra-3.0b1.dist-info/RECORD +0 -163
- {pyinfra-3.0b1.dist-info → pyinfra-3.0b3.dist-info}/LICENSE.md +0 -0
- {pyinfra-3.0b1.dist-info → pyinfra-3.0b3.dist-info}/entry_points.txt +0 -0
- {pyinfra-3.0b1.dist-info → pyinfra-3.0b3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manager Docker Containers, Volumes and Networks
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pyinfra import host
|
|
6
|
+
from pyinfra.api import operation
|
|
7
|
+
from pyinfra.facts.docker import DockerContainers, DockerNetworks, DockerVolumes
|
|
8
|
+
|
|
9
|
+
from .util.docker import handle_docker
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@operation()
|
|
13
|
+
def container(
|
|
14
|
+
container,
|
|
15
|
+
image="",
|
|
16
|
+
ports=None,
|
|
17
|
+
networks=None,
|
|
18
|
+
volumes=None,
|
|
19
|
+
env_vars=None,
|
|
20
|
+
pull_always=False,
|
|
21
|
+
present=True,
|
|
22
|
+
force=False,
|
|
23
|
+
start=True,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Manage Docker containers
|
|
27
|
+
|
|
28
|
+
+ container: name to identify the container
|
|
29
|
+
+ image: container image and tag ex: nginx:alpine
|
|
30
|
+
+ networks: network list to attach on container
|
|
31
|
+
+ ports: port list to expose
|
|
32
|
+
+ volumes: volume list to map on container
|
|
33
|
+
+ env_vars: environment varible list to inject on container
|
|
34
|
+
+ pull_always: force image pull
|
|
35
|
+
+ force: remove a contaner with same name and create a new one
|
|
36
|
+
+ present: whether the container should be up and running
|
|
37
|
+
+ start: start or stop the container
|
|
38
|
+
|
|
39
|
+
**Examples:**
|
|
40
|
+
|
|
41
|
+
.. code:: python
|
|
42
|
+
|
|
43
|
+
# Run a container
|
|
44
|
+
docker.container(
|
|
45
|
+
name="Deploy Nginx container",
|
|
46
|
+
container="nginx",
|
|
47
|
+
image="nginx:alpine",
|
|
48
|
+
ports=["80:80"],
|
|
49
|
+
present=True,
|
|
50
|
+
force=True,
|
|
51
|
+
networks=["proxy", "services"],
|
|
52
|
+
volumes=["nginx_data:/usr/share/nginx/html"],
|
|
53
|
+
pull_always=True,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Stop a container
|
|
57
|
+
docker.container(
|
|
58
|
+
name="Stop Nginx container",
|
|
59
|
+
container="nginx",
|
|
60
|
+
start=False,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Start a container
|
|
64
|
+
docker.container(
|
|
65
|
+
name="Start Nginx container",
|
|
66
|
+
container="nginx",
|
|
67
|
+
start=True,
|
|
68
|
+
)
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
existent_container = [c for c in host.get_fact(DockerContainers) if container in c["Name"]]
|
|
72
|
+
|
|
73
|
+
if force:
|
|
74
|
+
if existent_container:
|
|
75
|
+
yield handle_docker(
|
|
76
|
+
resource="container",
|
|
77
|
+
command="remove",
|
|
78
|
+
container=container,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if present:
|
|
82
|
+
if not existent_container or force:
|
|
83
|
+
yield handle_docker(
|
|
84
|
+
resource="container",
|
|
85
|
+
command="create",
|
|
86
|
+
container=container,
|
|
87
|
+
image=image,
|
|
88
|
+
ports=ports,
|
|
89
|
+
networks=networks,
|
|
90
|
+
volumes=volumes,
|
|
91
|
+
env_vars=env_vars,
|
|
92
|
+
pull_always=pull_always,
|
|
93
|
+
present=present,
|
|
94
|
+
force=force,
|
|
95
|
+
start=start,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if existent_container and start:
|
|
99
|
+
if existent_container[0]["State"]["Status"] != "running":
|
|
100
|
+
yield handle_docker(
|
|
101
|
+
resource="container",
|
|
102
|
+
command="start",
|
|
103
|
+
container=container,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
if existent_container and not start:
|
|
107
|
+
if existent_container[0]["State"]["Status"] == "running":
|
|
108
|
+
yield handle_docker(
|
|
109
|
+
resource="container",
|
|
110
|
+
command="stop",
|
|
111
|
+
container=container,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if existent_container and not present:
|
|
115
|
+
yield handle_docker(
|
|
116
|
+
resource="container",
|
|
117
|
+
command="remove",
|
|
118
|
+
container=container,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@operation(is_idempotent=False)
|
|
123
|
+
def image(image, present=True):
|
|
124
|
+
"""
|
|
125
|
+
Manage Docker images
|
|
126
|
+
|
|
127
|
+
+ image: Image and tag ex: nginx:alpine
|
|
128
|
+
+ present: whether the Docker image should be exist
|
|
129
|
+
|
|
130
|
+
**Examples:**
|
|
131
|
+
|
|
132
|
+
.. code:: python
|
|
133
|
+
|
|
134
|
+
# Pull a Docker image
|
|
135
|
+
docker.image(
|
|
136
|
+
name="Pull nginx image",
|
|
137
|
+
image="nginx:alpine",
|
|
138
|
+
present=True,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Remove a Docker image
|
|
142
|
+
docker.image(
|
|
143
|
+
name="Remove nginx image",
|
|
144
|
+
image:"nginx:image",
|
|
145
|
+
present=False,
|
|
146
|
+
)
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
if present:
|
|
150
|
+
yield handle_docker(
|
|
151
|
+
resource="image",
|
|
152
|
+
command="pull",
|
|
153
|
+
image=image,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
else:
|
|
157
|
+
yield handle_docker(
|
|
158
|
+
resource="image",
|
|
159
|
+
command="remove",
|
|
160
|
+
image=image,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@operation()
|
|
165
|
+
def volume(volume, driver="", labels=None, present=True):
|
|
166
|
+
"""
|
|
167
|
+
Manage Docker volumes
|
|
168
|
+
|
|
169
|
+
+ volume: Volume name
|
|
170
|
+
+ driver: Docker volume storage driver
|
|
171
|
+
+ labels: Label list to attach in the volume
|
|
172
|
+
+ present: whether the Docker volume should exist
|
|
173
|
+
|
|
174
|
+
**Examples:**
|
|
175
|
+
|
|
176
|
+
.. code:: python
|
|
177
|
+
|
|
178
|
+
# Create a Docker volume
|
|
179
|
+
docker.volume(
|
|
180
|
+
name="Create nginx volume",
|
|
181
|
+
volume="nginx_data",
|
|
182
|
+
present=True
|
|
183
|
+
)
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
existent_volume = [v for v in host.get_fact(DockerVolumes) if v["Name"] == volume]
|
|
187
|
+
|
|
188
|
+
if present:
|
|
189
|
+
|
|
190
|
+
if existent_volume:
|
|
191
|
+
host.noop("Volume alredy exist!")
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
yield handle_docker(
|
|
195
|
+
resource="volume",
|
|
196
|
+
command="create",
|
|
197
|
+
volume=volume,
|
|
198
|
+
driver=driver,
|
|
199
|
+
labels=labels,
|
|
200
|
+
present=present,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
else:
|
|
204
|
+
if existent_volume is None:
|
|
205
|
+
host.noop("There is no {0} volume!".format(volume))
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
yield handle_docker(
|
|
209
|
+
resource="volume",
|
|
210
|
+
command="remove",
|
|
211
|
+
volume=volume,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@operation()
|
|
216
|
+
def network(
|
|
217
|
+
network,
|
|
218
|
+
driver="",
|
|
219
|
+
gateway="",
|
|
220
|
+
ip_range="",
|
|
221
|
+
ipam_driver="",
|
|
222
|
+
subnet="",
|
|
223
|
+
scope="",
|
|
224
|
+
opts=None,
|
|
225
|
+
ipam_opts=None,
|
|
226
|
+
labels=None,
|
|
227
|
+
ingress=False,
|
|
228
|
+
attachable=False,
|
|
229
|
+
present=True,
|
|
230
|
+
):
|
|
231
|
+
"""
|
|
232
|
+
Manage docker networks
|
|
233
|
+
|
|
234
|
+
+ network_name: Image name
|
|
235
|
+
+ driver: Container image and tag ex: nginx:alpine
|
|
236
|
+
+ gateway: IPv4 or IPv6 Gateway for the master subnet
|
|
237
|
+
+ ip_range: Allocate container ip from a sub-range
|
|
238
|
+
+ ipam_driver: IP Address Management Driver
|
|
239
|
+
+ subnet: Subnet in CIDR format that represents a network segment
|
|
240
|
+
+ scope: Control the network's scope
|
|
241
|
+
+ opts: Set driver specific options
|
|
242
|
+
+ ipam_opts: Set IPAM driver specific options
|
|
243
|
+
+ labels: Label list to attach in the network
|
|
244
|
+
+ ingress: Create swarm routing-mesh network
|
|
245
|
+
+ attachable: Enable manual container attachment
|
|
246
|
+
+ present: whether the Docker network should exist
|
|
247
|
+
|
|
248
|
+
**Examples:**
|
|
249
|
+
|
|
250
|
+
.. code:: python
|
|
251
|
+
|
|
252
|
+
# Create Docker network
|
|
253
|
+
docker.network(
|
|
254
|
+
name="Create nginx network",
|
|
255
|
+
network_name="nginx",
|
|
256
|
+
attachable=True,
|
|
257
|
+
present=True,
|
|
258
|
+
)
|
|
259
|
+
"""
|
|
260
|
+
existent_network = [n for n in host.get_fact(DockerNetworks) if n["Name"] == network]
|
|
261
|
+
|
|
262
|
+
if present:
|
|
263
|
+
if existent_network:
|
|
264
|
+
host.noop("Alredy exist a network with {0} name!".format(network))
|
|
265
|
+
return
|
|
266
|
+
|
|
267
|
+
yield handle_docker(
|
|
268
|
+
resource="network",
|
|
269
|
+
command="create",
|
|
270
|
+
network=network,
|
|
271
|
+
driver=driver,
|
|
272
|
+
gateway=gateway,
|
|
273
|
+
ip_range=ip_range,
|
|
274
|
+
ipam_driver=ipam_driver,
|
|
275
|
+
subnet=subnet,
|
|
276
|
+
scope=scope,
|
|
277
|
+
opts=opts,
|
|
278
|
+
ipam_opts=ipam_opts,
|
|
279
|
+
labels=labels,
|
|
280
|
+
ingress=ingress,
|
|
281
|
+
attachable=attachable,
|
|
282
|
+
present=present,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
else:
|
|
286
|
+
if existent_network is None:
|
|
287
|
+
host.noop("Ther is not network with {0} name!".format(network))
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
yield handle_docker(
|
|
291
|
+
resource="network",
|
|
292
|
+
command="create",
|
|
293
|
+
network=network,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@operation(is_idempotent=False)
|
|
298
|
+
def prune(
|
|
299
|
+
all=False,
|
|
300
|
+
volume=False,
|
|
301
|
+
filter="",
|
|
302
|
+
):
|
|
303
|
+
"""
|
|
304
|
+
Execute a docker system prune.
|
|
305
|
+
|
|
306
|
+
+ all: Remove all unused images not just dangling ones
|
|
307
|
+
+ volumes: Prune anonymous volumes
|
|
308
|
+
+ filter: Provide filter values (e.g. "label=<key>=<value>" or "until=24h")
|
|
309
|
+
|
|
310
|
+
**Examples:**
|
|
311
|
+
|
|
312
|
+
.. code:: python
|
|
313
|
+
|
|
314
|
+
# Remove dangling images
|
|
315
|
+
docker.prune(
|
|
316
|
+
name="remove dangling images",
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Remove all images and volumes
|
|
320
|
+
docker.prune(
|
|
321
|
+
name="Remove all images and volumes",
|
|
322
|
+
all=True,
|
|
323
|
+
volumes=True,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
# Remove images older than 90 days
|
|
327
|
+
docker.prune(
|
|
328
|
+
name="Remove unused older than 90 days",
|
|
329
|
+
filter="until=2160h"
|
|
330
|
+
)
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
yield handle_docker(
|
|
334
|
+
resource="system",
|
|
335
|
+
command="prune",
|
|
336
|
+
all=all,
|
|
337
|
+
volume=volume,
|
|
338
|
+
filter=filter,
|
|
339
|
+
)
|
pyinfra/operations/files.py
CHANGED
|
@@ -12,7 +12,7 @@ from datetime import timedelta
|
|
|
12
12
|
from fnmatch import fnmatch
|
|
13
13
|
from io import StringIO
|
|
14
14
|
from pathlib import Path
|
|
15
|
-
from typing import Union
|
|
15
|
+
from typing import IO, Any, Union
|
|
16
16
|
|
|
17
17
|
from jinja2 import TemplateRuntimeError, TemplateSyntaxError, UndefinedError
|
|
18
18
|
|
|
@@ -60,19 +60,19 @@ from .util.files import adjust_regex, ensure_mode_int, get_timestamp, sed_replac
|
|
|
60
60
|
|
|
61
61
|
@operation()
|
|
62
62
|
def download(
|
|
63
|
-
src,
|
|
64
|
-
dest,
|
|
65
|
-
user=None,
|
|
66
|
-
group=None,
|
|
67
|
-
mode=None,
|
|
68
|
-
cache_time=None,
|
|
63
|
+
src: str,
|
|
64
|
+
dest: str,
|
|
65
|
+
user: str | None = None,
|
|
66
|
+
group: str | None = None,
|
|
67
|
+
mode: str | None = None,
|
|
68
|
+
cache_time: int | None = None,
|
|
69
69
|
force=False,
|
|
70
|
-
sha256sum=None,
|
|
71
|
-
sha1sum=None,
|
|
72
|
-
md5sum=None,
|
|
73
|
-
headers=None,
|
|
70
|
+
sha256sum: str | None = None,
|
|
71
|
+
sha1sum: str | None = None,
|
|
72
|
+
md5sum: str | None = None,
|
|
73
|
+
headers: dict[str, str] | None = None,
|
|
74
74
|
insecure=False,
|
|
75
|
-
proxy=None,
|
|
75
|
+
proxy: str | None = None,
|
|
76
76
|
):
|
|
77
77
|
"""
|
|
78
78
|
Download files from remote locations using ``curl`` or ``wget``.
|
|
@@ -123,8 +123,8 @@ def download(
|
|
|
123
123
|
if cache_time:
|
|
124
124
|
# Time on files is not tz-aware, and will be the same tz as the server's time,
|
|
125
125
|
# so we can safely remove the tzinfo from the Date fact before comparison.
|
|
126
|
-
|
|
127
|
-
if info["mtime"] and info["mtime"] <
|
|
126
|
+
ctime = host.get_fact(Date).replace(tzinfo=None) - timedelta(seconds=cache_time)
|
|
127
|
+
if info["mtime"] and info["mtime"] < ctime:
|
|
128
128
|
download = True
|
|
129
129
|
|
|
130
130
|
if sha1sum:
|
|
@@ -227,11 +227,11 @@ def download(
|
|
|
227
227
|
|
|
228
228
|
@operation()
|
|
229
229
|
def line(
|
|
230
|
-
path,
|
|
231
|
-
line,
|
|
230
|
+
path: str,
|
|
231
|
+
line: str,
|
|
232
232
|
present=True,
|
|
233
|
-
replace=None,
|
|
234
|
-
flags=None,
|
|
233
|
+
replace: str | None = None,
|
|
234
|
+
flags: list[str] | None = None,
|
|
235
235
|
backup=False,
|
|
236
236
|
interpolate_variables=False,
|
|
237
237
|
escape_regex_characters=False,
|
|
@@ -269,7 +269,7 @@ def line(
|
|
|
269
269
|
it will be append to the end of the file.
|
|
270
270
|
|
|
271
271
|
Ensure new line:
|
|
272
|
-
This will ensure that the ``line`` being appended is always on a
|
|
272
|
+
This will ensure that the ``line`` being appended is always on a separate new
|
|
273
273
|
line in case the file doesn't end with a newline character.
|
|
274
274
|
|
|
275
275
|
|
|
@@ -427,10 +427,10 @@ def line(
|
|
|
427
427
|
|
|
428
428
|
@operation()
|
|
429
429
|
def replace(
|
|
430
|
-
path,
|
|
431
|
-
text=None,
|
|
432
|
-
replace=None,
|
|
433
|
-
flags=None,
|
|
430
|
+
path: str,
|
|
431
|
+
text: str | None = None,
|
|
432
|
+
replace: str | None = None,
|
|
433
|
+
flags: list[str] | None = None,
|
|
434
434
|
backup=False,
|
|
435
435
|
interpolate_variables=False,
|
|
436
436
|
match=None, # deprecated
|
|
@@ -501,15 +501,15 @@ def replace(
|
|
|
501
501
|
|
|
502
502
|
@operation()
|
|
503
503
|
def sync(
|
|
504
|
-
src,
|
|
505
|
-
dest,
|
|
506
|
-
user=None,
|
|
507
|
-
group=None,
|
|
508
|
-
mode=None,
|
|
509
|
-
dir_mode=None,
|
|
504
|
+
src: str,
|
|
505
|
+
dest: str,
|
|
506
|
+
user: str | None = None,
|
|
507
|
+
group: str | None = None,
|
|
508
|
+
mode: str | None = None,
|
|
509
|
+
dir_mode: str | None = None,
|
|
510
510
|
delete=False,
|
|
511
|
-
exclude=None,
|
|
512
|
-
exclude_dir=None,
|
|
511
|
+
exclude: str | list[str] | tuple[str] | None = None,
|
|
512
|
+
exclude_dir: str | list[str] | tuple[str] | None = None,
|
|
513
513
|
add_deploy_dir=True,
|
|
514
514
|
):
|
|
515
515
|
"""
|
|
@@ -652,7 +652,7 @@ def show_rsync_warning():
|
|
|
652
652
|
|
|
653
653
|
|
|
654
654
|
@operation(is_idempotent=False)
|
|
655
|
-
def rsync(src, dest, flags
|
|
655
|
+
def rsync(src: str, dest: str, flags: list[str] | None = None):
|
|
656
656
|
"""
|
|
657
657
|
Use ``rsync`` to sync a local directory to the remote system. This operation will actually call
|
|
658
658
|
the ``rsync`` binary on your system.
|
|
@@ -667,6 +667,8 @@ def rsync(src, dest, flags=["-ax", "--delete"]):
|
|
|
667
667
|
global arguments.
|
|
668
668
|
"""
|
|
669
669
|
|
|
670
|
+
if flags is None:
|
|
671
|
+
flags = ["-ax", "--delete"]
|
|
670
672
|
show_rsync_warning()
|
|
671
673
|
|
|
672
674
|
try:
|
|
@@ -696,8 +698,8 @@ def _create_remote_dir(remote_filename, user, group):
|
|
|
696
698
|
is_idempotent=False,
|
|
697
699
|
)
|
|
698
700
|
def get(
|
|
699
|
-
src,
|
|
700
|
-
dest,
|
|
701
|
+
src: str,
|
|
702
|
+
dest: str,
|
|
701
703
|
add_deploy_dir=True,
|
|
702
704
|
create_local_dir=False,
|
|
703
705
|
force=False,
|
|
@@ -756,11 +758,11 @@ def get(
|
|
|
756
758
|
|
|
757
759
|
@operation()
|
|
758
760
|
def put(
|
|
759
|
-
src,
|
|
760
|
-
dest,
|
|
761
|
-
user=None,
|
|
762
|
-
group=None,
|
|
763
|
-
mode=None,
|
|
761
|
+
src: str | IO[Any],
|
|
762
|
+
dest: str,
|
|
763
|
+
user: str | None = None,
|
|
764
|
+
group: str | None = None,
|
|
765
|
+
mode: int | str | bool | None = None,
|
|
764
766
|
add_deploy_dir=True,
|
|
765
767
|
create_remote_dir=True,
|
|
766
768
|
force=False,
|
|
@@ -821,6 +823,7 @@ def put(
|
|
|
821
823
|
|
|
822
824
|
# Assume string filename
|
|
823
825
|
else:
|
|
826
|
+
assert isinstance(src, (str, Path))
|
|
824
827
|
# Add deploy directory?
|
|
825
828
|
if add_deploy_dir and state.cwd:
|
|
826
829
|
src = os.path.join(state.cwd, src)
|
|
@@ -835,7 +838,7 @@ def put(
|
|
|
835
838
|
raise IOError("No such file: {0}".format(local_file))
|
|
836
839
|
|
|
837
840
|
if mode is True:
|
|
838
|
-
if os.path.isfile(local_file):
|
|
841
|
+
if isinstance(local_file, str) and os.path.isfile(local_file):
|
|
839
842
|
mode = get_path_permissions_mode(local_file)
|
|
840
843
|
else:
|
|
841
844
|
logger.warning(
|
|
@@ -849,6 +852,7 @@ def put(
|
|
|
849
852
|
remote_file = host.get_fact(File, path=dest)
|
|
850
853
|
|
|
851
854
|
if not remote_file and bool(host.get_fact(Directory, path=dest)):
|
|
855
|
+
assert isinstance(src, str)
|
|
852
856
|
dest = unix_path_join(dest, os.path.basename(src))
|
|
853
857
|
remote_file = host.get_fact(File, path=dest)
|
|
854
858
|
|
|
@@ -905,7 +909,15 @@ def put(
|
|
|
905
909
|
|
|
906
910
|
|
|
907
911
|
@operation()
|
|
908
|
-
def template(
|
|
912
|
+
def template(
|
|
913
|
+
src: str | IO[Any],
|
|
914
|
+
dest: str,
|
|
915
|
+
user: str | None = None,
|
|
916
|
+
group: str | None = None,
|
|
917
|
+
mode: str | None = None,
|
|
918
|
+
create_remote_dir=True,
|
|
919
|
+
**data,
|
|
920
|
+
):
|
|
909
921
|
'''
|
|
910
922
|
Generate a template using jinja2 and write it to the remote system.
|
|
911
923
|
|
|
@@ -1055,16 +1067,16 @@ def _raise_or_remove_invalid_path(fs_type, path, force, force_backup, force_back
|
|
|
1055
1067
|
|
|
1056
1068
|
@operation()
|
|
1057
1069
|
def link(
|
|
1058
|
-
path,
|
|
1059
|
-
target=None,
|
|
1070
|
+
path: str,
|
|
1071
|
+
target: str | None = None,
|
|
1060
1072
|
present=True,
|
|
1061
|
-
user=None,
|
|
1062
|
-
group=None,
|
|
1073
|
+
user: str | None = None,
|
|
1074
|
+
group: str | None = None,
|
|
1063
1075
|
symbolic=True,
|
|
1064
1076
|
create_remote_dir=True,
|
|
1065
1077
|
force=False,
|
|
1066
1078
|
force_backup=True,
|
|
1067
|
-
force_backup_dir=None,
|
|
1079
|
+
force_backup_dir: str | None = None,
|
|
1068
1080
|
):
|
|
1069
1081
|
"""
|
|
1070
1082
|
Add/remove/update links.
|
|
@@ -1162,16 +1174,16 @@ def link(
|
|
|
1162
1174
|
|
|
1163
1175
|
@operation()
|
|
1164
1176
|
def file(
|
|
1165
|
-
path,
|
|
1177
|
+
path: str,
|
|
1166
1178
|
present=True,
|
|
1167
|
-
user=None,
|
|
1168
|
-
group=None,
|
|
1169
|
-
mode=None,
|
|
1179
|
+
user: str | None = None,
|
|
1180
|
+
group: str | None = None,
|
|
1181
|
+
mode: int | str | None = None,
|
|
1170
1182
|
touch=False,
|
|
1171
1183
|
create_remote_dir=True,
|
|
1172
1184
|
force=False,
|
|
1173
1185
|
force_backup=True,
|
|
1174
|
-
force_backup_dir=None,
|
|
1186
|
+
force_backup_dir: str | None = None,
|
|
1175
1187
|
):
|
|
1176
1188
|
"""
|
|
1177
1189
|
Add/remove/update files.
|
|
@@ -1264,15 +1276,15 @@ def file(
|
|
|
1264
1276
|
|
|
1265
1277
|
@operation()
|
|
1266
1278
|
def directory(
|
|
1267
|
-
path,
|
|
1279
|
+
path: str,
|
|
1268
1280
|
present=True,
|
|
1269
|
-
user=None,
|
|
1270
|
-
group=None,
|
|
1271
|
-
mode=None,
|
|
1281
|
+
user: str | None = None,
|
|
1282
|
+
group: str | None = None,
|
|
1283
|
+
mode: int | str | None = None,
|
|
1272
1284
|
recursive=False,
|
|
1273
1285
|
force=False,
|
|
1274
1286
|
force_backup=True,
|
|
1275
|
-
force_backup_dir=None,
|
|
1287
|
+
force_backup_dir: str | None = None,
|
|
1276
1288
|
_no_check_owner_mode=False,
|
|
1277
1289
|
_no_fail_on_link=False,
|
|
1278
1290
|
):
|
|
@@ -1365,7 +1377,7 @@ def directory(
|
|
|
1365
1377
|
|
|
1366
1378
|
|
|
1367
1379
|
@operation()
|
|
1368
|
-
def flags(path, flags=None, present=True):
|
|
1380
|
+
def flags(path: str, flags: list[str] | None = None, present=True):
|
|
1369
1381
|
"""
|
|
1370
1382
|
Set/clear file flags.
|
|
1371
1383
|
|
|
@@ -1414,18 +1426,18 @@ def flags(path, flags=None, present=True):
|
|
|
1414
1426
|
|
|
1415
1427
|
@operation()
|
|
1416
1428
|
def block(
|
|
1417
|
-
path,
|
|
1418
|
-
content=None,
|
|
1429
|
+
path: str,
|
|
1430
|
+
content: str | list[str] | None = None,
|
|
1419
1431
|
present=True,
|
|
1420
|
-
line=None,
|
|
1432
|
+
line: str | None = None,
|
|
1421
1433
|
backup=False,
|
|
1422
1434
|
escape_regex_characters=False,
|
|
1423
1435
|
try_prevent_shell_expansion=False,
|
|
1424
1436
|
before=False,
|
|
1425
1437
|
after=False,
|
|
1426
|
-
marker=None,
|
|
1427
|
-
begin=None,
|
|
1428
|
-
end=None,
|
|
1438
|
+
marker: str | None = None,
|
|
1439
|
+
begin: str | None = None,
|
|
1440
|
+
end: str | None = None,
|
|
1429
1441
|
):
|
|
1430
1442
|
"""
|
|
1431
1443
|
Ensure content, surrounded by the appropriate markers, is present (or not) in the file.
|
|
@@ -1573,6 +1585,7 @@ def block(
|
|
|
1573
1585
|
f"\n{the_block}\n{here}",
|
|
1574
1586
|
)
|
|
1575
1587
|
elif current == []: # markers not found and have a pattern to match (not start or end)
|
|
1588
|
+
assert isinstance(line, str)
|
|
1576
1589
|
regex = adjust_regex(line, escape_regex_characters)
|
|
1577
1590
|
print_before = "{ print }" if before else ""
|
|
1578
1591
|
print_after = "{ print }" if after else ""
|
|
@@ -1601,9 +1614,11 @@ def block(
|
|
|
1601
1614
|
out_prep,
|
|
1602
1615
|
prog,
|
|
1603
1616
|
q_path,
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1617
|
+
(
|
|
1618
|
+
'"' + "\n".join(content) + '"'
|
|
1619
|
+
if not try_prevent_shell_expansion
|
|
1620
|
+
else "'" + "\n".join(content) + "'"
|
|
1621
|
+
),
|
|
1607
1622
|
"> $OUT &&",
|
|
1608
1623
|
real_out,
|
|
1609
1624
|
)
|
pyinfra/operations/gem.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Manage Ruby gem packages. (see https://rubygems.org/ )
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
5
7
|
from pyinfra import host
|
|
6
8
|
from pyinfra.api import operation
|
|
7
9
|
from pyinfra.facts.gem import GemPackages
|
|
@@ -10,7 +12,7 @@ from .util.packaging import ensure_packages
|
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
@operation()
|
|
13
|
-
def packages(packages=None, present=True, latest=False):
|
|
15
|
+
def packages(packages: str | list[str] | None = None, present=True, latest=False):
|
|
14
16
|
"""
|
|
15
17
|
Add/remove/update gem packages.
|
|
16
18
|
|