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
pyinfra/operations/postgresql.py
CHANGED
pyinfra/operations/puppet.py
CHANGED
pyinfra/operations/python.py
CHANGED
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
The Python module allows you to execute Python code within the context of a deploy.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Callable
|
|
8
|
+
|
|
5
9
|
from pyinfra.api import FunctionCommand, operation
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
@operation(is_idempotent=False, _set_in_op=False)
|
|
9
|
-
def call(function, *args, **kwargs):
|
|
13
|
+
def call(function: Callable, *args, **kwargs):
|
|
10
14
|
"""
|
|
11
15
|
Execute a Python function within a deploy.
|
|
12
16
|
|
|
@@ -43,7 +47,7 @@ def call(function, *args, **kwargs):
|
|
|
43
47
|
|
|
44
48
|
|
|
45
49
|
@operation(is_idempotent=False, _set_in_op=False)
|
|
46
|
-
def raise_exception(exception, *args, **kwargs):
|
|
50
|
+
def raise_exception(exception: Exception, *args, **kwargs):
|
|
47
51
|
"""
|
|
48
52
|
Raise a Python exception within a deploy.
|
|
49
53
|
|
|
@@ -63,6 +67,6 @@ def raise_exception(exception, *args, **kwargs):
|
|
|
63
67
|
"""
|
|
64
68
|
|
|
65
69
|
def raise_exc(*args, **kwargs): # pragma: no cover
|
|
66
|
-
raise exception(*args, **kwargs)
|
|
70
|
+
raise exception(*args, **kwargs) # type: ignore[operator]
|
|
67
71
|
|
|
68
72
|
yield FunctionCommand(raise_exc, args, kwargs)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage runit services.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from pyinfra import host
|
|
8
|
+
from pyinfra.api import operation
|
|
9
|
+
from pyinfra.facts.files import File
|
|
10
|
+
from pyinfra.facts.runit import RunitManaged, RunitStatus
|
|
11
|
+
|
|
12
|
+
from .files import file, link
|
|
13
|
+
from .util.service import handle_service_control
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@operation()
|
|
17
|
+
def service(
|
|
18
|
+
service: str,
|
|
19
|
+
running: bool = True,
|
|
20
|
+
restarted: bool = False,
|
|
21
|
+
reloaded: bool = False,
|
|
22
|
+
command: Optional[str] = None,
|
|
23
|
+
enabled: Optional[bool] = None,
|
|
24
|
+
managed: bool = True,
|
|
25
|
+
svdir: str = "/var/service",
|
|
26
|
+
sourcedir: str = "/etc/sv",
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Manage the state of runit services.
|
|
30
|
+
|
|
31
|
+
+ service: name of the service to manage
|
|
32
|
+
+ running: whether the service should be running
|
|
33
|
+
+ restarted: whether the service should be restarted
|
|
34
|
+
+ reloaded: whether the service should be reloaded
|
|
35
|
+
+ command: custom command to pass like: ``sv <command> <service>``
|
|
36
|
+
+ enabled: whether this service should be enabled/disabled on boot
|
|
37
|
+
+ managed: whether runit should manage this service
|
|
38
|
+
|
|
39
|
+
For services to be controlled, they first need to be managed by runit by
|
|
40
|
+
adding a symlink to the service in ``SVDIR``.
|
|
41
|
+
By setting ``managed=False`` the symlink will be removed.
|
|
42
|
+
Other options won't have any effect after that.
|
|
43
|
+
Although the ``<service>/down`` file can still be controlled with the
|
|
44
|
+
``enabled`` option.
|
|
45
|
+
|
|
46
|
+
+ svdir: alternative ``SVDIR``
|
|
47
|
+
|
|
48
|
+
An alternative ``SVDIR`` can be specified. This can be used for user services.
|
|
49
|
+
|
|
50
|
+
+ sourcedir: where to search for available services
|
|
51
|
+
|
|
52
|
+
An alternative directory for available services can be specified.
|
|
53
|
+
Example: ``sourcedir=/etc/sv.local`` for services managed by the administrator.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
was_managed = service in host.get_fact(RunitManaged, service=service, svdir=svdir)
|
|
57
|
+
was_auto = not host.get_fact(File, path="{0}/{1}/down".format(sourcedir, service))
|
|
58
|
+
|
|
59
|
+
# Disable autostart for previously unmanaged services.
|
|
60
|
+
#
|
|
61
|
+
# Where ``running=False`` is requested, this prevents one case of briefly
|
|
62
|
+
# starting and stopping the service.
|
|
63
|
+
if not was_managed and managed and was_auto:
|
|
64
|
+
yield from auto._inner(
|
|
65
|
+
service=service,
|
|
66
|
+
auto=False,
|
|
67
|
+
sourcedir=sourcedir,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
yield from manage._inner(
|
|
71
|
+
service=service,
|
|
72
|
+
managed=managed,
|
|
73
|
+
svdir=svdir,
|
|
74
|
+
sourcedir=sourcedir,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Service wasn't managed before, so wait for ``runsv`` to start.
|
|
78
|
+
# ``runsvdir`` will check at least every 5 seconds for new services.
|
|
79
|
+
# Wait for at most 10 seconds for the service to be managed, otherwise fail.
|
|
80
|
+
if not was_managed and managed:
|
|
81
|
+
yield from wait_runsv._inner(
|
|
82
|
+
service=service,
|
|
83
|
+
svdir=svdir,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if isinstance(enabled, bool):
|
|
87
|
+
yield from auto._inner(
|
|
88
|
+
service=service,
|
|
89
|
+
auto=enabled,
|
|
90
|
+
sourcedir=sourcedir,
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
# restore previous state of ``<service>/down``
|
|
94
|
+
yield from auto._inner(
|
|
95
|
+
service=service,
|
|
96
|
+
auto=was_auto,
|
|
97
|
+
sourcedir=sourcedir,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Services need to be managed by ``runit`` for the other options to make sense.
|
|
101
|
+
if not managed:
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
yield from handle_service_control(
|
|
105
|
+
host,
|
|
106
|
+
service,
|
|
107
|
+
host.get_fact(RunitStatus, service=service, svdir=svdir),
|
|
108
|
+
"SVDIR={0} sv {{1}} {{0}}".format(svdir),
|
|
109
|
+
running,
|
|
110
|
+
restarted,
|
|
111
|
+
reloaded,
|
|
112
|
+
command,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@operation()
|
|
117
|
+
def manage(
|
|
118
|
+
service: str,
|
|
119
|
+
managed: bool = True,
|
|
120
|
+
svdir: str = "/var/service",
|
|
121
|
+
sourcedir: str = "/etc/sv",
|
|
122
|
+
):
|
|
123
|
+
"""
|
|
124
|
+
Manage runit svdir links.
|
|
125
|
+
|
|
126
|
+
+ service: name of the service to manage
|
|
127
|
+
+ managed: whether the link should exist
|
|
128
|
+
+ svdir: alternative ``SVDIR``
|
|
129
|
+
+ sourcedir: where to search for available services
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
yield from link._inner(
|
|
133
|
+
path="{0}/{1}".format(svdir, service),
|
|
134
|
+
target="{0}/{1}".format(sourcedir, service),
|
|
135
|
+
present=managed,
|
|
136
|
+
create_remote_dir=False,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@operation(is_idempotent=False)
|
|
141
|
+
def wait_runsv(
|
|
142
|
+
service: str,
|
|
143
|
+
svdir: str = "/var/service",
|
|
144
|
+
timeout: int = 10,
|
|
145
|
+
):
|
|
146
|
+
"""
|
|
147
|
+
Wait for runsv for ``service`` to be available.
|
|
148
|
+
|
|
149
|
+
+ service: name of the service to manage
|
|
150
|
+
+ svdir: alternative ``SVDIR``
|
|
151
|
+
+ timeout: time in seconds to wait
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
yield (
|
|
155
|
+
"export SVDIR={0}\n"
|
|
156
|
+
"for i in $(seq {1}); do\n"
|
|
157
|
+
" sv status {2} > /dev/null && exit 0\n"
|
|
158
|
+
" sleep 1;\n"
|
|
159
|
+
"done\n"
|
|
160
|
+
"exit 1"
|
|
161
|
+
).format(svdir, timeout, service)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@operation()
|
|
165
|
+
def auto(
|
|
166
|
+
service: str,
|
|
167
|
+
auto: bool = True,
|
|
168
|
+
sourcedir: str = "/etc/sv",
|
|
169
|
+
):
|
|
170
|
+
"""
|
|
171
|
+
Start service automatically by managing the ``service/down`` file.
|
|
172
|
+
|
|
173
|
+
+ service: name of the service to manage
|
|
174
|
+
+ auto: whether the service should start automatically
|
|
175
|
+
+ sourcedir: where to search for available services
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
yield from file._inner(
|
|
179
|
+
path="{0}/{1}/down".format(sourcedir, service),
|
|
180
|
+
present=not auto,
|
|
181
|
+
create_remote_dir=False,
|
|
182
|
+
)
|
pyinfra/operations/selinux.py
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Provides operations to set SELinux file contexts, booleans and port types.
|
|
3
3
|
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from enum import Enum
|
|
8
|
+
|
|
4
9
|
from pyinfra import host
|
|
5
|
-
from pyinfra.api import QuoteString, StringCommand, operation
|
|
10
|
+
from pyinfra.api import OperationValueError, QuoteString, StringCommand, operation
|
|
6
11
|
from pyinfra.facts.selinux import FileContext, FileContextMapping, SEBoolean, SEPort, SEPorts
|
|
7
12
|
from pyinfra.facts.server import Which
|
|
8
13
|
|
|
9
14
|
|
|
15
|
+
class Boolean(Enum):
|
|
16
|
+
ON = "on"
|
|
17
|
+
OFF = "off"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Protocol(Enum):
|
|
21
|
+
UDP = "udp"
|
|
22
|
+
TCP = "tcp"
|
|
23
|
+
SCTP = "sctp"
|
|
24
|
+
DCCP = "dccp"
|
|
25
|
+
|
|
26
|
+
|
|
10
27
|
@operation()
|
|
11
|
-
def boolean(bool_name, value, persistent=False):
|
|
28
|
+
def boolean(bool_name: str, value: Boolean, persistent=False):
|
|
12
29
|
"""
|
|
13
30
|
Set the specified SELinux boolean to the desired state.
|
|
14
31
|
|
|
15
32
|
+ boolean: name of an SELinux boolean
|
|
16
|
-
+
|
|
33
|
+
+ value: desired state of the boolean
|
|
17
34
|
+ persistent: whether to write updated policy or not
|
|
18
35
|
|
|
19
36
|
Note: This operation requires root privileges.
|
|
@@ -25,26 +42,31 @@ def boolean(bool_name, value, persistent=False):
|
|
|
25
42
|
selinux.boolean(
|
|
26
43
|
name='Allow Apache to connect to LDAP server',
|
|
27
44
|
'httpd_can_network_connect',
|
|
28
|
-
|
|
45
|
+
Boolean.ON,
|
|
29
46
|
persistent=True
|
|
30
47
|
)
|
|
31
48
|
"""
|
|
32
|
-
_valid_states = ["on", "off"]
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
value_str: str
|
|
51
|
+
if value in ["on", "off"]: # compatibility with the old version
|
|
52
|
+
assert isinstance(value, str)
|
|
53
|
+
value_str = value
|
|
54
|
+
elif value is Boolean.ON:
|
|
55
|
+
value_str = "on"
|
|
56
|
+
elif value is Boolean.OFF:
|
|
57
|
+
value_str = "off"
|
|
58
|
+
else:
|
|
59
|
+
raise OperationValueError(f"Invalid value '{value}' for boolean operation")
|
|
38
60
|
|
|
39
|
-
if host.get_fact(SEBoolean, boolean=bool_name) !=
|
|
61
|
+
if host.get_fact(SEBoolean, boolean=bool_name) != value_str:
|
|
40
62
|
persist = "-P " if persistent else ""
|
|
41
|
-
yield StringCommand("setsebool", f"{persist}{bool_name}",
|
|
63
|
+
yield StringCommand("setsebool", f"{persist}{bool_name}", value_str)
|
|
42
64
|
else:
|
|
43
|
-
host.noop(f"boolean '{bool_name}' already had the value '{
|
|
65
|
+
host.noop(f"boolean '{bool_name}' already had the value '{value_str}'")
|
|
44
66
|
|
|
45
67
|
|
|
46
68
|
@operation()
|
|
47
|
-
def file_context(path, se_type):
|
|
69
|
+
def file_context(path: str, se_type: str):
|
|
48
70
|
"""
|
|
49
71
|
Set the SELinux type for the specified path to the specified value.
|
|
50
72
|
|
|
@@ -70,7 +92,7 @@ def file_context(path, se_type):
|
|
|
70
92
|
|
|
71
93
|
|
|
72
94
|
@operation()
|
|
73
|
-
def file_context_mapping(target, se_type=None, present=True):
|
|
95
|
+
def file_context_mapping(target: str, se_type: str | None = None, present=True):
|
|
74
96
|
"""
|
|
75
97
|
Set the SELinux file context mapping for paths matching the target.
|
|
76
98
|
|
|
@@ -110,7 +132,7 @@ def file_context_mapping(target, se_type=None, present=True):
|
|
|
110
132
|
|
|
111
133
|
|
|
112
134
|
@operation()
|
|
113
|
-
def port(protocol, port_num, se_type=None, present=True):
|
|
135
|
+
def port(protocol: Protocol | str, port_num: int, se_type: str | None = None, present=True):
|
|
114
136
|
"""
|
|
115
137
|
Set the SELinux type for the specified protocol and port.
|
|
116
138
|
|
|
@@ -127,12 +149,16 @@ def port(protocol, port_num, se_type=None, present=True):
|
|
|
127
149
|
|
|
128
150
|
selinux.port(
|
|
129
151
|
name='Allow Apache to provide service on port 2222',
|
|
130
|
-
|
|
152
|
+
Protocol.TCP,
|
|
131
153
|
2222,
|
|
132
154
|
'http_port_t',
|
|
133
155
|
)
|
|
134
156
|
"""
|
|
135
157
|
|
|
158
|
+
if protocol is Protocol:
|
|
159
|
+
assert isinstance(protocol, Protocol)
|
|
160
|
+
protocol = protocol.value
|
|
161
|
+
|
|
136
162
|
if present and (se_type is None):
|
|
137
163
|
raise ValueError("se_type must have a valid value if present is set")
|
|
138
164
|
|
pyinfra/operations/server.py
CHANGED
|
@@ -3,6 +3,8 @@ The server module takes care of os-level state. Targets POSIX compatibility, tes
|
|
|
3
3
|
Linux/BSD.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import shlex
|
|
7
9
|
from io import StringIO
|
|
8
10
|
from itertools import filterfalse, tee
|
|
@@ -18,6 +20,7 @@ from pyinfra.facts.files import Directory, FindInFile, Link
|
|
|
18
20
|
from pyinfra.facts.server import (
|
|
19
21
|
Crontab,
|
|
20
22
|
Groups,
|
|
23
|
+
Home,
|
|
21
24
|
Hostname,
|
|
22
25
|
KernelModules,
|
|
23
26
|
Locales,
|
|
@@ -38,6 +41,7 @@ from . import (
|
|
|
38
41
|
openrc,
|
|
39
42
|
pacman,
|
|
40
43
|
pkg,
|
|
44
|
+
runit,
|
|
41
45
|
systemd,
|
|
42
46
|
sysvinit,
|
|
43
47
|
upstart,
|
|
@@ -139,7 +143,7 @@ def wait(port: int):
|
|
|
139
143
|
|
|
140
144
|
|
|
141
145
|
@operation(is_idempotent=False)
|
|
142
|
-
def shell(commands):
|
|
146
|
+
def shell(commands: str | list[str]):
|
|
143
147
|
"""
|
|
144
148
|
Run raw shell code on server during a deploy. If the command would
|
|
145
149
|
modify data that would be in a fact, the fact would not be updated
|
|
@@ -166,7 +170,7 @@ def shell(commands):
|
|
|
166
170
|
|
|
167
171
|
|
|
168
172
|
@operation(is_idempotent=False)
|
|
169
|
-
def script(src, args=()):
|
|
173
|
+
def script(src: str, args=()):
|
|
170
174
|
"""
|
|
171
175
|
Upload and execute a local script on the remote host.
|
|
172
176
|
|
|
@@ -199,7 +203,7 @@ def script(src, args=()):
|
|
|
199
203
|
|
|
200
204
|
|
|
201
205
|
@operation(is_idempotent=False)
|
|
202
|
-
def script_template(src, args=(), **data):
|
|
206
|
+
def script_template(src: str, args=(), **data):
|
|
203
207
|
"""
|
|
204
208
|
Generate, upload and execute a local script template on the remote host.
|
|
205
209
|
|
|
@@ -229,7 +233,7 @@ def script_template(src, args=(), **data):
|
|
|
229
233
|
|
|
230
234
|
|
|
231
235
|
@operation()
|
|
232
|
-
def modprobe(module, present=True, force=False):
|
|
236
|
+
def modprobe(module: str, present=True, force=False):
|
|
233
237
|
"""
|
|
234
238
|
Load/unload kernel modules.
|
|
235
239
|
|
|
@@ -281,11 +285,11 @@ def modprobe(module, present=True, force=False):
|
|
|
281
285
|
|
|
282
286
|
@operation()
|
|
283
287
|
def mount(
|
|
284
|
-
path,
|
|
288
|
+
path: str,
|
|
285
289
|
mounted=True,
|
|
286
|
-
options=None,
|
|
287
|
-
device=None,
|
|
288
|
-
fs_type=None,
|
|
290
|
+
options: list[str] | None = None,
|
|
291
|
+
device: str | None = None,
|
|
292
|
+
fs_type: str | None = None,
|
|
289
293
|
# TODO: do we want to manage fstab here?
|
|
290
294
|
# update_fstab=False,
|
|
291
295
|
):
|
|
@@ -344,7 +348,7 @@ def mount(
|
|
|
344
348
|
|
|
345
349
|
|
|
346
350
|
@operation()
|
|
347
|
-
def hostname(hostname, hostname_file=None):
|
|
351
|
+
def hostname(hostname: str, hostname_file: str | None = None):
|
|
348
352
|
"""
|
|
349
353
|
Set the system hostname using ``hostnamectl`` or ``hostname`` on older systems.
|
|
350
354
|
|
|
@@ -402,8 +406,8 @@ def hostname(hostname, hostname_file=None):
|
|
|
402
406
|
|
|
403
407
|
@operation()
|
|
404
408
|
def sysctl(
|
|
405
|
-
key,
|
|
406
|
-
value,
|
|
409
|
+
key: str,
|
|
410
|
+
value: str | int | list[str | int],
|
|
407
411
|
persist=False,
|
|
408
412
|
persist_file="/etc/sysctl.conf",
|
|
409
413
|
):
|
|
@@ -449,12 +453,12 @@ def sysctl(
|
|
|
449
453
|
|
|
450
454
|
@operation()
|
|
451
455
|
def service(
|
|
452
|
-
service,
|
|
456
|
+
service: str,
|
|
453
457
|
running=True,
|
|
454
458
|
restarted=False,
|
|
455
459
|
reloaded=False,
|
|
456
|
-
command=None,
|
|
457
|
-
enabled=None,
|
|
460
|
+
command: str | None = None,
|
|
461
|
+
enabled: bool | None = None,
|
|
458
462
|
):
|
|
459
463
|
"""
|
|
460
464
|
Manage the state of services. This command checks for the presence of all the
|
|
@@ -489,6 +493,9 @@ def service(
|
|
|
489
493
|
elif host.get_fact(Which, command="initctl"):
|
|
490
494
|
service_operation = upstart.service
|
|
491
495
|
|
|
496
|
+
elif host.get_fact(Which, command="sv"):
|
|
497
|
+
service_operation = runit.service
|
|
498
|
+
|
|
492
499
|
elif (
|
|
493
500
|
host.get_fact(Which, command="service")
|
|
494
501
|
or host.get_fact(Link, path="/etc/init.d")
|
|
@@ -518,7 +525,7 @@ def service(
|
|
|
518
525
|
|
|
519
526
|
@operation()
|
|
520
527
|
def packages(
|
|
521
|
-
packages,
|
|
528
|
+
packages: str | list[str],
|
|
522
529
|
present=True,
|
|
523
530
|
):
|
|
524
531
|
"""
|
|
@@ -583,16 +590,16 @@ def packages(
|
|
|
583
590
|
|
|
584
591
|
@operation()
|
|
585
592
|
def crontab(
|
|
586
|
-
command,
|
|
593
|
+
command: str,
|
|
587
594
|
present=True,
|
|
588
|
-
user=None,
|
|
589
|
-
cron_name=None,
|
|
595
|
+
user: str | None = None,
|
|
596
|
+
cron_name: str | None = None,
|
|
590
597
|
minute="*",
|
|
591
598
|
hour="*",
|
|
592
599
|
month="*",
|
|
593
600
|
day_of_week="*",
|
|
594
601
|
day_of_month="*",
|
|
595
|
-
special_time=None,
|
|
602
|
+
special_time: str | None = None,
|
|
596
603
|
interpolate_variables=False,
|
|
597
604
|
):
|
|
598
605
|
"""
|
|
@@ -663,7 +670,7 @@ def crontab(
|
|
|
663
670
|
|
|
664
671
|
exists = existing_crontab is not None
|
|
665
672
|
|
|
666
|
-
edit_commands = []
|
|
673
|
+
edit_commands: list[str | StringCommand] = []
|
|
667
674
|
temp_filename = host.get_temp_filename()
|
|
668
675
|
|
|
669
676
|
if special_time:
|
|
@@ -758,7 +765,7 @@ def crontab(
|
|
|
758
765
|
|
|
759
766
|
|
|
760
767
|
@operation()
|
|
761
|
-
def group(group, present=True, system=False, gid=None):
|
|
768
|
+
def group(group: str, present=True, system=False, gid: int | str | None = None):
|
|
762
769
|
"""
|
|
763
770
|
Add/remove system groups.
|
|
764
771
|
|
|
@@ -827,12 +834,12 @@ def group(group, present=True, system=False, gid=None):
|
|
|
827
834
|
|
|
828
835
|
@operation()
|
|
829
836
|
def user_authorized_keys(
|
|
830
|
-
user,
|
|
831
|
-
public_keys,
|
|
832
|
-
group=None,
|
|
837
|
+
user: str,
|
|
838
|
+
public_keys: str | list[str],
|
|
839
|
+
group: str | None = None,
|
|
833
840
|
delete_keys=False,
|
|
834
|
-
authorized_key_directory=None,
|
|
835
|
-
authorized_key_filename=None,
|
|
841
|
+
authorized_key_directory: str | None = None,
|
|
842
|
+
authorized_key_filename: str | None = None,
|
|
836
843
|
):
|
|
837
844
|
"""
|
|
838
845
|
Manage `authorized_keys` of system users.
|
|
@@ -858,7 +865,9 @@ def user_authorized_keys(
|
|
|
858
865
|
"""
|
|
859
866
|
|
|
860
867
|
if not authorized_key_directory:
|
|
861
|
-
|
|
868
|
+
home = host.get_fact(Home, user=user)
|
|
869
|
+
authorized_key_directory = f"{home}/.ssh"
|
|
870
|
+
|
|
862
871
|
if not authorized_key_filename:
|
|
863
872
|
authorized_key_filename = "authorized_keys"
|
|
864
873
|
|
|
@@ -923,22 +932,22 @@ def user_authorized_keys(
|
|
|
923
932
|
|
|
924
933
|
@operation()
|
|
925
934
|
def user(
|
|
926
|
-
user,
|
|
935
|
+
user: str,
|
|
927
936
|
present=True,
|
|
928
|
-
home=None,
|
|
929
|
-
shell=None,
|
|
930
|
-
group=None,
|
|
931
|
-
groups=None,
|
|
932
|
-
public_keys=None,
|
|
937
|
+
home: str | None = None,
|
|
938
|
+
shell: str | None = None,
|
|
939
|
+
group: str | None = None,
|
|
940
|
+
groups: list[str] | None = None,
|
|
941
|
+
public_keys: str | list[str] | None = None,
|
|
933
942
|
delete_keys=False,
|
|
934
943
|
ensure_home=True,
|
|
935
944
|
create_home=False,
|
|
936
945
|
system=False,
|
|
937
|
-
uid=None,
|
|
938
|
-
comment=None,
|
|
946
|
+
uid: int | None = None,
|
|
947
|
+
comment: str | None = None,
|
|
939
948
|
add_deploy_dir=True,
|
|
940
949
|
unique=True,
|
|
941
|
-
password=None,
|
|
950
|
+
password: str | None = None,
|
|
942
951
|
):
|
|
943
952
|
"""
|
|
944
953
|
Add/remove/update system users & their ssh `authorized_keys`.
|
|
@@ -1124,7 +1133,7 @@ def user(
|
|
|
1124
1133
|
existing_user["password"] = password
|
|
1125
1134
|
|
|
1126
1135
|
# Ensure home directory ownership
|
|
1127
|
-
if ensure_home:
|
|
1136
|
+
if ensure_home and home:
|
|
1128
1137
|
yield from files.directory._inner(
|
|
1129
1138
|
path=home,
|
|
1130
1139
|
user=user,
|
|
@@ -1147,7 +1156,7 @@ def user(
|
|
|
1147
1156
|
|
|
1148
1157
|
@operation()
|
|
1149
1158
|
def locale(
|
|
1150
|
-
locale,
|
|
1159
|
+
locale: str,
|
|
1151
1160
|
present=True,
|
|
1152
1161
|
):
|
|
1153
1162
|
"""
|
|
@@ -1217,10 +1226,10 @@ def locale(
|
|
|
1217
1226
|
|
|
1218
1227
|
@operation()
|
|
1219
1228
|
def security_limit(
|
|
1220
|
-
domain,
|
|
1221
|
-
limit_type,
|
|
1222
|
-
item,
|
|
1223
|
-
value,
|
|
1229
|
+
domain: str,
|
|
1230
|
+
limit_type: str,
|
|
1231
|
+
item: str,
|
|
1232
|
+
value: int,
|
|
1224
1233
|
):
|
|
1225
1234
|
"""
|
|
1226
1235
|
Edit /etc/security/limits.conf configuration.
|
|
@@ -1239,7 +1248,7 @@ def security_limit(
|
|
|
1239
1248
|
domain='*',
|
|
1240
1249
|
limit_type='soft',
|
|
1241
1250
|
item='nofile',
|
|
1242
|
-
value=
|
|
1251
|
+
value=1024,
|
|
1243
1252
|
)
|
|
1244
1253
|
"""
|
|
1245
1254
|
|
pyinfra/operations/snap.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Manage snap packages. See https://snapcraft.io/
|
|
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.snap import SnapPackage, SnapPackages
|
|
@@ -9,7 +11,7 @@ from pyinfra.facts.snap import SnapPackage, SnapPackages
|
|
|
9
11
|
|
|
10
12
|
@operation()
|
|
11
13
|
def package(
|
|
12
|
-
packages=None,
|
|
14
|
+
packages: str | list[str] | None = None,
|
|
13
15
|
channel="latest/stable",
|
|
14
16
|
classic=False,
|
|
15
17
|
present=True,
|
pyinfra/operations/ssh.py
CHANGED
|
@@ -4,6 +4,8 @@ Execute commands and up/download files *from* the remote host.
|
|
|
4
4
|
Eg: ``pyinfra -> inventory-host.net <-> another-host.net``
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
7
9
|
import shlex
|
|
8
10
|
|
|
9
11
|
from pyinfra import host
|
|
@@ -15,7 +17,7 @@ from . import files
|
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
@operation()
|
|
18
|
-
def keyscan(hostname, force=False, port=22):
|
|
20
|
+
def keyscan(hostname: str, force=False, port=22):
|
|
19
21
|
"""
|
|
20
22
|
Check/add hosts to the ``~/.ssh/known_hosts`` file.
|
|
21
23
|
|
|
@@ -63,7 +65,7 @@ def keyscan(hostname, force=False, port=22):
|
|
|
63
65
|
|
|
64
66
|
|
|
65
67
|
@operation(is_idempotent=False)
|
|
66
|
-
def command(hostname, command, user=None, port=22):
|
|
68
|
+
def command(hostname: str, command: str, user: str | None = None, port=22):
|
|
67
69
|
"""
|
|
68
70
|
Execute commands on other servers over SSH.
|
|
69
71
|
|
|
@@ -95,11 +97,11 @@ def command(hostname, command, user=None, port=22):
|
|
|
95
97
|
|
|
96
98
|
@operation(is_idempotent=False)
|
|
97
99
|
def upload(
|
|
98
|
-
hostname,
|
|
99
|
-
filename,
|
|
100
|
-
remote_filename=None,
|
|
100
|
+
hostname: str,
|
|
101
|
+
filename: str,
|
|
102
|
+
remote_filename: str | None = None,
|
|
101
103
|
port=22,
|
|
102
|
-
user=None,
|
|
104
|
+
user: str | None = None,
|
|
103
105
|
use_remote_sudo=False,
|
|
104
106
|
ssh_keyscan=False,
|
|
105
107
|
):
|
|
@@ -159,12 +161,12 @@ def upload(
|
|
|
159
161
|
|
|
160
162
|
@operation()
|
|
161
163
|
def download(
|
|
162
|
-
hostname,
|
|
163
|
-
filename,
|
|
164
|
-
local_filename=None,
|
|
164
|
+
hostname: str,
|
|
165
|
+
filename: str,
|
|
166
|
+
local_filename: str | None = None,
|
|
165
167
|
force=False,
|
|
166
168
|
port=22,
|
|
167
|
-
user=None,
|
|
169
|
+
user: str | None = None,
|
|
168
170
|
ssh_keyscan=False,
|
|
169
171
|
):
|
|
170
172
|
"""
|