pyinfra 3.0b2__tar.gz → 3.0b3__tar.gz
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-3.0b2 → pyinfra-3.0b3}/CHANGELOG.md +1 -1
- {pyinfra-3.0b2/pyinfra.egg-info → pyinfra-3.0b3}/PKG-INFO +3 -3
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/docker.py +16 -0
- pyinfra-3.0b3/pyinfra/facts/runit.py +68 -0
- pyinfra-3.0b3/pyinfra/operations/docker.py +339 -0
- pyinfra-3.0b3/pyinfra/operations/runit.py +182 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/server.py +4 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/systemd.py +4 -2
- pyinfra-3.0b3/pyinfra/operations/util/docker.py +177 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/util/service.py +7 -5
- {pyinfra-3.0b2 → pyinfra-3.0b3/pyinfra.egg-info}/PKG-INFO +3 -3
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra.egg-info/SOURCES.txt +4 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra.egg-info/requires.txt +2 -2
- {pyinfra-3.0b2 → pyinfra-3.0b3}/setup.py +1 -1
- {pyinfra-3.0b2 → pyinfra-3.0b3}/LICENSE.md +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/MANIFEST.in +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/README.md +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/__main__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/arguments.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/arguments_typed.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/command.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/config.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/connect.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/connectors.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/deploy.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/exceptions.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/facts.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/host.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/inventory.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/operation.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/operations.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/state.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/util.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/base.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/chroot.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/docker.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/dockerssh.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/local.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/ssh.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/ssh_util.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/sshuserclient/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/sshuserclient/client.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/sshuserclient/config.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/terraform.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/util.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/vagrant.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/context.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/apk.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/apt.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/brew.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/bsdinit.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/cargo.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/choco.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/deb.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/dnf.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/files.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/gem.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/git.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/gpg.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/hardware.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/iptables.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/launchd.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/lxd.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/mysql.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/npm.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/openrc.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/pacman.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/pip.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/pkg.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/pkgin.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/postgres.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/postgresql.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/rpm.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/selinux.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/server.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/snap.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/systemd.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/sysvinit.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/upstart.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/util/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/util/databases.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/util/packaging.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/util/win_files.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/vzctl.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/xbps.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/yum.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/zypper.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/local.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/apk.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/apt.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/brew.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/bsdinit.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/cargo.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/choco.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/dnf.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/files.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/gem.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/git.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/iptables.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/launchd.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/lxd.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/mysql.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/npm.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/openrc.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/pacman.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/pip.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/pkg.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/pkgin.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/postgres.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/postgresql.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/puppet.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/python.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/selinux.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/snap.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/ssh.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/sysvinit.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/upstart.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/util/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/util/files.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/util/packaging.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/vzctl.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/xbps.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/yum.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/zypper.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/progress.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/py.typed +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/version.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra.egg-info/dependency_links.txt +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra.egg-info/entry_points.txt +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra.egg-info/top_level.txt +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/__main__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/commands.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/exceptions.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/inventory.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/log.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/main.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/prints.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/util.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/virtualenv.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/pyproject.toml +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/setup.cfg +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_arguments.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_command.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_config.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_deploys.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_facts.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_host.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_inventory.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_operations.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_util.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/test_cli.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/test_cli_deploy.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/test_cli_exceptions.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/test_cli_util.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/test_context_objects.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/util.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/__init__.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_chroot.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_docker.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_dockerssh.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_local.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_ssh.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_sshuserclient.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_terraform.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_util.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_vagrant.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_facts.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_global_arguments.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_operations.py +0 -0
- {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_operations_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyinfra
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.0b3
|
|
4
4
|
Summary: pyinfra automates/provisions/manages/deploys infrastructure.
|
|
5
5
|
Home-page: https://pyinfra.com
|
|
6
6
|
Author: Nick / Fizzadar
|
|
@@ -57,7 +57,7 @@ Requires-Dist: types-python-dateutil; extra == "test"
|
|
|
57
57
|
Requires-Dist: types-PyYAML; extra == "test"
|
|
58
58
|
Requires-Dist: types-setuptools; extra == "test"
|
|
59
59
|
Provides-Extra: docs
|
|
60
|
-
Requires-Dist: pyinfra-guzzle_sphinx_theme==0.
|
|
60
|
+
Requires-Dist: pyinfra-guzzle_sphinx_theme==0.16; extra == "docs"
|
|
61
61
|
Requires-Dist: myst-parser==2.0.0; extra == "docs"
|
|
62
62
|
Requires-Dist: sphinx==6.2.1; extra == "docs"
|
|
63
63
|
Provides-Extra: dev
|
|
@@ -75,7 +75,7 @@ Requires-Dist: types-paramiko; extra == "dev"
|
|
|
75
75
|
Requires-Dist: types-python-dateutil; extra == "dev"
|
|
76
76
|
Requires-Dist: types-PyYAML; extra == "dev"
|
|
77
77
|
Requires-Dist: types-setuptools; extra == "dev"
|
|
78
|
-
Requires-Dist: pyinfra-guzzle_sphinx_theme==0.
|
|
78
|
+
Requires-Dist: pyinfra-guzzle_sphinx_theme==0.16; extra == "dev"
|
|
79
79
|
Requires-Dist: myst-parser==2.0.0; extra == "dev"
|
|
80
80
|
Requires-Dist: sphinx==6.2.1; extra == "dev"
|
|
81
81
|
Requires-Dist: wheel; extra == "dev"
|
|
@@ -86,3 +86,19 @@ class DockerNetwork(DockerSingleMixin):
|
|
|
86
86
|
"""
|
|
87
87
|
|
|
88
88
|
docker_type = "network"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class DockerVolumes(DockerFactBase):
|
|
92
|
+
"""
|
|
93
|
+
Returns ``docker inspect`` output for all Docker volumes.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
command = "docker volume inspect `docker volume ls -q`"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class DockerVolume(DockerSingleMixin):
|
|
100
|
+
"""
|
|
101
|
+
Returns ``docker inspect`` output for a single Docker container.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
docker_type = "volume"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from pyinfra.api import FactBase
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RunitStatus(FactBase):
|
|
5
|
+
"""
|
|
6
|
+
Returns a dict of name -> status for runit services.
|
|
7
|
+
|
|
8
|
+
+ service: optionally check only for a single service
|
|
9
|
+
+ svdir: alternative ``SVDIR``
|
|
10
|
+
|
|
11
|
+
.. code:: python
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
'agetty-tty1': True, # service is running
|
|
15
|
+
'dhcpcd': False, # service is down
|
|
16
|
+
'wpa_supplicant': None, # service is managed, but not running or down,
|
|
17
|
+
# possibly in a fail state
|
|
18
|
+
}
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
requires_command = "sv"
|
|
22
|
+
default = dict
|
|
23
|
+
|
|
24
|
+
def command(self, service=None, svdir="/var/service"):
|
|
25
|
+
if service is None:
|
|
26
|
+
return (
|
|
27
|
+
'export SVDIR="{0}" && '
|
|
28
|
+
'cd "$SVDIR" && find * -maxdepth 0 -exec sv status {{}} + 2>/dev/null'
|
|
29
|
+
).format(svdir)
|
|
30
|
+
else:
|
|
31
|
+
return 'SVDIR="{0}" sv status "{1}"'.format(svdir, service)
|
|
32
|
+
|
|
33
|
+
def process(self, output):
|
|
34
|
+
services = {}
|
|
35
|
+
for line in output:
|
|
36
|
+
statusstr, service, _ = line.split(sep=": ", maxsplit=2)
|
|
37
|
+
status = None
|
|
38
|
+
|
|
39
|
+
if statusstr == "run":
|
|
40
|
+
status = True
|
|
41
|
+
elif statusstr == "down":
|
|
42
|
+
status = False
|
|
43
|
+
# another observable state is "fail"
|
|
44
|
+
# report as ``None`` for now
|
|
45
|
+
|
|
46
|
+
services[service] = status
|
|
47
|
+
|
|
48
|
+
return services
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class RunitManaged(FactBase):
|
|
52
|
+
"""
|
|
53
|
+
Returns a set of all services managed by runit
|
|
54
|
+
|
|
55
|
+
+ service: optionally check only for a single service
|
|
56
|
+
+ svdir: alternative ``SVDIR``
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
default = set
|
|
60
|
+
|
|
61
|
+
def command(self, service=None, svdir="/var/service"):
|
|
62
|
+
if service is None:
|
|
63
|
+
return 'cd "{0}" && find -mindepth 1 -maxdepth 1 -type l -printf "%f\n"'.format(svdir)
|
|
64
|
+
else:
|
|
65
|
+
return 'cd "{0}" && test -h "{1}" && echo "{1}" || true'.format(svdir, service)
|
|
66
|
+
|
|
67
|
+
def process(self, output):
|
|
68
|
+
return set(output)
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manager Docker Containers, Volumes and Networks
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pyinfra import host
|
|
6
|
+
from pyinfra.api import operation
|
|
7
|
+
from pyinfra.facts.docker import DockerContainers, DockerNetworks, DockerVolumes
|
|
8
|
+
|
|
9
|
+
from .util.docker import handle_docker
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@operation()
|
|
13
|
+
def container(
|
|
14
|
+
container,
|
|
15
|
+
image="",
|
|
16
|
+
ports=None,
|
|
17
|
+
networks=None,
|
|
18
|
+
volumes=None,
|
|
19
|
+
env_vars=None,
|
|
20
|
+
pull_always=False,
|
|
21
|
+
present=True,
|
|
22
|
+
force=False,
|
|
23
|
+
start=True,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Manage Docker containers
|
|
27
|
+
|
|
28
|
+
+ container: name to identify the container
|
|
29
|
+
+ image: container image and tag ex: nginx:alpine
|
|
30
|
+
+ networks: network list to attach on container
|
|
31
|
+
+ ports: port list to expose
|
|
32
|
+
+ volumes: volume list to map on container
|
|
33
|
+
+ env_vars: environment varible list to inject on container
|
|
34
|
+
+ pull_always: force image pull
|
|
35
|
+
+ force: remove a contaner with same name and create a new one
|
|
36
|
+
+ present: whether the container should be up and running
|
|
37
|
+
+ start: start or stop the container
|
|
38
|
+
|
|
39
|
+
**Examples:**
|
|
40
|
+
|
|
41
|
+
.. code:: python
|
|
42
|
+
|
|
43
|
+
# Run a container
|
|
44
|
+
docker.container(
|
|
45
|
+
name="Deploy Nginx container",
|
|
46
|
+
container="nginx",
|
|
47
|
+
image="nginx:alpine",
|
|
48
|
+
ports=["80:80"],
|
|
49
|
+
present=True,
|
|
50
|
+
force=True,
|
|
51
|
+
networks=["proxy", "services"],
|
|
52
|
+
volumes=["nginx_data:/usr/share/nginx/html"],
|
|
53
|
+
pull_always=True,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Stop a container
|
|
57
|
+
docker.container(
|
|
58
|
+
name="Stop Nginx container",
|
|
59
|
+
container="nginx",
|
|
60
|
+
start=False,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Start a container
|
|
64
|
+
docker.container(
|
|
65
|
+
name="Start Nginx container",
|
|
66
|
+
container="nginx",
|
|
67
|
+
start=True,
|
|
68
|
+
)
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
existent_container = [c for c in host.get_fact(DockerContainers) if container in c["Name"]]
|
|
72
|
+
|
|
73
|
+
if force:
|
|
74
|
+
if existent_container:
|
|
75
|
+
yield handle_docker(
|
|
76
|
+
resource="container",
|
|
77
|
+
command="remove",
|
|
78
|
+
container=container,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if present:
|
|
82
|
+
if not existent_container or force:
|
|
83
|
+
yield handle_docker(
|
|
84
|
+
resource="container",
|
|
85
|
+
command="create",
|
|
86
|
+
container=container,
|
|
87
|
+
image=image,
|
|
88
|
+
ports=ports,
|
|
89
|
+
networks=networks,
|
|
90
|
+
volumes=volumes,
|
|
91
|
+
env_vars=env_vars,
|
|
92
|
+
pull_always=pull_always,
|
|
93
|
+
present=present,
|
|
94
|
+
force=force,
|
|
95
|
+
start=start,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if existent_container and start:
|
|
99
|
+
if existent_container[0]["State"]["Status"] != "running":
|
|
100
|
+
yield handle_docker(
|
|
101
|
+
resource="container",
|
|
102
|
+
command="start",
|
|
103
|
+
container=container,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
if existent_container and not start:
|
|
107
|
+
if existent_container[0]["State"]["Status"] == "running":
|
|
108
|
+
yield handle_docker(
|
|
109
|
+
resource="container",
|
|
110
|
+
command="stop",
|
|
111
|
+
container=container,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if existent_container and not present:
|
|
115
|
+
yield handle_docker(
|
|
116
|
+
resource="container",
|
|
117
|
+
command="remove",
|
|
118
|
+
container=container,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@operation(is_idempotent=False)
|
|
123
|
+
def image(image, present=True):
|
|
124
|
+
"""
|
|
125
|
+
Manage Docker images
|
|
126
|
+
|
|
127
|
+
+ image: Image and tag ex: nginx:alpine
|
|
128
|
+
+ present: whether the Docker image should be exist
|
|
129
|
+
|
|
130
|
+
**Examples:**
|
|
131
|
+
|
|
132
|
+
.. code:: python
|
|
133
|
+
|
|
134
|
+
# Pull a Docker image
|
|
135
|
+
docker.image(
|
|
136
|
+
name="Pull nginx image",
|
|
137
|
+
image="nginx:alpine",
|
|
138
|
+
present=True,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Remove a Docker image
|
|
142
|
+
docker.image(
|
|
143
|
+
name="Remove nginx image",
|
|
144
|
+
image:"nginx:image",
|
|
145
|
+
present=False,
|
|
146
|
+
)
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
if present:
|
|
150
|
+
yield handle_docker(
|
|
151
|
+
resource="image",
|
|
152
|
+
command="pull",
|
|
153
|
+
image=image,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
else:
|
|
157
|
+
yield handle_docker(
|
|
158
|
+
resource="image",
|
|
159
|
+
command="remove",
|
|
160
|
+
image=image,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@operation()
|
|
165
|
+
def volume(volume, driver="", labels=None, present=True):
|
|
166
|
+
"""
|
|
167
|
+
Manage Docker volumes
|
|
168
|
+
|
|
169
|
+
+ volume: Volume name
|
|
170
|
+
+ driver: Docker volume storage driver
|
|
171
|
+
+ labels: Label list to attach in the volume
|
|
172
|
+
+ present: whether the Docker volume should exist
|
|
173
|
+
|
|
174
|
+
**Examples:**
|
|
175
|
+
|
|
176
|
+
.. code:: python
|
|
177
|
+
|
|
178
|
+
# Create a Docker volume
|
|
179
|
+
docker.volume(
|
|
180
|
+
name="Create nginx volume",
|
|
181
|
+
volume="nginx_data",
|
|
182
|
+
present=True
|
|
183
|
+
)
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
existent_volume = [v for v in host.get_fact(DockerVolumes) if v["Name"] == volume]
|
|
187
|
+
|
|
188
|
+
if present:
|
|
189
|
+
|
|
190
|
+
if existent_volume:
|
|
191
|
+
host.noop("Volume alredy exist!")
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
yield handle_docker(
|
|
195
|
+
resource="volume",
|
|
196
|
+
command="create",
|
|
197
|
+
volume=volume,
|
|
198
|
+
driver=driver,
|
|
199
|
+
labels=labels,
|
|
200
|
+
present=present,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
else:
|
|
204
|
+
if existent_volume is None:
|
|
205
|
+
host.noop("There is no {0} volume!".format(volume))
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
yield handle_docker(
|
|
209
|
+
resource="volume",
|
|
210
|
+
command="remove",
|
|
211
|
+
volume=volume,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@operation()
|
|
216
|
+
def network(
|
|
217
|
+
network,
|
|
218
|
+
driver="",
|
|
219
|
+
gateway="",
|
|
220
|
+
ip_range="",
|
|
221
|
+
ipam_driver="",
|
|
222
|
+
subnet="",
|
|
223
|
+
scope="",
|
|
224
|
+
opts=None,
|
|
225
|
+
ipam_opts=None,
|
|
226
|
+
labels=None,
|
|
227
|
+
ingress=False,
|
|
228
|
+
attachable=False,
|
|
229
|
+
present=True,
|
|
230
|
+
):
|
|
231
|
+
"""
|
|
232
|
+
Manage docker networks
|
|
233
|
+
|
|
234
|
+
+ network_name: Image name
|
|
235
|
+
+ driver: Container image and tag ex: nginx:alpine
|
|
236
|
+
+ gateway: IPv4 or IPv6 Gateway for the master subnet
|
|
237
|
+
+ ip_range: Allocate container ip from a sub-range
|
|
238
|
+
+ ipam_driver: IP Address Management Driver
|
|
239
|
+
+ subnet: Subnet in CIDR format that represents a network segment
|
|
240
|
+
+ scope: Control the network's scope
|
|
241
|
+
+ opts: Set driver specific options
|
|
242
|
+
+ ipam_opts: Set IPAM driver specific options
|
|
243
|
+
+ labels: Label list to attach in the network
|
|
244
|
+
+ ingress: Create swarm routing-mesh network
|
|
245
|
+
+ attachable: Enable manual container attachment
|
|
246
|
+
+ present: whether the Docker network should exist
|
|
247
|
+
|
|
248
|
+
**Examples:**
|
|
249
|
+
|
|
250
|
+
.. code:: python
|
|
251
|
+
|
|
252
|
+
# Create Docker network
|
|
253
|
+
docker.network(
|
|
254
|
+
name="Create nginx network",
|
|
255
|
+
network_name="nginx",
|
|
256
|
+
attachable=True,
|
|
257
|
+
present=True,
|
|
258
|
+
)
|
|
259
|
+
"""
|
|
260
|
+
existent_network = [n for n in host.get_fact(DockerNetworks) if n["Name"] == network]
|
|
261
|
+
|
|
262
|
+
if present:
|
|
263
|
+
if existent_network:
|
|
264
|
+
host.noop("Alredy exist a network with {0} name!".format(network))
|
|
265
|
+
return
|
|
266
|
+
|
|
267
|
+
yield handle_docker(
|
|
268
|
+
resource="network",
|
|
269
|
+
command="create",
|
|
270
|
+
network=network,
|
|
271
|
+
driver=driver,
|
|
272
|
+
gateway=gateway,
|
|
273
|
+
ip_range=ip_range,
|
|
274
|
+
ipam_driver=ipam_driver,
|
|
275
|
+
subnet=subnet,
|
|
276
|
+
scope=scope,
|
|
277
|
+
opts=opts,
|
|
278
|
+
ipam_opts=ipam_opts,
|
|
279
|
+
labels=labels,
|
|
280
|
+
ingress=ingress,
|
|
281
|
+
attachable=attachable,
|
|
282
|
+
present=present,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
else:
|
|
286
|
+
if existent_network is None:
|
|
287
|
+
host.noop("Ther is not network with {0} name!".format(network))
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
yield handle_docker(
|
|
291
|
+
resource="network",
|
|
292
|
+
command="create",
|
|
293
|
+
network=network,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@operation(is_idempotent=False)
|
|
298
|
+
def prune(
|
|
299
|
+
all=False,
|
|
300
|
+
volume=False,
|
|
301
|
+
filter="",
|
|
302
|
+
):
|
|
303
|
+
"""
|
|
304
|
+
Execute a docker system prune.
|
|
305
|
+
|
|
306
|
+
+ all: Remove all unused images not just dangling ones
|
|
307
|
+
+ volumes: Prune anonymous volumes
|
|
308
|
+
+ filter: Provide filter values (e.g. "label=<key>=<value>" or "until=24h")
|
|
309
|
+
|
|
310
|
+
**Examples:**
|
|
311
|
+
|
|
312
|
+
.. code:: python
|
|
313
|
+
|
|
314
|
+
# Remove dangling images
|
|
315
|
+
docker.prune(
|
|
316
|
+
name="remove dangling images",
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Remove all images and volumes
|
|
320
|
+
docker.prune(
|
|
321
|
+
name="Remove all images and volumes",
|
|
322
|
+
all=True,
|
|
323
|
+
volumes=True,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
# Remove images older than 90 days
|
|
327
|
+
docker.prune(
|
|
328
|
+
name="Remove unused older than 90 days",
|
|
329
|
+
filter="until=2160h"
|
|
330
|
+
)
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
yield handle_docker(
|
|
334
|
+
resource="system",
|
|
335
|
+
command="prune",
|
|
336
|
+
all=all,
|
|
337
|
+
volume=volume,
|
|
338
|
+
filter=filter,
|
|
339
|
+
)
|
|
@@ -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
|
+
)
|
|
@@ -41,6 +41,7 @@ from . import (
|
|
|
41
41
|
openrc,
|
|
42
42
|
pacman,
|
|
43
43
|
pkg,
|
|
44
|
+
runit,
|
|
44
45
|
systemd,
|
|
45
46
|
sysvinit,
|
|
46
47
|
upstart,
|
|
@@ -492,6 +493,9 @@ def service(
|
|
|
492
493
|
elif host.get_fact(Which, command="initctl"):
|
|
493
494
|
service_operation = upstart.service
|
|
494
495
|
|
|
496
|
+
elif host.get_fact(Which, command="sv"):
|
|
497
|
+
service_operation = runit.service
|
|
498
|
+
|
|
495
499
|
elif (
|
|
496
500
|
host.get_fact(Which, command="service")
|
|
497
501
|
or host.get_fact(Link, path="/etc/init.d")
|
|
@@ -4,6 +4,8 @@ Manage systemd services.
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
import shlex
|
|
8
|
+
|
|
7
9
|
from pyinfra import host
|
|
8
10
|
from pyinfra.api import StringCommand, operation
|
|
9
11
|
from pyinfra.facts.systemd import SystemdEnabled, SystemdStatus, _make_systemctl_cmd
|
|
@@ -140,8 +142,8 @@ def service(
|
|
|
140
142
|
|
|
141
143
|
# Isn't enabled and want enabled?
|
|
142
144
|
if not is_enabled and enabled is True:
|
|
143
|
-
yield "{0} enable {1}".format(systemctl_cmd, service)
|
|
145
|
+
yield "{0} enable {1}".format(systemctl_cmd, shlex.quote(service))
|
|
144
146
|
|
|
145
147
|
# Is enabled and want disabled?
|
|
146
148
|
elif is_enabled and enabled is False:
|
|
147
|
-
yield "{0} disable {1}".format(systemctl_cmd, service)
|
|
149
|
+
yield "{0} disable {1}".format(systemctl_cmd, shlex.quote(service))
|