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/ssh.py
CHANGED
|
@@ -4,9 +4,11 @@ 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
|
-
from pyinfra import host
|
|
11
|
+
from pyinfra import host
|
|
10
12
|
from pyinfra.api import OperationError, operation
|
|
11
13
|
from pyinfra.facts.files import File, FindInFile
|
|
12
14
|
from pyinfra.facts.server import Home
|
|
@@ -14,8 +16,8 @@ from pyinfra.facts.server import Home
|
|
|
14
16
|
from . import files
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
@operation
|
|
18
|
-
def keyscan(hostname, force=False, port=22):
|
|
19
|
+
@operation()
|
|
20
|
+
def keyscan(hostname: str, force=False, port=22):
|
|
19
21
|
"""
|
|
20
22
|
Check/add hosts to the ``~/.ssh/known_hosts`` file.
|
|
21
23
|
|
|
@@ -34,7 +36,7 @@ def keyscan(hostname, force=False, port=22):
|
|
|
34
36
|
|
|
35
37
|
homedir = host.get_fact(Home)
|
|
36
38
|
|
|
37
|
-
yield from files.directory(
|
|
39
|
+
yield from files.directory._inner(
|
|
38
40
|
"{0}/.ssh".format(homedir),
|
|
39
41
|
mode=700,
|
|
40
42
|
)
|
|
@@ -45,7 +47,6 @@ def keyscan(hostname, force=False, port=22):
|
|
|
45
47
|
pattern=hostname,
|
|
46
48
|
)
|
|
47
49
|
|
|
48
|
-
did_keyscan = False
|
|
49
50
|
keyscan_command = "ssh-keyscan -p {0} {1} >> {2}/.ssh/known_hosts".format(
|
|
50
51
|
port,
|
|
51
52
|
hostname,
|
|
@@ -54,26 +55,17 @@ def keyscan(hostname, force=False, port=22):
|
|
|
54
55
|
|
|
55
56
|
if not hostname_present:
|
|
56
57
|
yield keyscan_command
|
|
57
|
-
did_keyscan = True
|
|
58
58
|
|
|
59
59
|
elif force:
|
|
60
60
|
yield "ssh-keygen -R {0}".format(hostname)
|
|
61
61
|
yield keyscan_command
|
|
62
|
-
did_keyscan = True
|
|
63
62
|
|
|
64
63
|
else:
|
|
65
64
|
host.noop("host key for {0} already exists".format(hostname))
|
|
66
65
|
|
|
67
|
-
if did_keyscan:
|
|
68
|
-
host.create_fact(
|
|
69
|
-
FindInFile,
|
|
70
|
-
kwargs={"path": "{0}/.ssh/known_hosts".format(homedir), "pattern": hostname},
|
|
71
|
-
data=["{0} unknown unknown".format(hostname)],
|
|
72
|
-
)
|
|
73
|
-
|
|
74
66
|
|
|
75
67
|
@operation(is_idempotent=False)
|
|
76
|
-
def command(hostname, command, user=None, port=22):
|
|
68
|
+
def command(hostname: str, command: str, user: str | None = None, port=22):
|
|
77
69
|
"""
|
|
78
70
|
Execute commands on other servers over SSH.
|
|
79
71
|
|
|
@@ -105,11 +97,11 @@ def command(hostname, command, user=None, port=22):
|
|
|
105
97
|
|
|
106
98
|
@operation(is_idempotent=False)
|
|
107
99
|
def upload(
|
|
108
|
-
hostname,
|
|
109
|
-
filename,
|
|
110
|
-
remote_filename=None,
|
|
100
|
+
hostname: str,
|
|
101
|
+
filename: str,
|
|
102
|
+
remote_filename: str | None = None,
|
|
111
103
|
port=22,
|
|
112
|
-
user=None,
|
|
104
|
+
user: str | None = None,
|
|
113
105
|
use_remote_sudo=False,
|
|
114
106
|
ssh_keyscan=False,
|
|
115
107
|
):
|
|
@@ -133,7 +125,7 @@ def upload(
|
|
|
133
125
|
connection_target = "@".join((user, hostname))
|
|
134
126
|
|
|
135
127
|
if ssh_keyscan:
|
|
136
|
-
yield from keyscan(hostname)
|
|
128
|
+
yield from keyscan._inner(hostname)
|
|
137
129
|
|
|
138
130
|
# If we're not using sudo on the remote side, just scp the file over
|
|
139
131
|
if not use_remote_sudo:
|
|
@@ -146,7 +138,7 @@ def upload(
|
|
|
146
138
|
|
|
147
139
|
else:
|
|
148
140
|
# Otherwise - we need a temporary location for the file
|
|
149
|
-
temp_remote_filename =
|
|
141
|
+
temp_remote_filename = host.get_temp_filename()
|
|
150
142
|
|
|
151
143
|
# scp it to the temporary location
|
|
152
144
|
upload_cmd = "scp -P {0} {1} {2}:{3}".format(
|
|
@@ -159,7 +151,7 @@ def upload(
|
|
|
159
151
|
yield upload_cmd
|
|
160
152
|
|
|
161
153
|
# And sudo sudo to move it
|
|
162
|
-
yield from command(
|
|
154
|
+
yield from command._inner(
|
|
163
155
|
hostname=hostname,
|
|
164
156
|
command="sudo mv {0} {1}".format(temp_remote_filename, remote_filename),
|
|
165
157
|
port=port,
|
|
@@ -167,14 +159,14 @@ def upload(
|
|
|
167
159
|
)
|
|
168
160
|
|
|
169
161
|
|
|
170
|
-
@operation
|
|
162
|
+
@operation()
|
|
171
163
|
def download(
|
|
172
|
-
hostname,
|
|
173
|
-
filename,
|
|
174
|
-
local_filename=None,
|
|
164
|
+
hostname: str,
|
|
165
|
+
filename: str,
|
|
166
|
+
local_filename: str | None = None,
|
|
175
167
|
force=False,
|
|
176
168
|
port=22,
|
|
177
|
-
user=None,
|
|
169
|
+
user: str | None = None,
|
|
178
170
|
ssh_keyscan=False,
|
|
179
171
|
):
|
|
180
172
|
"""
|
|
@@ -213,7 +205,7 @@ def download(
|
|
|
213
205
|
connection_target = "@".join((user, hostname))
|
|
214
206
|
|
|
215
207
|
if ssh_keyscan:
|
|
216
|
-
yield from keyscan(hostname)
|
|
208
|
+
yield from keyscan._inner(hostname)
|
|
217
209
|
|
|
218
210
|
# Download the file with scp
|
|
219
211
|
yield "scp -P {0} {1}:{2} {3}".format(
|
|
@@ -222,8 +214,3 @@ def download(
|
|
|
222
214
|
filename,
|
|
223
215
|
local_filename,
|
|
224
216
|
)
|
|
225
|
-
host.create_fact(
|
|
226
|
-
File,
|
|
227
|
-
kwargs={"path": local_filename},
|
|
228
|
-
data={"mode": None, "group": None, "user": user, "mtime": None},
|
|
229
|
-
)
|
pyinfra/operations/systemd.py
CHANGED
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
Manage systemd services.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import shlex
|
|
8
|
+
|
|
5
9
|
from pyinfra import host
|
|
6
|
-
from pyinfra.api import operation
|
|
10
|
+
from pyinfra.api import StringCommand, operation
|
|
7
11
|
from pyinfra.facts.systemd import SystemdEnabled, SystemdStatus, _make_systemctl_cmd
|
|
8
12
|
|
|
9
13
|
from .util.service import handle_service_control
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
@operation(is_idempotent=False)
|
|
13
|
-
def daemon_reload(user_mode=False, machine=None, user_name=None):
|
|
17
|
+
def daemon_reload(user_mode=False, machine: str | None = None, user_name: str | None = None):
|
|
14
18
|
"""
|
|
15
19
|
Reload the systemd daemon to read unit file changes.
|
|
16
20
|
|
|
@@ -25,24 +29,24 @@ def daemon_reload(user_mode=False, machine=None, user_name=None):
|
|
|
25
29
|
user_name=user_name,
|
|
26
30
|
)
|
|
27
31
|
|
|
28
|
-
yield "
|
|
32
|
+
yield StringCommand(systemctl_cmd, "daemon-reload")
|
|
29
33
|
|
|
30
34
|
|
|
31
|
-
_daemon_reload = daemon_reload # noqa: E305
|
|
35
|
+
_daemon_reload = daemon_reload._inner # noqa: E305
|
|
32
36
|
|
|
33
37
|
|
|
34
|
-
@operation
|
|
38
|
+
@operation()
|
|
35
39
|
def service(
|
|
36
|
-
service,
|
|
40
|
+
service: str,
|
|
37
41
|
running=True,
|
|
38
42
|
restarted=False,
|
|
39
43
|
reloaded=False,
|
|
40
|
-
command=None,
|
|
41
|
-
enabled=None,
|
|
44
|
+
command: str | None = None,
|
|
45
|
+
enabled: bool | None = None,
|
|
42
46
|
daemon_reload=False,
|
|
43
47
|
user_mode=False,
|
|
44
|
-
machine=None,
|
|
45
|
-
user_name=None,
|
|
48
|
+
machine: str | None = None,
|
|
49
|
+
user_name: str | None = None,
|
|
46
50
|
):
|
|
47
51
|
"""
|
|
48
52
|
Manage the state of systemd managed units.
|
|
@@ -117,8 +121,9 @@ def service(
|
|
|
117
121
|
user_mode=user_mode,
|
|
118
122
|
machine=machine,
|
|
119
123
|
user_name=user_name,
|
|
124
|
+
services=[service],
|
|
120
125
|
),
|
|
121
|
-
" ".join([systemctl_cmd, "{1}", "{0}"]),
|
|
126
|
+
" ".join([systemctl_cmd.get_raw_value(), "{1}", "{0}"]),
|
|
122
127
|
running,
|
|
123
128
|
restarted,
|
|
124
129
|
reloaded,
|
|
@@ -131,15 +136,14 @@ def service(
|
|
|
131
136
|
user_mode=user_mode,
|
|
132
137
|
machine=machine,
|
|
133
138
|
user_name=user_name,
|
|
139
|
+
services=[service],
|
|
134
140
|
)
|
|
135
141
|
is_enabled = systemd_enabled.get(service, False)
|
|
136
142
|
|
|
137
143
|
# Isn't enabled and want enabled?
|
|
138
144
|
if not is_enabled and enabled is True:
|
|
139
|
-
yield "{0} enable {1}".format(systemctl_cmd, service)
|
|
140
|
-
systemd_enabled[service] = True
|
|
145
|
+
yield "{0} enable {1}".format(systemctl_cmd, shlex.quote(service))
|
|
141
146
|
|
|
142
147
|
# Is enabled and want disabled?
|
|
143
148
|
elif is_enabled and enabled is False:
|
|
144
|
-
yield "{0} disable {1}".format(systemctl_cmd, service)
|
|
145
|
-
systemd_enabled[service] = False
|
|
149
|
+
yield "{0} disable {1}".format(systemctl_cmd, shlex.quote(service))
|
pyinfra/operations/sysvinit.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Manage sysvinit services (``/etc/init.d``).
|
|
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.files import FindLinks
|
|
@@ -12,14 +14,14 @@ from . import files
|
|
|
12
14
|
from .util.service import handle_service_control
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
@operation
|
|
17
|
+
@operation()
|
|
16
18
|
def service(
|
|
17
|
-
service,
|
|
19
|
+
service: str,
|
|
18
20
|
running=True,
|
|
19
21
|
restarted=False,
|
|
20
22
|
reloaded=False,
|
|
21
|
-
enabled=None,
|
|
22
|
-
command=None,
|
|
23
|
+
enabled: bool | None = None,
|
|
24
|
+
command: str | None = None,
|
|
23
25
|
):
|
|
24
26
|
"""
|
|
25
27
|
Manage the state of SysV Init (/etc/init.d) services.
|
|
@@ -75,36 +77,27 @@ def service(
|
|
|
75
77
|
# If no links exist, attempt to enable the service using distro-specific commands
|
|
76
78
|
if enabled is True and not start_links:
|
|
77
79
|
distro = host.get_fact(LinuxDistribution).get("name")
|
|
78
|
-
did_add = False
|
|
79
80
|
|
|
80
81
|
if distro in ("Ubuntu", "Debian"):
|
|
81
82
|
yield "update-rc.d {0} defaults".format(service)
|
|
82
|
-
did_add = True
|
|
83
83
|
|
|
84
84
|
elif distro in ("CentOS", "Fedora", "Red Hat Enterprise Linux"):
|
|
85
85
|
yield "chkconfig {0} --add".format(service)
|
|
86
86
|
yield "chkconfig {0} on".format(service)
|
|
87
|
-
did_add = True
|
|
88
87
|
|
|
89
88
|
elif distro == "Gentoo":
|
|
90
89
|
yield "rc-update add {0} default".format(service)
|
|
91
|
-
did_add = True
|
|
92
|
-
|
|
93
|
-
# Add a single start link to the fact if we executed a command
|
|
94
|
-
if did_add:
|
|
95
|
-
start_links.append("/etc/rc0.d/S20{0}".format(service))
|
|
96
90
|
|
|
97
91
|
# Remove any /etc/rcX.d/<service> start links
|
|
98
92
|
elif enabled is False:
|
|
99
93
|
# No state checking, just blindly remove any that exist
|
|
100
94
|
for link in start_links:
|
|
101
95
|
yield "rm -f {0}".format(link)
|
|
102
|
-
start_links.remove(link)
|
|
103
96
|
|
|
104
97
|
|
|
105
|
-
@operation
|
|
98
|
+
@operation()
|
|
106
99
|
def enable(
|
|
107
|
-
service,
|
|
100
|
+
service: str,
|
|
108
101
|
start_priority=20,
|
|
109
102
|
stop_priority=80,
|
|
110
103
|
start_levels=(2, 3, 4, 5),
|
|
@@ -142,7 +135,7 @@ def enable(
|
|
|
142
135
|
|
|
143
136
|
# Ensure all the new links exist
|
|
144
137
|
for link in links:
|
|
145
|
-
yield from files.link(
|
|
138
|
+
yield from files.link._inner(
|
|
146
139
|
path=link,
|
|
147
140
|
target="/etc/init.d/{0}".format(service),
|
|
148
141
|
)
|
pyinfra/operations/upstart.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Manage upstart services.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
5
7
|
from io import StringIO
|
|
6
8
|
|
|
7
9
|
from pyinfra import host
|
|
@@ -12,14 +14,14 @@ from . import files
|
|
|
12
14
|
from .util.service import handle_service_control
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
@operation
|
|
17
|
+
@operation()
|
|
16
18
|
def service(
|
|
17
|
-
service,
|
|
19
|
+
service: str,
|
|
18
20
|
running=True,
|
|
19
21
|
restarted=False,
|
|
20
22
|
reloaded=False,
|
|
21
|
-
command=None,
|
|
22
|
-
enabled=None,
|
|
23
|
+
command: str | None = None,
|
|
24
|
+
enabled: bool | None = None,
|
|
23
25
|
):
|
|
24
26
|
"""
|
|
25
27
|
Manage the state of upstart managed services.
|
|
@@ -52,15 +54,15 @@ def service(
|
|
|
52
54
|
# Upstart jobs are setup w/runlevels etc in their config files, so here we just check
|
|
53
55
|
# there's no override file.
|
|
54
56
|
if enabled is True:
|
|
55
|
-
yield from files.file(
|
|
56
|
-
"/etc/init/{0}.override".format(service),
|
|
57
|
+
yield from files.file._inner(
|
|
58
|
+
path="/etc/init/{0}.override".format(service),
|
|
57
59
|
present=False,
|
|
58
60
|
)
|
|
59
61
|
|
|
60
62
|
# Set the override file to "manual" to disable automatic start
|
|
61
63
|
elif enabled is False:
|
|
62
64
|
file = StringIO("manual\n")
|
|
63
|
-
yield from files.put(
|
|
65
|
+
yield from files.put._inner(
|
|
64
66
|
src=file,
|
|
65
67
|
dest="/etc/init/{0}.override".format(service),
|
|
66
68
|
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Callable
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from pyinfra.api.operation import OperationMeta
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def any_changed(*args: "OperationMeta") -> Callable[[], bool]:
|
|
8
|
+
return lambda: any((meta.did_change() for meta in args))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def all_changed(*args: "OperationMeta") -> Callable[[], bool]:
|
|
12
|
+
return lambda: all((meta.did_change() for meta in args))
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
from pyinfra.api import OperationError
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _create_container(**kwargs):
|
|
5
|
+
command = []
|
|
6
|
+
|
|
7
|
+
networks = kwargs["networks"] if kwargs["networks"] else []
|
|
8
|
+
ports = kwargs["ports"] if kwargs["ports"] else []
|
|
9
|
+
volumes = kwargs["volumes"] if kwargs["volumes"] else []
|
|
10
|
+
env_vars = kwargs["env_vars"] if kwargs["env_vars"] else []
|
|
11
|
+
|
|
12
|
+
if kwargs["image"] == "":
|
|
13
|
+
raise OperationError("missing 1 required argument: 'image'")
|
|
14
|
+
|
|
15
|
+
command.append("docker container create --name {0}".format(kwargs["container"]))
|
|
16
|
+
|
|
17
|
+
for network in networks:
|
|
18
|
+
command.append("--network {0}".format(network))
|
|
19
|
+
|
|
20
|
+
for port in ports:
|
|
21
|
+
command.append("-p {0}".format(port))
|
|
22
|
+
|
|
23
|
+
for volume in volumes:
|
|
24
|
+
command.append("-v {0}".format(volume))
|
|
25
|
+
|
|
26
|
+
for env_var in env_vars:
|
|
27
|
+
command.append("-e {0}".format(env_var))
|
|
28
|
+
|
|
29
|
+
if kwargs["pull_always"]:
|
|
30
|
+
command.append("--pull always")
|
|
31
|
+
|
|
32
|
+
command.append(kwargs["image"])
|
|
33
|
+
|
|
34
|
+
if kwargs["start"]:
|
|
35
|
+
command.append("; {0}".format(_start_container(container=kwargs["container"])))
|
|
36
|
+
|
|
37
|
+
return " ".join(command)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _remove_container(**kwargs):
|
|
41
|
+
return "docker container rm -f {0}".format(kwargs["container"])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _start_container(**kwargs):
|
|
45
|
+
return "docker container start {0}".format(kwargs["container"])
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _stop_container(**kwargs):
|
|
49
|
+
return "docker container stop {0}".format(kwargs["container"])
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _pull_image(**kwargs):
|
|
53
|
+
return "docker image pull {0}".format(kwargs["image"])
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _remove_image(**kwargs):
|
|
57
|
+
return "docker image rm {0}".format(kwargs["image"])
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _prune_command(**kwargs):
|
|
61
|
+
command = ["docker system prune"]
|
|
62
|
+
|
|
63
|
+
if kwargs["all"]:
|
|
64
|
+
command.append("-a")
|
|
65
|
+
|
|
66
|
+
if kwargs["filter"] != "":
|
|
67
|
+
command.append("--filter={0}".format(kwargs["filter"]))
|
|
68
|
+
|
|
69
|
+
if kwargs["volumes"]:
|
|
70
|
+
command.append("--volumes")
|
|
71
|
+
|
|
72
|
+
command.append("-f")
|
|
73
|
+
|
|
74
|
+
return " ".join(command)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _create_volume(**kwargs):
|
|
78
|
+
command = []
|
|
79
|
+
labels = kwargs["labels"] if kwargs["labels"] else []
|
|
80
|
+
|
|
81
|
+
command.append("docker volume create {0}".format(kwargs["volume"]))
|
|
82
|
+
|
|
83
|
+
if kwargs["driver"] != "":
|
|
84
|
+
command.append("-d {0}".format(kwargs["driver"]))
|
|
85
|
+
|
|
86
|
+
for label in labels:
|
|
87
|
+
command.append("--label {0}".format(label))
|
|
88
|
+
|
|
89
|
+
return " ".join(command)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _remove_volume(**kwargs):
|
|
93
|
+
return "docker image rm {0}".format(kwargs["volume"])
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _create_network(**kwargs):
|
|
97
|
+
command = []
|
|
98
|
+
opts = kwargs["opts"] if kwargs["opts"] else []
|
|
99
|
+
ipam_opts = kwargs["ipam_opts"] if kwargs["ipam_opts"] else []
|
|
100
|
+
labels = kwargs["labels"] if kwargs["labels"] else []
|
|
101
|
+
|
|
102
|
+
command.append("docker network create {0}".format(kwargs["network"]))
|
|
103
|
+
if kwargs["driver"] != "":
|
|
104
|
+
command.append("-d {0}".format(kwargs["driver"]))
|
|
105
|
+
|
|
106
|
+
if kwargs["gateway"] != "":
|
|
107
|
+
command.append("--gateway {0}".format(kwargs["gateway"]))
|
|
108
|
+
|
|
109
|
+
if kwargs["ip_range"] != "":
|
|
110
|
+
command.append("--ip-range {0}".format(kwargs["ip_range"]))
|
|
111
|
+
|
|
112
|
+
if kwargs["ipam_driver"] != "":
|
|
113
|
+
command.append("--ipam-driver {0}".format(kwargs["ipam_driver"]))
|
|
114
|
+
|
|
115
|
+
if kwargs["subnet"] != "":
|
|
116
|
+
command.append("--subnet {0}".format(kwargs["subnet"]))
|
|
117
|
+
|
|
118
|
+
if kwargs["scope"] != "":
|
|
119
|
+
command.append("--scope {0}".format(kwargs["scope"]))
|
|
120
|
+
|
|
121
|
+
if kwargs["ingress"]:
|
|
122
|
+
command.append("--ingress")
|
|
123
|
+
|
|
124
|
+
if kwargs["attachable"]:
|
|
125
|
+
command.append("--attachable")
|
|
126
|
+
|
|
127
|
+
for opt in opts:
|
|
128
|
+
command.append("--opt {0}".format(opt))
|
|
129
|
+
|
|
130
|
+
for opt in ipam_opts:
|
|
131
|
+
command.append("--ipam-opt {0}".format(opt))
|
|
132
|
+
|
|
133
|
+
for label in labels:
|
|
134
|
+
command.append("--label {0}".format(label))
|
|
135
|
+
return " ".join(command)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _remove_network(**kwargs):
|
|
139
|
+
return "docker network rm {0}".format(kwargs["network"])
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def handle_docker(resource, command, **kwargs):
|
|
143
|
+
container_commands = {
|
|
144
|
+
"create": _create_container,
|
|
145
|
+
"remove": _remove_container,
|
|
146
|
+
"start": _start_container,
|
|
147
|
+
"stop": _stop_container,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
image_commands = {
|
|
151
|
+
"pull": _pull_image,
|
|
152
|
+
"remove": _remove_image,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
volume_commands = {
|
|
156
|
+
"create": _create_volume,
|
|
157
|
+
"remove": _remove_volume,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
network_commands = {
|
|
161
|
+
"create": _create_network,
|
|
162
|
+
"remove": _remove_network,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
system_commands = {
|
|
166
|
+
"prune": _prune_command,
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
docker_commands = {
|
|
170
|
+
"container": container_commands,
|
|
171
|
+
"image": image_commands,
|
|
172
|
+
"volume": volume_commands,
|
|
173
|
+
"network": network_commands,
|
|
174
|
+
"system": system_commands,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return docker_commands[resource][command](**kwargs)
|
pyinfra/operations/util/files.py
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import re
|
|
2
4
|
from datetime import datetime
|
|
3
5
|
|
|
4
6
|
from pyinfra.api import QuoteString, StringCommand
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
def unix_path_join(*parts):
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return "/".join(
|
|
9
|
+
def unix_path_join(*parts) -> str:
|
|
10
|
+
part_list = list(parts)
|
|
11
|
+
part_list[0:-1] = [part.rstrip("/") for part in part_list[0:-1]]
|
|
12
|
+
return "/".join(part_list)
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
def ensure_mode_int(mode):
|
|
15
|
+
def ensure_mode_int(mode: str | int | None) -> int | str | None:
|
|
14
16
|
# Already an int (/None)?
|
|
15
17
|
if isinstance(mode, int) or mode is None:
|
|
16
18
|
return mode
|
|
@@ -26,19 +28,19 @@ def ensure_mode_int(mode):
|
|
|
26
28
|
return mode
|
|
27
29
|
|
|
28
30
|
|
|
29
|
-
def get_timestamp():
|
|
31
|
+
def get_timestamp() -> str:
|
|
30
32
|
return datetime.now().strftime("%y%m%d%H%M")
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
def sed_replace(
|
|
34
|
-
filename,
|
|
35
|
-
line,
|
|
36
|
-
replace,
|
|
37
|
-
flags=None,
|
|
36
|
+
filename: str,
|
|
37
|
+
line: str,
|
|
38
|
+
replace: str,
|
|
39
|
+
flags: list[str] | None = None,
|
|
38
40
|
backup=False,
|
|
39
41
|
interpolate_variables=False,
|
|
40
|
-
):
|
|
41
|
-
|
|
42
|
+
) -> StringCommand:
|
|
43
|
+
flags_str = "".join(flags) if flags else ""
|
|
42
44
|
|
|
43
45
|
line = line.replace("/", r"\/")
|
|
44
46
|
replace = str(replace)
|
|
@@ -57,7 +59,7 @@ def sed_replace(
|
|
|
57
59
|
replace = replace.replace("'", "'\"'\"'")
|
|
58
60
|
sed_script_formatter = "'s/{0}/{1}/{2}'"
|
|
59
61
|
|
|
60
|
-
sed_script = sed_script_formatter.format(line, replace,
|
|
62
|
+
sed_script = sed_script_formatter.format(line, replace, flags_str)
|
|
61
63
|
|
|
62
64
|
sed_command = StringCommand(
|
|
63
65
|
"sed",
|
|
@@ -73,7 +75,7 @@ def sed_replace(
|
|
|
73
75
|
return sed_command
|
|
74
76
|
|
|
75
77
|
|
|
76
|
-
def chmod(target, mode, recursive=False):
|
|
78
|
+
def chmod(target: str, mode: str | int, recursive=False) -> StringCommand:
|
|
77
79
|
args = ["chmod"]
|
|
78
80
|
if recursive:
|
|
79
81
|
args.append("-R")
|
|
@@ -83,7 +85,13 @@ def chmod(target, mode, recursive=False):
|
|
|
83
85
|
return StringCommand(" ".join(args), QuoteString(target))
|
|
84
86
|
|
|
85
87
|
|
|
86
|
-
def chown(
|
|
88
|
+
def chown(
|
|
89
|
+
target: str,
|
|
90
|
+
user: str | None = None,
|
|
91
|
+
group: str | None = None,
|
|
92
|
+
recursive=False,
|
|
93
|
+
dereference=True,
|
|
94
|
+
) -> StringCommand:
|
|
87
95
|
command = "chown"
|
|
88
96
|
user_group = None
|
|
89
97
|
|
|
@@ -107,7 +115,7 @@ def chown(target, user, group=None, recursive=False, dereference=True):
|
|
|
107
115
|
return StringCommand(" ".join(args), user_group, QuoteString(target))
|
|
108
116
|
|
|
109
117
|
|
|
110
|
-
def adjust_regex(line, escape_regex_characters):
|
|
118
|
+
def adjust_regex(line: str, escape_regex_characters: bool) -> str:
|
|
111
119
|
"""
|
|
112
120
|
Ensure the regex starts with '^' and ends with '$' and escape regex characters if requested
|
|
113
121
|
"""
|