pyinfra 0.11.dev3__py3-none-any.whl → 3.6__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/__init__.py +9 -12
- pyinfra/__main__.py +4 -0
- pyinfra/api/__init__.py +19 -3
- pyinfra/api/arguments.py +413 -0
- pyinfra/api/arguments_typed.py +79 -0
- pyinfra/api/command.py +274 -0
- pyinfra/api/config.py +222 -28
- pyinfra/api/connect.py +33 -13
- pyinfra/api/connectors.py +27 -0
- pyinfra/api/deploy.py +65 -66
- pyinfra/api/exceptions.py +73 -18
- pyinfra/api/facts.py +267 -200
- pyinfra/api/host.py +416 -50
- pyinfra/api/inventory.py +121 -160
- pyinfra/api/metadata.py +69 -0
- pyinfra/api/operation.py +432 -262
- pyinfra/api/operations.py +273 -260
- pyinfra/api/state.py +302 -248
- pyinfra/api/util.py +309 -369
- pyinfra/connectors/base.py +173 -0
- pyinfra/connectors/chroot.py +212 -0
- pyinfra/connectors/docker.py +405 -0
- pyinfra/connectors/dockerssh.py +297 -0
- pyinfra/connectors/local.py +238 -0
- pyinfra/connectors/scp/__init__.py +1 -0
- pyinfra/connectors/scp/client.py +204 -0
- pyinfra/connectors/ssh.py +727 -0
- pyinfra/connectors/ssh_util.py +114 -0
- pyinfra/connectors/sshuserclient/client.py +309 -0
- pyinfra/connectors/sshuserclient/config.py +102 -0
- pyinfra/connectors/terraform.py +135 -0
- pyinfra/connectors/util.py +417 -0
- pyinfra/connectors/vagrant.py +183 -0
- pyinfra/context.py +145 -0
- pyinfra/facts/__init__.py +7 -6
- pyinfra/facts/apk.py +22 -7
- pyinfra/facts/apt.py +117 -60
- pyinfra/facts/brew.py +100 -15
- pyinfra/facts/bsdinit.py +23 -0
- pyinfra/facts/cargo.py +37 -0
- pyinfra/facts/choco.py +47 -0
- pyinfra/facts/crontab.py +195 -0
- pyinfra/facts/deb.py +94 -0
- pyinfra/facts/dnf.py +48 -0
- pyinfra/facts/docker.py +96 -23
- pyinfra/facts/efibootmgr.py +113 -0
- pyinfra/facts/files.py +629 -58
- pyinfra/facts/flatpak.py +77 -0
- pyinfra/facts/freebsd.py +70 -0
- pyinfra/facts/gem.py +19 -6
- pyinfra/facts/git.py +59 -14
- pyinfra/facts/gpg.py +150 -0
- pyinfra/facts/hardware.py +313 -167
- pyinfra/facts/iptables.py +72 -62
- pyinfra/facts/launchd.py +44 -0
- pyinfra/facts/lxd.py +17 -4
- pyinfra/facts/mysql.py +122 -86
- pyinfra/facts/npm.py +17 -9
- pyinfra/facts/openrc.py +71 -0
- pyinfra/facts/opkg.py +246 -0
- pyinfra/facts/pacman.py +50 -7
- pyinfra/facts/pip.py +24 -7
- pyinfra/facts/pipx.py +82 -0
- pyinfra/facts/pkg.py +15 -6
- pyinfra/facts/pkgin.py +35 -0
- pyinfra/facts/podman.py +54 -0
- pyinfra/facts/postgres.py +178 -0
- pyinfra/facts/postgresql.py +6 -147
- pyinfra/facts/rpm.py +105 -0
- pyinfra/facts/runit.py +77 -0
- pyinfra/facts/selinux.py +161 -0
- pyinfra/facts/server.py +762 -285
- pyinfra/facts/snap.py +88 -0
- pyinfra/facts/systemd.py +139 -0
- pyinfra/facts/sysvinit.py +59 -0
- pyinfra/facts/upstart.py +35 -0
- pyinfra/facts/util/__init__.py +17 -0
- pyinfra/facts/util/databases.py +4 -6
- pyinfra/facts/util/packaging.py +37 -6
- pyinfra/facts/util/units.py +30 -0
- pyinfra/facts/util/win_files.py +99 -0
- pyinfra/facts/vzctl.py +20 -13
- pyinfra/facts/xbps.py +35 -0
- pyinfra/facts/yum.py +34 -40
- pyinfra/facts/zfs.py +77 -0
- pyinfra/facts/zypper.py +42 -0
- pyinfra/local.py +45 -83
- pyinfra/operations/__init__.py +12 -0
- pyinfra/operations/apk.py +99 -0
- pyinfra/operations/apt.py +496 -0
- pyinfra/operations/brew.py +232 -0
- pyinfra/operations/bsdinit.py +59 -0
- pyinfra/operations/cargo.py +45 -0
- pyinfra/operations/choco.py +61 -0
- pyinfra/operations/crontab.py +194 -0
- pyinfra/operations/dnf.py +213 -0
- pyinfra/operations/docker.py +492 -0
- pyinfra/operations/files.py +2014 -0
- pyinfra/operations/flatpak.py +95 -0
- 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/gem.py +48 -0
- pyinfra/operations/git.py +420 -0
- pyinfra/operations/iptables.py +312 -0
- pyinfra/operations/launchd.py +45 -0
- pyinfra/operations/lxd.py +69 -0
- pyinfra/operations/mysql.py +610 -0
- pyinfra/operations/npm.py +57 -0
- pyinfra/operations/openrc.py +63 -0
- pyinfra/operations/opkg.py +89 -0
- pyinfra/operations/pacman.py +82 -0
- pyinfra/operations/pip.py +206 -0
- pyinfra/operations/pipx.py +103 -0
- pyinfra/operations/pkg.py +71 -0
- pyinfra/operations/pkgin.py +92 -0
- pyinfra/operations/postgres.py +437 -0
- pyinfra/operations/postgresql.py +30 -0
- pyinfra/operations/puppet.py +41 -0
- pyinfra/operations/python.py +73 -0
- pyinfra/operations/runit.py +184 -0
- pyinfra/operations/selinux.py +190 -0
- pyinfra/operations/server.py +1100 -0
- pyinfra/operations/snap.py +118 -0
- pyinfra/operations/ssh.py +217 -0
- pyinfra/operations/systemd.py +150 -0
- pyinfra/operations/sysvinit.py +142 -0
- pyinfra/operations/upstart.py +68 -0
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/docker.py +407 -0
- pyinfra/operations/util/files.py +247 -0
- pyinfra/operations/util/packaging.py +338 -0
- pyinfra/operations/util/service.py +46 -0
- pyinfra/operations/vzctl.py +137 -0
- pyinfra/operations/xbps.py +78 -0
- pyinfra/operations/yum.py +213 -0
- pyinfra/operations/zfs.py +176 -0
- pyinfra/operations/zypper.py +193 -0
- pyinfra/progress.py +44 -32
- pyinfra/py.typed +0 -0
- pyinfra/version.py +9 -1
- pyinfra-3.6.dist-info/METADATA +142 -0
- pyinfra-3.6.dist-info/RECORD +160 -0
- {pyinfra-0.11.dev3.dist-info → pyinfra-3.6.dist-info}/WHEEL +1 -2
- pyinfra-3.6.dist-info/entry_points.txt +12 -0
- {pyinfra-0.11.dev3.dist-info → pyinfra-3.6.dist-info/licenses}/LICENSE.md +1 -1
- pyinfra_cli/__init__.py +1 -0
- pyinfra_cli/cli.py +793 -0
- pyinfra_cli/commands.py +66 -0
- pyinfra_cli/exceptions.py +155 -65
- pyinfra_cli/inventory.py +233 -89
- pyinfra_cli/log.py +39 -43
- pyinfra_cli/main.py +26 -495
- pyinfra_cli/prints.py +215 -156
- pyinfra_cli/util.py +172 -105
- pyinfra_cli/virtualenv.py +25 -20
- pyinfra/api/connectors/__init__.py +0 -21
- pyinfra/api/connectors/ansible.py +0 -99
- pyinfra/api/connectors/docker.py +0 -178
- pyinfra/api/connectors/local.py +0 -169
- pyinfra/api/connectors/ssh.py +0 -402
- pyinfra/api/connectors/sshuserclient/client.py +0 -105
- pyinfra/api/connectors/sshuserclient/config.py +0 -90
- pyinfra/api/connectors/util.py +0 -63
- pyinfra/api/connectors/vagrant.py +0 -155
- pyinfra/facts/init.py +0 -176
- pyinfra/facts/util/files.py +0 -102
- pyinfra/hook.py +0 -41
- pyinfra/modules/__init__.py +0 -11
- pyinfra/modules/apk.py +0 -64
- pyinfra/modules/apt.py +0 -272
- pyinfra/modules/brew.py +0 -122
- pyinfra/modules/files.py +0 -711
- pyinfra/modules/gem.py +0 -30
- pyinfra/modules/git.py +0 -115
- pyinfra/modules/init.py +0 -344
- pyinfra/modules/iptables.py +0 -271
- pyinfra/modules/lxd.py +0 -45
- pyinfra/modules/mysql.py +0 -347
- pyinfra/modules/npm.py +0 -47
- pyinfra/modules/pacman.py +0 -60
- pyinfra/modules/pip.py +0 -99
- pyinfra/modules/pkg.py +0 -43
- pyinfra/modules/postgresql.py +0 -245
- pyinfra/modules/puppet.py +0 -20
- pyinfra/modules/python.py +0 -37
- pyinfra/modules/server.py +0 -524
- pyinfra/modules/ssh.py +0 -150
- pyinfra/modules/util/files.py +0 -52
- pyinfra/modules/util/packaging.py +0 -118
- pyinfra/modules/vzctl.py +0 -133
- pyinfra/modules/yum.py +0 -171
- pyinfra/pseudo_modules.py +0 -64
- pyinfra-0.11.dev3.dist-info/.DS_Store +0 -0
- pyinfra-0.11.dev3.dist-info/METADATA +0 -135
- pyinfra-0.11.dev3.dist-info/RECORD +0 -95
- pyinfra-0.11.dev3.dist-info/entry_points.txt +0 -3
- pyinfra-0.11.dev3.dist-info/top_level.txt +0 -2
- pyinfra_cli/__main__.py +0 -40
- pyinfra_cli/config.py +0 -92
- /pyinfra/{modules/util → connectors}/__init__.py +0 -0
- /pyinfra/{api/connectors → connectors}/sshuserclient/__init__.py +0 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage brew packages on mac/OSX. See https://brew.sh/
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import urllib.parse
|
|
8
|
+
|
|
9
|
+
from pyinfra import host
|
|
10
|
+
from pyinfra.api import operation
|
|
11
|
+
from pyinfra.facts.brew import BrewCasks, BrewPackages, BrewTaps, BrewVersion, new_cask_cli
|
|
12
|
+
|
|
13
|
+
from .util.packaging import ensure_packages
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@operation(is_idempotent=False)
|
|
17
|
+
def update():
|
|
18
|
+
"""
|
|
19
|
+
Updates brew repositories.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
yield "brew update"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_update = update # noqa: E305
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@operation(is_idempotent=False)
|
|
29
|
+
def upgrade():
|
|
30
|
+
"""
|
|
31
|
+
Upgrades all brew packages.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
yield "brew upgrade"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
_upgrade = upgrade # noqa: E305
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@operation()
|
|
41
|
+
def packages(
|
|
42
|
+
packages: str | list[str] | None = None,
|
|
43
|
+
present=True,
|
|
44
|
+
latest=False,
|
|
45
|
+
update=False,
|
|
46
|
+
upgrade=False,
|
|
47
|
+
):
|
|
48
|
+
"""
|
|
49
|
+
Add/remove/update brew packages.
|
|
50
|
+
|
|
51
|
+
+ packages: list of packages to ensure
|
|
52
|
+
+ present: whether the packages should be installed
|
|
53
|
+
+ latest: whether to upgrade packages without a specified version
|
|
54
|
+
+ update: run ``brew update`` before installing packages
|
|
55
|
+
+ upgrade: run ``brew upgrade`` before installing packages
|
|
56
|
+
|
|
57
|
+
Versions:
|
|
58
|
+
Package versions can be pinned like brew: ``<pkg>@<version>``.
|
|
59
|
+
|
|
60
|
+
**Examples:**
|
|
61
|
+
|
|
62
|
+
.. code:: python
|
|
63
|
+
|
|
64
|
+
from pyinfra.operations import brew
|
|
65
|
+
# Update package list and install packages
|
|
66
|
+
brew.packages(
|
|
67
|
+
name='Install Vim and vimpager',
|
|
68
|
+
packages=["vimpager", "vim"],
|
|
69
|
+
update=True,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Install the latest versions of packages (always check)
|
|
73
|
+
brew.packages(
|
|
74
|
+
name="Install latest Vim",
|
|
75
|
+
packages=["vim"],
|
|
76
|
+
latest=True,
|
|
77
|
+
)
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
if update:
|
|
81
|
+
yield from _update._inner()
|
|
82
|
+
|
|
83
|
+
if upgrade:
|
|
84
|
+
yield from _upgrade._inner()
|
|
85
|
+
|
|
86
|
+
yield from ensure_packages(
|
|
87
|
+
host,
|
|
88
|
+
packages,
|
|
89
|
+
host.get_fact(BrewPackages),
|
|
90
|
+
present,
|
|
91
|
+
install_command="brew install",
|
|
92
|
+
uninstall_command="brew uninstall",
|
|
93
|
+
upgrade_command="brew upgrade",
|
|
94
|
+
version_join="@",
|
|
95
|
+
latest=latest,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def cask_args():
|
|
100
|
+
return ("", " --cask") if new_cask_cli(host.get_fact(BrewVersion)) else ("cask ", "")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@operation(is_idempotent=False)
|
|
104
|
+
def cask_upgrade():
|
|
105
|
+
"""
|
|
106
|
+
Upgrades all brew casks.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
yield "brew %supgrade%s" % cask_args()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@operation()
|
|
113
|
+
def casks(
|
|
114
|
+
casks: str | list[str] | None = None,
|
|
115
|
+
present=True,
|
|
116
|
+
latest=False,
|
|
117
|
+
upgrade=False,
|
|
118
|
+
):
|
|
119
|
+
"""
|
|
120
|
+
Add/remove/update brew casks.
|
|
121
|
+
|
|
122
|
+
+ casks: list of casks to ensure
|
|
123
|
+
+ present: whether the casks should be installed
|
|
124
|
+
+ latest: whether to upgrade casks without a specified version
|
|
125
|
+
+ upgrade: run brew cask upgrade before installing casks
|
|
126
|
+
|
|
127
|
+
Versions:
|
|
128
|
+
Cask versions can be pinned like brew: ``<pkg>@<version>``.
|
|
129
|
+
|
|
130
|
+
**Example:**
|
|
131
|
+
|
|
132
|
+
.. code:: python
|
|
133
|
+
|
|
134
|
+
brew.casks(
|
|
135
|
+
name='Upgrade and install the latest cask',
|
|
136
|
+
casks=["godot"],
|
|
137
|
+
upgrade=True,
|
|
138
|
+
latest=True,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
if upgrade:
|
|
144
|
+
yield from cask_upgrade._inner()
|
|
145
|
+
|
|
146
|
+
args = cask_args()
|
|
147
|
+
|
|
148
|
+
yield from ensure_packages(
|
|
149
|
+
host,
|
|
150
|
+
casks,
|
|
151
|
+
host.get_fact(BrewCasks),
|
|
152
|
+
present,
|
|
153
|
+
install_command="brew %sinstall%s" % args,
|
|
154
|
+
uninstall_command="brew %suninstall%s" % args,
|
|
155
|
+
upgrade_command="brew %supgrade%s" % args,
|
|
156
|
+
version_join="@",
|
|
157
|
+
latest=latest,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@operation()
|
|
162
|
+
def tap(src: str | None = None, present=True, url: str | None = None):
|
|
163
|
+
"""
|
|
164
|
+
Add/remove brew taps.
|
|
165
|
+
|
|
166
|
+
+ src: the name of the tap
|
|
167
|
+
+ present: whether this tap should be present or not
|
|
168
|
+
+ url: the url of the tap. See https://docs.brew.sh/Taps
|
|
169
|
+
|
|
170
|
+
**Examples:**
|
|
171
|
+
|
|
172
|
+
.. code:: python
|
|
173
|
+
|
|
174
|
+
brew.tap(
|
|
175
|
+
name="Add a brew tap",
|
|
176
|
+
src="includeos/includeos",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Just url is equivalent to
|
|
180
|
+
# `brew tap kptdev/kpt https://github.com/kptdev/kpt`
|
|
181
|
+
brew.tap(
|
|
182
|
+
url="https://github.com/kptdev/kpt",
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# src and url is equivalent to
|
|
186
|
+
# `brew tap example/project https://github.example.com/project`
|
|
187
|
+
brew.tap(
|
|
188
|
+
src="example/project",
|
|
189
|
+
url="https://github.example.com/project",
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Multiple taps
|
|
193
|
+
for tap in ["includeos/includeos", "ktr0731/evans"]:
|
|
194
|
+
brew.tap(
|
|
195
|
+
name={f"Add brew tap {tap}"},
|
|
196
|
+
src=tap,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
if not (src or url):
|
|
202
|
+
host.noop("no tap was specified")
|
|
203
|
+
return
|
|
204
|
+
|
|
205
|
+
src = src or str(urllib.parse.urlparse(url).path).strip("/")
|
|
206
|
+
|
|
207
|
+
if len(src.split("/")) != 2:
|
|
208
|
+
host.noop("src '{0}' doesn't have two components.".format(src))
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
taps = host.get_fact(BrewTaps)
|
|
212
|
+
already_tapped = src in taps
|
|
213
|
+
|
|
214
|
+
if present and already_tapped:
|
|
215
|
+
host.noop("tap {0} already exists".format(src))
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
if already_tapped:
|
|
219
|
+
yield "brew untap {0}".format(src)
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
if not present:
|
|
223
|
+
host.noop("tap {0} does not exist".format(src))
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
cmd = "brew tap {0}".format(src)
|
|
227
|
+
|
|
228
|
+
if url is not None:
|
|
229
|
+
cmd = " ".join([cmd, url])
|
|
230
|
+
|
|
231
|
+
yield cmd
|
|
232
|
+
return
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage BSD init services (``/etc/rc.d``, ``/usr/local/etc/rc.d``).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pyinfra import host
|
|
8
|
+
from pyinfra.api import operation
|
|
9
|
+
from pyinfra.facts.bsdinit import RcdStatus
|
|
10
|
+
from pyinfra.facts.server import Os
|
|
11
|
+
|
|
12
|
+
from . import files
|
|
13
|
+
from .util.service import handle_service_control
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@operation()
|
|
17
|
+
def service(
|
|
18
|
+
service: str,
|
|
19
|
+
running=True,
|
|
20
|
+
restarted=False,
|
|
21
|
+
reloaded=False,
|
|
22
|
+
command: str | None = None,
|
|
23
|
+
enabled: bool | None = None,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Manage the state of BSD init services.
|
|
27
|
+
|
|
28
|
+
+ service: name of the service to manage
|
|
29
|
+
+ running: whether the service should be running
|
|
30
|
+
+ restarted: whether the service should be restarted
|
|
31
|
+
+ reloaded: whether the service should be reloaded
|
|
32
|
+
+ command: custom command to pass like: ``/etc/rc.d/<service> <command>``
|
|
33
|
+
+ enabled: whether this service should be enabled/disabled on boot
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
status_argument = "status"
|
|
37
|
+
if host.get_fact(Os) == "OpenBSD":
|
|
38
|
+
status_argument = "check"
|
|
39
|
+
|
|
40
|
+
yield from handle_service_control(
|
|
41
|
+
host,
|
|
42
|
+
service,
|
|
43
|
+
host.get_fact(RcdStatus),
|
|
44
|
+
"test -e /etc/rc.d/{0} && /etc/rc.d/{0} {1} || /usr/local/etc/rc.d/{0} {1}",
|
|
45
|
+
running,
|
|
46
|
+
restarted,
|
|
47
|
+
reloaded,
|
|
48
|
+
command,
|
|
49
|
+
status_argument=status_argument,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# BSD init is simple, just add/remove <service>_enabled="YES"
|
|
53
|
+
if isinstance(enabled, bool):
|
|
54
|
+
yield from files.line._inner(
|
|
55
|
+
path="/etc/rc.conf.local",
|
|
56
|
+
line="^{0}_enable=".format(service),
|
|
57
|
+
replace='{0}_enable="YES"'.format(service),
|
|
58
|
+
present=enabled,
|
|
59
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage cargo (aka Rust) packages.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pyinfra import host
|
|
8
|
+
from pyinfra.api import operation
|
|
9
|
+
from pyinfra.facts.cargo import CargoPackages
|
|
10
|
+
|
|
11
|
+
from .util.packaging import ensure_packages
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@operation()
|
|
15
|
+
def packages(packages: str | list[str] | None = None, present=True, latest=False):
|
|
16
|
+
"""
|
|
17
|
+
Install/remove/update cargo packages.
|
|
18
|
+
|
|
19
|
+
+ packages: list of packages to ensure
|
|
20
|
+
+ present: whether the packages should be present
|
|
21
|
+
+ latest: whether to upgrade packages without a specified version
|
|
22
|
+
|
|
23
|
+
Versions:
|
|
24
|
+
Package versions can be pinned like cargo: ``<pkg>@<version>``.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
current_packages = host.get_fact(CargoPackages)
|
|
28
|
+
|
|
29
|
+
install_command = "cargo install"
|
|
30
|
+
|
|
31
|
+
uninstall_command = "cargo uninstall"
|
|
32
|
+
|
|
33
|
+
upgrade_command = "cargo install"
|
|
34
|
+
|
|
35
|
+
yield from ensure_packages(
|
|
36
|
+
host,
|
|
37
|
+
packages,
|
|
38
|
+
current_packages,
|
|
39
|
+
present,
|
|
40
|
+
install_command=install_command,
|
|
41
|
+
uninstall_command=uninstall_command,
|
|
42
|
+
upgrade_command=upgrade_command,
|
|
43
|
+
version_join="@",
|
|
44
|
+
latest=latest,
|
|
45
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage ``choco`` (Chocolatey) packages (https://chocolatey.org).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pyinfra import host
|
|
8
|
+
from pyinfra.api import operation
|
|
9
|
+
from pyinfra.facts.choco import ChocoPackages
|
|
10
|
+
|
|
11
|
+
from .util.packaging import ensure_packages
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@operation()
|
|
15
|
+
def packages(packages: str | list[str] | None = None, present=True, latest=False):
|
|
16
|
+
"""
|
|
17
|
+
Add/remove/update ``choco`` packages.
|
|
18
|
+
|
|
19
|
+
+ packages: list of packages to ensure
|
|
20
|
+
+ present: whether the packages should be installed
|
|
21
|
+
+ latest: whether to upgrade packages without a specified version
|
|
22
|
+
|
|
23
|
+
Versions:
|
|
24
|
+
Package versions can be pinned like gem: ``<pkg>:<version>``.
|
|
25
|
+
|
|
26
|
+
**Example:**
|
|
27
|
+
|
|
28
|
+
.. code:: python
|
|
29
|
+
|
|
30
|
+
# Note: Assumes that 'choco' is installed and
|
|
31
|
+
# user has Administrator permission.
|
|
32
|
+
choco.packages(
|
|
33
|
+
name="Install Notepad++",
|
|
34
|
+
packages=["notepadplusplus"],
|
|
35
|
+
)
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
yield from ensure_packages(
|
|
39
|
+
host,
|
|
40
|
+
packages,
|
|
41
|
+
host.get_fact(ChocoPackages),
|
|
42
|
+
present,
|
|
43
|
+
install_command="choco install -y",
|
|
44
|
+
uninstall_command="choco uninstall -y -x",
|
|
45
|
+
upgrade_command="choco update -y",
|
|
46
|
+
version_join=":",
|
|
47
|
+
latest=latest,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@operation(is_idempotent=False)
|
|
52
|
+
def install():
|
|
53
|
+
"""
|
|
54
|
+
Install ``choco`` (Chocolatey).
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
yield (
|
|
58
|
+
"Set-ExecutionPolicy Bypass -Scope Process -Force ;"
|
|
59
|
+
"iex ((New-Object System.Net.WebClient).DownloadString"
|
|
60
|
+
'("https://chocolatey.org/install.ps1"))'
|
|
61
|
+
) # noqa
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shlex
|
|
4
|
+
|
|
5
|
+
from pyinfra import logger
|
|
6
|
+
from pyinfra.api.command import StringCommand
|
|
7
|
+
from pyinfra.api.operation import operation
|
|
8
|
+
from pyinfra.api.util import try_int
|
|
9
|
+
from pyinfra.context import host
|
|
10
|
+
from pyinfra.facts.crontab import Crontab, CrontabFile
|
|
11
|
+
from pyinfra.operations.util.files import sed_delete, sed_replace
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@operation()
|
|
15
|
+
def crontab(
|
|
16
|
+
command: str,
|
|
17
|
+
present=True,
|
|
18
|
+
user: str | None = None,
|
|
19
|
+
cron_name: str | None = None,
|
|
20
|
+
minute="*",
|
|
21
|
+
hour="*",
|
|
22
|
+
month="*",
|
|
23
|
+
day_of_week="*",
|
|
24
|
+
day_of_month="*",
|
|
25
|
+
special_time: str | None = None,
|
|
26
|
+
interpolate_variables=False,
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Add/remove/update crontab entries.
|
|
30
|
+
|
|
31
|
+
+ command: the command for the cron
|
|
32
|
+
+ present: whether this cron command should exist
|
|
33
|
+
+ user: the user whose crontab to manage
|
|
34
|
+
+ cron_name: name the cronjob so future changes to the command will overwrite
|
|
35
|
+
+ modify_cron_name: modify the cron name
|
|
36
|
+
+ minute: which minutes to execute the cron
|
|
37
|
+
+ hour: which hours to execute the cron
|
|
38
|
+
+ month: which months to execute the cron
|
|
39
|
+
+ day_of_week: which day of the week to execute the cron
|
|
40
|
+
+ day_of_month: which day of the month to execute the cron
|
|
41
|
+
+ special_time: cron "nickname" time (@reboot, @daily, etc), overrides others
|
|
42
|
+
+ interpolate_variables: whether to interpolate variables in ``command``
|
|
43
|
+
|
|
44
|
+
Cron commands:
|
|
45
|
+
Unless ``name`` is specified the command is used to identify crontab entries.
|
|
46
|
+
This means commands must be unique within a given users crontab. If you require
|
|
47
|
+
multiple identical commands, provide a different name argument for each.
|
|
48
|
+
|
|
49
|
+
Special times:
|
|
50
|
+
When provided, ``special_time`` will be used instead of any values passed in
|
|
51
|
+
for ``minute``/``hour``/``month``/``day_of_week``/``day_of_month``.
|
|
52
|
+
|
|
53
|
+
**Example:**
|
|
54
|
+
|
|
55
|
+
.. code:: python
|
|
56
|
+
|
|
57
|
+
from pyinfra.operations import crontab
|
|
58
|
+
# simple example for a crontab
|
|
59
|
+
crontab.crontab(
|
|
60
|
+
name="Backup /etc weekly",
|
|
61
|
+
command="/bin/tar cf /tmp/etc_bup.tar /etc",
|
|
62
|
+
name="backup_etc",
|
|
63
|
+
day_of_week=0,
|
|
64
|
+
hour=1,
|
|
65
|
+
minute=0,
|
|
66
|
+
)
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def comma_sep(value):
|
|
70
|
+
if isinstance(value, (list, tuple)):
|
|
71
|
+
return ",".join("{0}".format(v) for v in value)
|
|
72
|
+
return value
|
|
73
|
+
|
|
74
|
+
minute = comma_sep(minute)
|
|
75
|
+
hour = comma_sep(hour)
|
|
76
|
+
month = comma_sep(month)
|
|
77
|
+
day_of_week = comma_sep(day_of_week)
|
|
78
|
+
day_of_month = comma_sep(day_of_month)
|
|
79
|
+
|
|
80
|
+
ctb0: CrontabFile | dict = host.get_fact(Crontab, user=user)
|
|
81
|
+
# facts from test are in dict
|
|
82
|
+
if isinstance(ctb0, dict):
|
|
83
|
+
ctb = CrontabFile(ctb0)
|
|
84
|
+
else:
|
|
85
|
+
ctb = ctb0
|
|
86
|
+
name_comment = "# pyinfra-name={0}".format(cron_name)
|
|
87
|
+
|
|
88
|
+
existing_crontab = ctb.get_command(
|
|
89
|
+
command=command if cron_name is None else None, name=cron_name
|
|
90
|
+
)
|
|
91
|
+
existing_crontab_command = existing_crontab["command"] if existing_crontab else command
|
|
92
|
+
existing_crontab_match = existing_crontab["command"] if existing_crontab else command
|
|
93
|
+
|
|
94
|
+
exists = existing_crontab is not None
|
|
95
|
+
exists_name = existing_crontab is not None and name_comment in existing_crontab.get(
|
|
96
|
+
"comments", ""
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
edit_commands: list[str | StringCommand] = []
|
|
100
|
+
temp_filename = host.get_temp_filename()
|
|
101
|
+
|
|
102
|
+
if special_time:
|
|
103
|
+
new_crontab_line = "{0} {1}".format(special_time, command)
|
|
104
|
+
else:
|
|
105
|
+
new_crontab_line = "{minute} {hour} {day_of_month} {month} {day_of_week} {command}".format(
|
|
106
|
+
minute=minute,
|
|
107
|
+
hour=hour,
|
|
108
|
+
day_of_month=day_of_month,
|
|
109
|
+
month=month,
|
|
110
|
+
day_of_week=day_of_week,
|
|
111
|
+
command=command,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
existing_crontab_match = ".*{0}.*".format(existing_crontab_match)
|
|
115
|
+
|
|
116
|
+
# Don't want the cron and it does exist? Remove the line
|
|
117
|
+
if not present and exists:
|
|
118
|
+
edit_commands.append(
|
|
119
|
+
sed_delete(
|
|
120
|
+
temp_filename,
|
|
121
|
+
existing_crontab_match,
|
|
122
|
+
"",
|
|
123
|
+
interpolate_variables=interpolate_variables,
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Want the cron but it doesn't exist? Append the line
|
|
128
|
+
elif present and not exists:
|
|
129
|
+
logger.debug(f"present: {present}, exists: {exists}")
|
|
130
|
+
if ctb: # append a blank line if cron entries already exist
|
|
131
|
+
edit_commands.append("echo '' >> {0}".format(temp_filename))
|
|
132
|
+
if cron_name:
|
|
133
|
+
edit_commands.append(
|
|
134
|
+
"echo {0} >> {1}".format(
|
|
135
|
+
shlex.quote(name_comment),
|
|
136
|
+
temp_filename,
|
|
137
|
+
),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
edit_commands.append(
|
|
141
|
+
"echo {0} >> {1}".format(
|
|
142
|
+
shlex.quote(new_crontab_line),
|
|
143
|
+
temp_filename,
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# We have the cron and it exists, do it's details? If not, replace the line
|
|
148
|
+
elif present and exists:
|
|
149
|
+
assert existing_crontab is not None
|
|
150
|
+
if any(
|
|
151
|
+
(
|
|
152
|
+
exists_name != (cron_name is not None),
|
|
153
|
+
special_time != existing_crontab.get("special_time"),
|
|
154
|
+
try_int(minute) != existing_crontab.get("minute"),
|
|
155
|
+
try_int(hour) != existing_crontab.get("hour"),
|
|
156
|
+
try_int(month) != existing_crontab.get("month"),
|
|
157
|
+
try_int(day_of_week) != existing_crontab.get("day_of_week"),
|
|
158
|
+
try_int(day_of_month) != existing_crontab.get("day_of_month"),
|
|
159
|
+
existing_crontab_command != command,
|
|
160
|
+
),
|
|
161
|
+
):
|
|
162
|
+
if not exists_name and cron_name:
|
|
163
|
+
new_crontab_line = f"{name_comment}\\n{new_crontab_line}"
|
|
164
|
+
edit_commands.append(
|
|
165
|
+
sed_replace(
|
|
166
|
+
temp_filename,
|
|
167
|
+
existing_crontab_match,
|
|
168
|
+
new_crontab_line,
|
|
169
|
+
interpolate_variables=interpolate_variables,
|
|
170
|
+
),
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
if edit_commands:
|
|
174
|
+
crontab_args = []
|
|
175
|
+
if user:
|
|
176
|
+
crontab_args.append("-u {0}".format(user))
|
|
177
|
+
|
|
178
|
+
# List the crontab into a temporary file if it exists
|
|
179
|
+
if ctb:
|
|
180
|
+
yield "crontab -l {0} > {1}".format(" ".join(crontab_args), temp_filename)
|
|
181
|
+
|
|
182
|
+
# Now yield any edits
|
|
183
|
+
for edit_command in edit_commands:
|
|
184
|
+
yield edit_command
|
|
185
|
+
|
|
186
|
+
# Finally, use the tempfile to write a new crontab
|
|
187
|
+
yield "crontab {0} {1}".format(" ".join(crontab_args), temp_filename)
|
|
188
|
+
else:
|
|
189
|
+
host.noop(
|
|
190
|
+
"crontab {0} {1}".format(
|
|
191
|
+
command,
|
|
192
|
+
"exists" if present else "does not exist",
|
|
193
|
+
),
|
|
194
|
+
)
|