pyinfra 2.9.1__py2.py3-none-any.whl → 3.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyinfra/api/__init__.py +3 -0
- pyinfra/api/arguments.py +265 -253
- pyinfra/api/arguments_typed.py +80 -0
- pyinfra/api/command.py +68 -53
- pyinfra/api/config.py +139 -32
- pyinfra/api/connect.py +1 -1
- pyinfra/api/connectors.py +7 -26
- pyinfra/api/deploy.py +21 -52
- pyinfra/api/exceptions.py +33 -8
- pyinfra/api/facts.py +102 -137
- pyinfra/api/host.py +150 -82
- pyinfra/api/inventory.py +21 -25
- pyinfra/api/operation.py +240 -198
- pyinfra/api/operations.py +102 -148
- pyinfra/api/state.py +137 -79
- pyinfra/api/util.py +79 -86
- pyinfra/connectors/base.py +147 -0
- pyinfra/connectors/chroot.py +160 -169
- pyinfra/connectors/docker.py +220 -237
- pyinfra/connectors/dockerssh.py +231 -253
- pyinfra/connectors/local.py +196 -208
- pyinfra/connectors/ssh.py +530 -613
- pyinfra/connectors/ssh_util.py +114 -0
- pyinfra/connectors/sshuserclient/client.py +5 -3
- pyinfra/connectors/terraform.py +86 -65
- pyinfra/connectors/util.py +211 -137
- pyinfra/connectors/vagrant.py +60 -53
- pyinfra/context.py +4 -2
- 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 +19 -0
- pyinfra/facts/files.py +47 -32
- pyinfra/facts/gem.py +2 -0
- pyinfra/facts/git.py +3 -1
- pyinfra/facts/gpg.py +3 -1
- pyinfra/facts/hardware.py +34 -24
- pyinfra/facts/iptables.py +5 -3
- pyinfra/facts/launchd.py +2 -0
- pyinfra/facts/lxd.py +2 -0
- pyinfra/facts/mysql.py +13 -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 +168 -0
- pyinfra/facts/postgresql.py +6 -160
- pyinfra/facts/rpm.py +12 -9
- pyinfra/facts/runit.py +68 -0
- pyinfra/facts/selinux.py +3 -1
- pyinfra/facts/server.py +80 -36
- pyinfra/facts/snap.py +2 -0
- pyinfra/facts/systemd.py +31 -12
- pyinfra/facts/sysvinit.py +10 -10
- pyinfra/facts/upstart.py +2 -0
- pyinfra/facts/util/packaging.py +7 -4
- 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/local.py +4 -5
- pyinfra/operations/apk.py +6 -4
- pyinfra/operations/apt.py +46 -65
- pyinfra/operations/brew.py +17 -22
- pyinfra/operations/bsdinit.py +9 -7
- pyinfra/operations/cargo.py +4 -2
- pyinfra/operations/choco.py +4 -2
- pyinfra/operations/dnf.py +19 -23
- pyinfra/operations/docker.py +339 -0
- pyinfra/operations/files.py +188 -386
- pyinfra/operations/gem.py +4 -2
- pyinfra/operations/git.py +24 -53
- pyinfra/operations/iptables.py +29 -35
- pyinfra/operations/launchd.py +6 -7
- pyinfra/operations/lxd.py +8 -13
- pyinfra/operations/mysql.py +62 -81
- pyinfra/operations/npm.py +9 -2
- pyinfra/operations/openrc.py +6 -4
- pyinfra/operations/pacman.py +7 -8
- pyinfra/operations/pip.py +25 -24
- pyinfra/operations/pkg.py +4 -2
- pyinfra/operations/pkgin.py +6 -4
- pyinfra/operations/postgres.py +349 -0
- pyinfra/operations/postgresql.py +18 -379
- pyinfra/operations/puppet.py +3 -1
- pyinfra/operations/python.py +8 -19
- pyinfra/operations/runit.py +182 -0
- pyinfra/operations/selinux.py +47 -44
- pyinfra/operations/server.py +111 -127
- pyinfra/operations/snap.py +4 -4
- pyinfra/operations/ssh.py +20 -33
- pyinfra/operations/systemd.py +19 -15
- pyinfra/operations/sysvinit.py +9 -16
- pyinfra/operations/upstart.py +9 -7
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/docker.py +177 -0
- pyinfra/operations/util/files.py +24 -16
- pyinfra/operations/util/packaging.py +55 -57
- pyinfra/operations/util/service.py +39 -51
- pyinfra/operations/vzctl.py +12 -10
- pyinfra/operations/xbps.py +6 -4
- pyinfra/operations/yum.py +18 -22
- pyinfra/operations/zypper.py +12 -13
- pyinfra/version.py +5 -2
- {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/METADATA +40 -41
- pyinfra-3.0.dist-info/RECORD +167 -0
- {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/WHEEL +1 -1
- pyinfra-3.0.dist-info/entry_points.txt +11 -0
- pyinfra_cli/__main__.py +4 -3
- pyinfra_cli/commands.py +7 -2
- pyinfra_cli/exceptions.py +78 -42
- pyinfra_cli/inventory.py +40 -6
- pyinfra_cli/log.py +17 -3
- pyinfra_cli/main.py +133 -90
- pyinfra_cli/prints.py +95 -127
- pyinfra_cli/util.py +62 -29
- tests/test_api/test_api.py +2 -0
- tests/test_api/test_api_arguments.py +13 -13
- tests/test_api/test_api_deploys.py +28 -29
- tests/test_api/test_api_facts.py +60 -98
- tests/test_api/test_api_operations.py +101 -201
- tests/test_cli/test_cli.py +18 -49
- tests/test_cli/test_cli_deploy.py +11 -37
- tests/test_cli/test_cli_exceptions.py +50 -19
- tests/test_cli/util.py +1 -1
- tests/test_connectors/test_chroot.py +6 -6
- tests/test_connectors/test_docker.py +4 -4
- tests/test_connectors/test_dockerssh.py +38 -50
- tests/test_connectors/test_local.py +11 -12
- tests/test_connectors/test_ssh.py +105 -93
- tests/test_connectors/test_terraform.py +9 -15
- tests/test_connectors/test_util.py +24 -46
- tests/test_connectors/test_vagrant.py +7 -7
- pyinfra/api/operation.pyi +0 -117
- pyinfra/connectors/ansible.py +0 -171
- pyinfra/connectors/mech.py +0 -186
- pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
- pyinfra/connectors/winrm.py +0 -320
- pyinfra/facts/windows.py +0 -366
- pyinfra/facts/windows_files.py +0 -90
- pyinfra/operations/windows.py +0 -59
- pyinfra/operations/windows_files.py +0 -551
- pyinfra-2.9.1.dist-info/RECORD +0 -170
- pyinfra-2.9.1.dist-info/entry_points.txt +0 -14
- tests/test_connectors/test_ansible.py +0 -64
- tests/test_connectors/test_mech.py +0 -126
- tests/test_connectors/test_winrm.py +0 -76
- {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/LICENSE.md +0 -0
- {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/top_level.txt +0 -0
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
|
|
@@ -9,8 +11,8 @@ from pyinfra.facts.gem import GemPackages
|
|
|
9
11
|
from .util.packaging import ensure_packages
|
|
10
12
|
|
|
11
13
|
|
|
12
|
-
@operation
|
|
13
|
-
def packages(packages=None, present=True, latest=False):
|
|
14
|
+
@operation()
|
|
15
|
+
def packages(packages: str | list[str] | None = None, present=True, latest=False):
|
|
14
16
|
"""
|
|
15
17
|
Add/remove/update gem packages.
|
|
16
18
|
|
pyinfra/operations/git.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Manage git repositories and configuration.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
5
7
|
import re
|
|
6
8
|
|
|
7
9
|
from pyinfra import host
|
|
@@ -13,10 +15,8 @@ from . import files, ssh
|
|
|
13
15
|
from .util.files import chown, unix_path_join
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
@operation(
|
|
17
|
-
|
|
18
|
-
)
|
|
19
|
-
def config(key, value, multi_value=False, repo=None):
|
|
18
|
+
@operation()
|
|
19
|
+
def config(key: str, value: str, multi_value=False, repo: str | None = None):
|
|
20
20
|
"""
|
|
21
21
|
Manage git config for a repository or globally.
|
|
22
22
|
|
|
@@ -54,27 +54,23 @@ def config(key, value, multi_value=False, repo=None):
|
|
|
54
54
|
|
|
55
55
|
if not multi_value and existing_config.get(key) != [value]:
|
|
56
56
|
yield '{0} {1} "{2}"'.format(base_command, key, value)
|
|
57
|
-
existing_config[key] = [value]
|
|
58
57
|
|
|
59
58
|
elif multi_value and value not in existing_config.get(key, []):
|
|
60
59
|
yield '{0} --add {1} "{2}"'.format(base_command, key, value)
|
|
61
|
-
existing_config.setdefault(key, []).append(value)
|
|
62
60
|
|
|
63
61
|
else:
|
|
64
62
|
host.noop("git config {0} is set to {1}".format(key, value))
|
|
65
63
|
|
|
66
64
|
|
|
67
|
-
@operation(
|
|
68
|
-
pipeline_facts={"git_branch": "target"},
|
|
69
|
-
)
|
|
65
|
+
@operation()
|
|
70
66
|
def repo(
|
|
71
|
-
src,
|
|
72
|
-
dest,
|
|
73
|
-
branch=None,
|
|
67
|
+
src: str,
|
|
68
|
+
dest: str,
|
|
69
|
+
branch: str | None = None,
|
|
74
70
|
pull=True,
|
|
75
71
|
rebase=False,
|
|
76
|
-
user=None,
|
|
77
|
-
group=None,
|
|
72
|
+
user: str | None = None,
|
|
73
|
+
group: str | None = None,
|
|
78
74
|
ssh_keyscan=False,
|
|
79
75
|
update_submodules=False,
|
|
80
76
|
recursive_submodules=False,
|
|
@@ -105,7 +101,7 @@ def repo(
|
|
|
105
101
|
"""
|
|
106
102
|
|
|
107
103
|
# Ensure our target directory exists
|
|
108
|
-
yield from files.directory(dest)
|
|
104
|
+
yield from files.directory._inner(dest)
|
|
109
105
|
|
|
110
106
|
# Do we need to scan for the remote host key?
|
|
111
107
|
if ssh_keyscan:
|
|
@@ -113,7 +109,7 @@ def repo(
|
|
|
113
109
|
domain = re.match(r"^[a-zA-Z0-9]+@([0-9a-zA-Z\.\-]+)", src)
|
|
114
110
|
|
|
115
111
|
if domain:
|
|
116
|
-
yield from ssh.keyscan(domain.group(1))
|
|
112
|
+
yield from ssh.keyscan._inner(domain.group(1))
|
|
117
113
|
else:
|
|
118
114
|
raise OperationError(
|
|
119
115
|
"Could not parse domain (to SSH keyscan) from: {0}".format(src),
|
|
@@ -130,21 +126,11 @@ def repo(
|
|
|
130
126
|
git_commands.append("clone {0} --branch {1} .".format(src, branch))
|
|
131
127
|
else:
|
|
132
128
|
git_commands.append("clone {0} .".format(src))
|
|
133
|
-
|
|
134
|
-
host.create_fact(GitBranch, kwargs={"repo": dest}, data=branch)
|
|
135
|
-
host.create_fact(
|
|
136
|
-
Directory,
|
|
137
|
-
kwargs={"path": git_dir},
|
|
138
|
-
data={"user": user, "group": group},
|
|
139
|
-
)
|
|
140
|
-
|
|
141
129
|
# Ensuring existing repo
|
|
142
130
|
else:
|
|
143
131
|
if branch and host.get_fact(GitBranch, repo=dest) != branch:
|
|
144
132
|
git_commands.append("fetch") # fetch to ensure we have the branch locally
|
|
145
133
|
git_commands.append("checkout {0}".format(branch))
|
|
146
|
-
host.create_fact(GitBranch, kwargs={"repo": dest}, data=branch)
|
|
147
|
-
|
|
148
134
|
if pull:
|
|
149
135
|
if rebase:
|
|
150
136
|
git_commands.append("pull --rebase")
|
|
@@ -171,19 +157,19 @@ def repo(
|
|
|
171
157
|
|
|
172
158
|
@operation()
|
|
173
159
|
def worktree(
|
|
174
|
-
worktree,
|
|
175
|
-
repo=None,
|
|
160
|
+
worktree: str,
|
|
161
|
+
repo: str | None = None,
|
|
176
162
|
detached=False,
|
|
177
|
-
new_branch=None,
|
|
178
|
-
commitish=None,
|
|
163
|
+
new_branch: str | None = None,
|
|
164
|
+
commitish: str | None = None,
|
|
179
165
|
pull=True,
|
|
180
166
|
rebase=False,
|
|
181
|
-
from_remote_branch=None,
|
|
167
|
+
from_remote_branch: tuple[str, str] | None = None,
|
|
182
168
|
present=True,
|
|
183
169
|
assume_repo_exists=False,
|
|
184
170
|
force=False,
|
|
185
|
-
user=None,
|
|
186
|
-
group=None,
|
|
171
|
+
user: str | None = None,
|
|
172
|
+
group: str | None = None,
|
|
187
173
|
):
|
|
188
174
|
"""
|
|
189
175
|
Manage git worktrees.
|
|
@@ -293,7 +279,6 @@ def worktree(
|
|
|
293
279
|
|
|
294
280
|
# Doesn't exist & we want it
|
|
295
281
|
if not host.get_fact(Directory, path=worktree) and present:
|
|
296
|
-
|
|
297
282
|
# be sure that `repo` is a GIT repository
|
|
298
283
|
if not assume_repo_exists and not host.get_fact(
|
|
299
284
|
Directory,
|
|
@@ -321,12 +306,8 @@ def worktree(
|
|
|
321
306
|
if user or group:
|
|
322
307
|
yield chown(worktree, user, group, recursive=True)
|
|
323
308
|
|
|
324
|
-
host.create_fact(Directory, kwargs={"path": worktree}, data={"user": user, "group": group})
|
|
325
|
-
host.create_fact(GitTrackingBranch, kwargs={"repo": worktree}, data=new_branch)
|
|
326
|
-
|
|
327
309
|
# It exists and we don't want it
|
|
328
310
|
elif host.get_fact(Directory, path=worktree) and not present:
|
|
329
|
-
|
|
330
311
|
command = "cd {0} && git worktree remove .".format(worktree)
|
|
331
312
|
|
|
332
313
|
if force:
|
|
@@ -334,12 +315,8 @@ def worktree(
|
|
|
334
315
|
|
|
335
316
|
yield command
|
|
336
317
|
|
|
337
|
-
host.delete_fact(Directory, kwargs={"path": worktree})
|
|
338
|
-
host.create_fact(GitTrackingBranch, kwargs={"repo": worktree})
|
|
339
|
-
|
|
340
318
|
# It exists and we still want it => pull/rebase it
|
|
341
319
|
elif host.get_fact(Directory, path=worktree) and present:
|
|
342
|
-
|
|
343
320
|
# pull the worktree only if it's already linked to a tracking branch or
|
|
344
321
|
# if a remote branch is set
|
|
345
322
|
if host.get_fact(GitTrackingBranch, repo=worktree) or from_remote_branch:
|
|
@@ -359,11 +336,11 @@ def worktree(
|
|
|
359
336
|
yield command
|
|
360
337
|
|
|
361
338
|
|
|
362
|
-
@operation
|
|
339
|
+
@operation()
|
|
363
340
|
def bare_repo(
|
|
364
|
-
path,
|
|
365
|
-
user=None,
|
|
366
|
-
group=None,
|
|
341
|
+
path: str,
|
|
342
|
+
user: str | None = None,
|
|
343
|
+
group: str | None = None,
|
|
367
344
|
present=True,
|
|
368
345
|
):
|
|
369
346
|
"""
|
|
@@ -384,7 +361,7 @@ def bare_repo(
|
|
|
384
361
|
)
|
|
385
362
|
"""
|
|
386
363
|
|
|
387
|
-
yield from files.directory(path, present=present)
|
|
364
|
+
yield from files.directory._inner(path, present=present)
|
|
388
365
|
|
|
389
366
|
if present:
|
|
390
367
|
head_filename = unix_path_join(path, "HEAD")
|
|
@@ -397,9 +374,3 @@ def bare_repo(
|
|
|
397
374
|
else:
|
|
398
375
|
if (user and head_file["user"] != user) or (group and head_file["group"] != group):
|
|
399
376
|
yield chown(path, user, group, recursive=True)
|
|
400
|
-
|
|
401
|
-
host.create_fact(
|
|
402
|
-
File,
|
|
403
|
-
kwargs={"path": head_filename},
|
|
404
|
-
data={"user": user, "group": group, "mode": None},
|
|
405
|
-
)
|
pyinfra/operations/iptables.py
CHANGED
|
@@ -2,18 +2,20 @@
|
|
|
2
2
|
The iptables modules handles iptables rules
|
|
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.api.exceptions import OperationError
|
|
8
10
|
from pyinfra.facts.iptables import Ip6tablesChains, Ip6tablesRules, IptablesChains, IptablesRules
|
|
9
11
|
|
|
10
12
|
|
|
11
|
-
@operation
|
|
13
|
+
@operation()
|
|
12
14
|
def chain(
|
|
13
|
-
chain,
|
|
15
|
+
chain: str,
|
|
14
16
|
present=True,
|
|
15
17
|
table="filter",
|
|
16
|
-
policy=None,
|
|
18
|
+
policy: str | None = None,
|
|
17
19
|
version=4,
|
|
18
20
|
):
|
|
19
21
|
"""
|
|
@@ -41,7 +43,6 @@ def chain(
|
|
|
41
43
|
if not present:
|
|
42
44
|
if chain in chains:
|
|
43
45
|
yield "{0} -X {1}".format(command, chain)
|
|
44
|
-
chains.pop(chain)
|
|
45
46
|
else:
|
|
46
47
|
host.noop("iptables chain {0} does not exist".format(chain))
|
|
47
48
|
return
|
|
@@ -49,44 +50,42 @@ def chain(
|
|
|
49
50
|
if present:
|
|
50
51
|
if chain not in chains:
|
|
51
52
|
yield "{0} -N {1}".format(command, chain)
|
|
52
|
-
chains[chain] = None # policy will be set below
|
|
53
53
|
else:
|
|
54
54
|
host.noop("iptables chain {0} exists".format(chain))
|
|
55
55
|
|
|
56
56
|
if policy:
|
|
57
57
|
if chain not in chains or chains[chain] != policy:
|
|
58
58
|
yield "{0} -P {1} {2}".format(command, chain, policy)
|
|
59
|
-
chains[chain] = policy
|
|
60
59
|
|
|
61
60
|
|
|
62
|
-
@operation
|
|
61
|
+
@operation()
|
|
63
62
|
def rule(
|
|
64
|
-
chain,
|
|
65
|
-
jump,
|
|
66
|
-
present=True,
|
|
67
|
-
table="filter",
|
|
68
|
-
append=True,
|
|
69
|
-
version=4,
|
|
63
|
+
chain: str,
|
|
64
|
+
jump: str,
|
|
65
|
+
present: bool = True,
|
|
66
|
+
table: str = "filter",
|
|
67
|
+
append: bool = True,
|
|
68
|
+
version: int = 4,
|
|
70
69
|
# Core iptables filter arguments
|
|
71
|
-
protocol=None,
|
|
72
|
-
not_protocol=None,
|
|
73
|
-
source=None,
|
|
74
|
-
not_source=None,
|
|
75
|
-
destination=None,
|
|
76
|
-
not_destination=None,
|
|
77
|
-
in_interface=None,
|
|
78
|
-
not_in_interface=None,
|
|
79
|
-
out_interface=None,
|
|
80
|
-
not_out_interface=None,
|
|
70
|
+
protocol: str | None = None,
|
|
71
|
+
not_protocol: str | None = None,
|
|
72
|
+
source: str | None = None,
|
|
73
|
+
not_source: str | None = None,
|
|
74
|
+
destination: str | None = None,
|
|
75
|
+
not_destination: str | None = None,
|
|
76
|
+
in_interface: str | None = None,
|
|
77
|
+
not_in_interface: str | None = None,
|
|
78
|
+
out_interface: str | None = None,
|
|
79
|
+
not_out_interface: str | None = None,
|
|
81
80
|
# After-rule arguments
|
|
82
|
-
to_destination=None,
|
|
83
|
-
to_source=None,
|
|
84
|
-
to_ports=None,
|
|
85
|
-
log_prefix=None,
|
|
81
|
+
to_destination: str | None = None,
|
|
82
|
+
to_source: str | None = None,
|
|
83
|
+
to_ports: int | str | None = None,
|
|
84
|
+
log_prefix: str | None = None,
|
|
86
85
|
# Extras and extra shortcuts
|
|
87
|
-
destination_port=None,
|
|
88
|
-
source_port=None,
|
|
89
|
-
extras="",
|
|
86
|
+
destination_port: int | None = None,
|
|
87
|
+
source_port: int | None = None,
|
|
88
|
+
extras: str = "",
|
|
90
89
|
):
|
|
91
90
|
"""
|
|
92
91
|
Add/remove iptables rules.
|
|
@@ -310,8 +309,3 @@ def rule(
|
|
|
310
309
|
|
|
311
310
|
# Build the final iptables command
|
|
312
311
|
yield " ".join(args)
|
|
313
|
-
|
|
314
|
-
if action == "-D":
|
|
315
|
-
rules.remove(definition)
|
|
316
|
-
else:
|
|
317
|
-
rules.append(definition)
|
pyinfra/operations/launchd.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Manage launchd services.
|
|
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.launchd import LaunchdStatus
|
|
@@ -9,12 +11,12 @@ from pyinfra.facts.launchd import LaunchdStatus
|
|
|
9
11
|
from .util.service import handle_service_control
|
|
10
12
|
|
|
11
13
|
|
|
12
|
-
@operation
|
|
14
|
+
@operation()
|
|
13
15
|
def service(
|
|
14
|
-
service,
|
|
16
|
+
service: str,
|
|
15
17
|
running=True,
|
|
16
18
|
restarted=False,
|
|
17
|
-
command=None,
|
|
19
|
+
command: str | None = None,
|
|
18
20
|
):
|
|
19
21
|
"""
|
|
20
22
|
Manage the state of systemd managed services.
|
|
@@ -33,11 +35,8 @@ def service(
|
|
|
33
35
|
service,
|
|
34
36
|
host.get_fact(LaunchdStatus),
|
|
35
37
|
"launchctl {1} {0}",
|
|
36
|
-
# No support for restart/reload/command
|
|
37
38
|
running,
|
|
38
|
-
|
|
39
|
-
None,
|
|
40
|
-
None,
|
|
39
|
+
# No support for restart/reload/command
|
|
41
40
|
)
|
|
42
41
|
|
|
43
42
|
# No restart command, so just stop/start
|
pyinfra/operations/lxd.py
CHANGED
|
@@ -2,22 +2,25 @@
|
|
|
2
2
|
The LXD modules manage LXD containers
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
5
9
|
from pyinfra import host
|
|
6
10
|
from pyinfra.api import operation
|
|
7
11
|
from pyinfra.facts.lxd import LxdContainers
|
|
8
12
|
|
|
9
13
|
|
|
10
|
-
def get_container_named(name, containers):
|
|
14
|
+
def get_container_named(name: str, containers: list[dict[str, Any]]) -> dict[str, Any] | None:
|
|
11
15
|
for container in containers:
|
|
12
16
|
if container["name"] == name:
|
|
13
17
|
return container
|
|
14
|
-
|
|
15
|
-
return None
|
|
18
|
+
return None
|
|
16
19
|
|
|
17
20
|
|
|
18
|
-
@operation
|
|
21
|
+
@operation()
|
|
19
22
|
def container(
|
|
20
|
-
id,
|
|
23
|
+
id: str,
|
|
21
24
|
present=True,
|
|
22
25
|
image="ubuntu:16.04",
|
|
23
26
|
):
|
|
@@ -53,8 +56,6 @@ def container(
|
|
|
53
56
|
|
|
54
57
|
# Command to remove the container:
|
|
55
58
|
yield "lxc delete {0}".format(id)
|
|
56
|
-
|
|
57
|
-
current_containers.remove(container)
|
|
58
59
|
else:
|
|
59
60
|
host.noop("container {0} does not exist".format(id))
|
|
60
61
|
|
|
@@ -63,11 +64,5 @@ def container(
|
|
|
63
64
|
if not container:
|
|
64
65
|
# Command to create the container:
|
|
65
66
|
yield "lxc launch {image} {id} < /dev/null".format(id=id, image=image)
|
|
66
|
-
current_containers.append(
|
|
67
|
-
{
|
|
68
|
-
"name": id,
|
|
69
|
-
"image": image,
|
|
70
|
-
},
|
|
71
|
-
)
|
|
72
67
|
else:
|
|
73
68
|
host.noop("container {0} exists".format(id))
|
pyinfra/operations/mysql.py
CHANGED
|
@@ -13,6 +13,8 @@ See the example/mysql.py
|
|
|
13
13
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
16
18
|
from pyinfra import host
|
|
17
19
|
from pyinfra.api import MaskString, OperationError, QuoteString, StringCommand, operation
|
|
18
20
|
from pyinfra.facts.mysql import (
|
|
@@ -26,13 +28,13 @@ from pyinfra.facts.mysql import (
|
|
|
26
28
|
|
|
27
29
|
@operation(is_idempotent=False)
|
|
28
30
|
def sql(
|
|
29
|
-
sql,
|
|
30
|
-
database=None,
|
|
31
|
+
sql: str,
|
|
32
|
+
database: str | None = None,
|
|
31
33
|
# Details for speaking to MySQL via `mysql` CLI
|
|
32
|
-
mysql_user=None,
|
|
33
|
-
mysql_password=None,
|
|
34
|
-
mysql_host=None,
|
|
35
|
-
mysql_port=None,
|
|
34
|
+
mysql_user: str | None = None,
|
|
35
|
+
mysql_password: str | None = None,
|
|
36
|
+
mysql_host: str | None = None,
|
|
37
|
+
mysql_port: int | None = None,
|
|
36
38
|
):
|
|
37
39
|
"""
|
|
38
40
|
Execute arbitrary SQL against MySQL.
|
|
@@ -52,29 +54,30 @@ def sql(
|
|
|
52
54
|
)
|
|
53
55
|
|
|
54
56
|
|
|
55
|
-
@operation
|
|
57
|
+
@operation()
|
|
56
58
|
def user(
|
|
57
|
-
user,
|
|
58
|
-
present=True,
|
|
59
|
-
user_hostname="localhost",
|
|
60
|
-
password=None,
|
|
61
|
-
privileges=None,
|
|
59
|
+
user: str,
|
|
60
|
+
present: bool = True,
|
|
61
|
+
user_hostname: str = "localhost",
|
|
62
|
+
password: str | None = None,
|
|
63
|
+
privileges: str | list[str] | None = None,
|
|
62
64
|
# MySQL REQUIRE SSL/TLS options
|
|
63
|
-
require=None, # SSL or X509
|
|
64
|
-
require_cipher=
|
|
65
|
-
require_issuer=
|
|
66
|
-
require_subject=
|
|
65
|
+
require: str | None = None, # SSL or X509
|
|
66
|
+
require_cipher: str | None = None,
|
|
67
|
+
require_issuer: str | None = None,
|
|
68
|
+
require_subject: str | None = None,
|
|
67
69
|
# MySQL WITH resource limit options
|
|
68
|
-
max_connections=None,
|
|
69
|
-
max_queries_per_hour=None,
|
|
70
|
-
max_updates_per_hour=None,
|
|
71
|
-
max_connections_per_hour=None,
|
|
70
|
+
max_connections: int | None = None,
|
|
71
|
+
max_queries_per_hour: int | None = None,
|
|
72
|
+
max_updates_per_hour: int | None = None,
|
|
73
|
+
max_connections_per_hour: int | None = None,
|
|
72
74
|
# Details for speaking to MySQL via `mysql` CLI via `mysql` CLI
|
|
73
|
-
mysql_user=None,
|
|
74
|
-
mysql_password=None,
|
|
75
|
-
mysql_host=None,
|
|
76
|
-
mysql_port=None,
|
|
75
|
+
mysql_user: str | None = None,
|
|
76
|
+
mysql_password: str | None = None,
|
|
77
|
+
mysql_host: str | None = None,
|
|
78
|
+
mysql_port: int | None = None,
|
|
77
79
|
):
|
|
80
|
+
...
|
|
78
81
|
"""
|
|
79
82
|
Add/remove/update MySQL users.
|
|
80
83
|
|
|
@@ -162,22 +165,10 @@ def user(
|
|
|
162
165
|
host=mysql_host,
|
|
163
166
|
port=mysql_port,
|
|
164
167
|
)
|
|
165
|
-
current_users.pop(user_host)
|
|
166
168
|
else:
|
|
167
169
|
host.noop("mysql user {0}@{1} does not exist".format(user, user_hostname))
|
|
168
170
|
return
|
|
169
171
|
|
|
170
|
-
new_or_updated_user_fact = {
|
|
171
|
-
"ssl_type": "ANY" if require == "SSL" else require,
|
|
172
|
-
"ssl_cipher": require_cipher,
|
|
173
|
-
"x509_issuer": require_issuer,
|
|
174
|
-
"x509_subject": require_subject,
|
|
175
|
-
"max_user_connections": max_connections,
|
|
176
|
-
"max_questions": max_queries_per_hour,
|
|
177
|
-
"max_updates": max_updates_per_hour,
|
|
178
|
-
"max_connections": max_connections_per_hour,
|
|
179
|
-
}
|
|
180
|
-
|
|
181
172
|
if present and not is_present:
|
|
182
173
|
sql_bits = ['CREATE USER "{0}"@"{1}"'.format(user, user_hostname)]
|
|
183
174
|
if password:
|
|
@@ -224,8 +215,6 @@ def user(
|
|
|
224
215
|
port=mysql_port,
|
|
225
216
|
)
|
|
226
217
|
|
|
227
|
-
current_users[user_host] = new_or_updated_user_fact
|
|
228
|
-
|
|
229
218
|
if present and is_present:
|
|
230
219
|
current_user = current_users.get(user_host)
|
|
231
220
|
|
|
@@ -277,14 +266,13 @@ def user(
|
|
|
277
266
|
host=mysql_host,
|
|
278
267
|
port=mysql_port,
|
|
279
268
|
)
|
|
280
|
-
current_user.update(new_or_updated_user_fact)
|
|
281
269
|
else:
|
|
282
270
|
host.noop("mysql user {0}@{1} exists".format(user, user_hostname))
|
|
283
271
|
|
|
284
272
|
# If we're here either the user exists or we just created them; either way
|
|
285
273
|
# now we can check any privileges are set.
|
|
286
274
|
if privileges:
|
|
287
|
-
yield from _privileges(
|
|
275
|
+
yield from _privileges._inner(
|
|
288
276
|
user,
|
|
289
277
|
privileges,
|
|
290
278
|
user_hostname=user_hostname,
|
|
@@ -295,26 +283,27 @@ def user(
|
|
|
295
283
|
)
|
|
296
284
|
|
|
297
285
|
|
|
298
|
-
@operation
|
|
286
|
+
@operation()
|
|
299
287
|
def database(
|
|
300
|
-
database,
|
|
288
|
+
database: str,
|
|
301
289
|
# Desired database settings
|
|
302
|
-
present=True,
|
|
303
|
-
collate=None,
|
|
304
|
-
charset=None,
|
|
305
|
-
user=None,
|
|
306
|
-
user_hostname="localhost",
|
|
307
|
-
user_privileges="ALL",
|
|
290
|
+
present: bool = True,
|
|
291
|
+
collate: str | None = None,
|
|
292
|
+
charset: str | None = None,
|
|
293
|
+
user: str | None = None,
|
|
294
|
+
user_hostname: str = "localhost",
|
|
295
|
+
user_privileges: str | list[str] = "ALL",
|
|
308
296
|
# Details for speaking to MySQL via `mysql` CLI
|
|
309
|
-
mysql_user=None,
|
|
310
|
-
mysql_password=None,
|
|
311
|
-
mysql_host=None,
|
|
312
|
-
mysql_port=None,
|
|
297
|
+
mysql_user: str | None = None,
|
|
298
|
+
mysql_password: str | None = None,
|
|
299
|
+
mysql_host: str | None = None,
|
|
300
|
+
mysql_port: int | None = None,
|
|
313
301
|
):
|
|
302
|
+
...
|
|
314
303
|
"""
|
|
315
304
|
Add/remove MySQL databases.
|
|
316
305
|
|
|
317
|
-
+
|
|
306
|
+
+ database: the name of the database
|
|
318
307
|
+ present: whether the database should exist or not
|
|
319
308
|
+ collate: the collate to use when creating the database
|
|
320
309
|
+ charset: the charset to use when creating the database
|
|
@@ -359,7 +348,6 @@ def database(
|
|
|
359
348
|
host=mysql_host,
|
|
360
349
|
port=mysql_port,
|
|
361
350
|
)
|
|
362
|
-
current_databases.pop(database)
|
|
363
351
|
else:
|
|
364
352
|
host.noop("mysql database {0} does not exist".format(database))
|
|
365
353
|
return
|
|
@@ -381,16 +369,12 @@ def database(
|
|
|
381
369
|
host=mysql_host,
|
|
382
370
|
port=mysql_port,
|
|
383
371
|
)
|
|
384
|
-
current_databases[database] = {
|
|
385
|
-
"collate": collate,
|
|
386
|
-
"charset": charset,
|
|
387
|
-
}
|
|
388
372
|
else:
|
|
389
373
|
host.noop("mysql database {0} exists".format(database))
|
|
390
374
|
|
|
391
375
|
# Ensure any user privileges for this database
|
|
392
376
|
if user and user_privileges:
|
|
393
|
-
yield from privileges(
|
|
377
|
+
yield from privileges._inner(
|
|
394
378
|
user,
|
|
395
379
|
user_hostname=user_hostname,
|
|
396
380
|
privileges=user_privileges,
|
|
@@ -402,20 +386,20 @@ def database(
|
|
|
402
386
|
)
|
|
403
387
|
|
|
404
388
|
|
|
405
|
-
@operation
|
|
389
|
+
@operation()
|
|
406
390
|
def privileges(
|
|
407
|
-
user,
|
|
408
|
-
privileges,
|
|
391
|
+
user: str,
|
|
392
|
+
privileges: str | list[str] | set[str],
|
|
409
393
|
user_hostname="localhost",
|
|
410
394
|
database="*",
|
|
411
395
|
table="*",
|
|
412
396
|
flush=True,
|
|
413
397
|
with_grant_option=False,
|
|
414
398
|
# Details for speaking to MySQL via `mysql` CLI
|
|
415
|
-
mysql_user=None,
|
|
416
|
-
mysql_password=None,
|
|
417
|
-
mysql_host=None,
|
|
418
|
-
mysql_port=None,
|
|
399
|
+
mysql_user: str | None = None,
|
|
400
|
+
mysql_password: str | None = None,
|
|
401
|
+
mysql_host: str | None = None,
|
|
402
|
+
mysql_port: int | None = None,
|
|
419
403
|
):
|
|
420
404
|
"""
|
|
421
405
|
Add/remove MySQL privileges for a user, either global, database or table specific.
|
|
@@ -504,9 +488,6 @@ def privileges(
|
|
|
504
488
|
# Find / grant any privileges that we want but do not exist
|
|
505
489
|
privileges_to_grant = privileges - existing_privileges
|
|
506
490
|
|
|
507
|
-
user_grants[database_table] -= privileges_to_revoke
|
|
508
|
-
user_grants[database_table].update(privileges_to_grant)
|
|
509
|
-
|
|
510
491
|
if privileges_to_grant:
|
|
511
492
|
# We will grant something on this table, no need to revoke "USAGE" as it will be overridden
|
|
512
493
|
privileges_to_revoke.discard("USAGE")
|
|
@@ -547,13 +528,13 @@ _privileges = privileges # noqa: E305 (for use where kwarg is the same)
|
|
|
547
528
|
|
|
548
529
|
@operation(is_idempotent=False)
|
|
549
530
|
def dump(
|
|
550
|
-
dest,
|
|
551
|
-
database=None,
|
|
531
|
+
dest: str,
|
|
532
|
+
database: str | None = None,
|
|
552
533
|
# Details for speaking to MySQL via `mysql` CLI
|
|
553
|
-
mysql_user=None,
|
|
554
|
-
mysql_password=None,
|
|
555
|
-
mysql_host=None,
|
|
556
|
-
mysql_port=None,
|
|
534
|
+
mysql_user: str | None = None,
|
|
535
|
+
mysql_password: str | None = None,
|
|
536
|
+
mysql_host: str | None = None,
|
|
537
|
+
mysql_port: int | None = None,
|
|
557
538
|
):
|
|
558
539
|
"""
|
|
559
540
|
Dump a MySQL database into a ``.sql`` file. Requires ``mysqldump``.
|
|
@@ -588,13 +569,13 @@ def dump(
|
|
|
588
569
|
|
|
589
570
|
@operation(is_idempotent=False)
|
|
590
571
|
def load(
|
|
591
|
-
src,
|
|
592
|
-
database=None,
|
|
572
|
+
src: str,
|
|
573
|
+
database: str | None = None,
|
|
593
574
|
# Details for speaking to MySQL via `mysql` CLI
|
|
594
|
-
mysql_user=None,
|
|
595
|
-
mysql_password=None,
|
|
596
|
-
mysql_host=None,
|
|
597
|
-
mysql_port=None,
|
|
575
|
+
mysql_user: str | None = None,
|
|
576
|
+
mysql_password: str | None = None,
|
|
577
|
+
mysql_host: str | None = None,
|
|
578
|
+
mysql_port: int | None = None,
|
|
598
579
|
):
|
|
599
580
|
"""
|
|
600
581
|
Load ``.sql`` file into a database.
|