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.
Files changed (179) hide show
  1. {pyinfra-3.0b2 → pyinfra-3.0b3}/CHANGELOG.md +1 -1
  2. {pyinfra-3.0b2/pyinfra.egg-info → pyinfra-3.0b3}/PKG-INFO +3 -3
  3. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/docker.py +16 -0
  4. pyinfra-3.0b3/pyinfra/facts/runit.py +68 -0
  5. pyinfra-3.0b3/pyinfra/operations/docker.py +339 -0
  6. pyinfra-3.0b3/pyinfra/operations/runit.py +182 -0
  7. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/server.py +4 -0
  8. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/systemd.py +4 -2
  9. pyinfra-3.0b3/pyinfra/operations/util/docker.py +177 -0
  10. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/util/service.py +7 -5
  11. {pyinfra-3.0b2 → pyinfra-3.0b3/pyinfra.egg-info}/PKG-INFO +3 -3
  12. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra.egg-info/SOURCES.txt +4 -0
  13. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra.egg-info/requires.txt +2 -2
  14. {pyinfra-3.0b2 → pyinfra-3.0b3}/setup.py +1 -1
  15. {pyinfra-3.0b2 → pyinfra-3.0b3}/LICENSE.md +0 -0
  16. {pyinfra-3.0b2 → pyinfra-3.0b3}/MANIFEST.in +0 -0
  17. {pyinfra-3.0b2 → pyinfra-3.0b3}/README.md +0 -0
  18. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/__init__.py +0 -0
  19. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/__main__.py +0 -0
  20. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/__init__.py +0 -0
  21. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/arguments.py +0 -0
  22. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/arguments_typed.py +0 -0
  23. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/command.py +0 -0
  24. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/config.py +0 -0
  25. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/connect.py +0 -0
  26. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/connectors.py +0 -0
  27. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/deploy.py +0 -0
  28. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/exceptions.py +0 -0
  29. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/facts.py +0 -0
  30. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/host.py +0 -0
  31. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/inventory.py +0 -0
  32. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/operation.py +0 -0
  33. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/operations.py +0 -0
  34. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/state.py +0 -0
  35. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/api/util.py +0 -0
  36. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/__init__.py +0 -0
  37. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/base.py +0 -0
  38. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/chroot.py +0 -0
  39. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/docker.py +0 -0
  40. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/dockerssh.py +0 -0
  41. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/local.py +0 -0
  42. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/ssh.py +0 -0
  43. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/ssh_util.py +0 -0
  44. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/sshuserclient/__init__.py +0 -0
  45. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/sshuserclient/client.py +0 -0
  46. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/sshuserclient/config.py +0 -0
  47. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/terraform.py +0 -0
  48. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/util.py +0 -0
  49. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/connectors/vagrant.py +0 -0
  50. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/context.py +0 -0
  51. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/__init__.py +0 -0
  52. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/apk.py +0 -0
  53. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/apt.py +0 -0
  54. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/brew.py +0 -0
  55. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/bsdinit.py +0 -0
  56. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/cargo.py +0 -0
  57. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/choco.py +0 -0
  58. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/deb.py +0 -0
  59. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/dnf.py +0 -0
  60. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/files.py +0 -0
  61. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/gem.py +0 -0
  62. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/git.py +0 -0
  63. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/gpg.py +0 -0
  64. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/hardware.py +0 -0
  65. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/iptables.py +0 -0
  66. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/launchd.py +0 -0
  67. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/lxd.py +0 -0
  68. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/mysql.py +0 -0
  69. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/npm.py +0 -0
  70. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/openrc.py +0 -0
  71. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/pacman.py +0 -0
  72. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/pip.py +0 -0
  73. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/pkg.py +0 -0
  74. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/pkgin.py +0 -0
  75. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/postgres.py +0 -0
  76. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/postgresql.py +0 -0
  77. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/rpm.py +0 -0
  78. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/selinux.py +0 -0
  79. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/server.py +0 -0
  80. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/snap.py +0 -0
  81. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/systemd.py +0 -0
  82. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/sysvinit.py +0 -0
  83. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/upstart.py +0 -0
  84. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/util/__init__.py +0 -0
  85. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/util/databases.py +0 -0
  86. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/util/packaging.py +0 -0
  87. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/util/win_files.py +0 -0
  88. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/vzctl.py +0 -0
  89. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/xbps.py +0 -0
  90. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/yum.py +0 -0
  91. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/facts/zypper.py +0 -0
  92. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/local.py +0 -0
  93. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/__init__.py +0 -0
  94. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/apk.py +0 -0
  95. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/apt.py +0 -0
  96. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/brew.py +0 -0
  97. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/bsdinit.py +0 -0
  98. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/cargo.py +0 -0
  99. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/choco.py +0 -0
  100. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/dnf.py +0 -0
  101. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/files.py +0 -0
  102. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/gem.py +0 -0
  103. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/git.py +0 -0
  104. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/iptables.py +0 -0
  105. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/launchd.py +0 -0
  106. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/lxd.py +0 -0
  107. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/mysql.py +0 -0
  108. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/npm.py +0 -0
  109. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/openrc.py +0 -0
  110. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/pacman.py +0 -0
  111. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/pip.py +0 -0
  112. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/pkg.py +0 -0
  113. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/pkgin.py +0 -0
  114. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/postgres.py +0 -0
  115. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/postgresql.py +0 -0
  116. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/puppet.py +0 -0
  117. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/python.py +0 -0
  118. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/selinux.py +0 -0
  119. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/snap.py +0 -0
  120. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/ssh.py +0 -0
  121. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/sysvinit.py +0 -0
  122. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/upstart.py +0 -0
  123. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/util/__init__.py +0 -0
  124. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/util/files.py +0 -0
  125. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/util/packaging.py +0 -0
  126. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/vzctl.py +0 -0
  127. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/xbps.py +0 -0
  128. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/yum.py +0 -0
  129. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/operations/zypper.py +0 -0
  130. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/progress.py +0 -0
  131. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/py.typed +0 -0
  132. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra/version.py +0 -0
  133. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra.egg-info/dependency_links.txt +0 -0
  134. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra.egg-info/entry_points.txt +0 -0
  135. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra.egg-info/top_level.txt +0 -0
  136. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/__init__.py +0 -0
  137. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/__main__.py +0 -0
  138. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/commands.py +0 -0
  139. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/exceptions.py +0 -0
  140. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/inventory.py +0 -0
  141. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/log.py +0 -0
  142. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/main.py +0 -0
  143. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/prints.py +0 -0
  144. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/util.py +0 -0
  145. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyinfra_cli/virtualenv.py +0 -0
  146. {pyinfra-3.0b2 → pyinfra-3.0b3}/pyproject.toml +0 -0
  147. {pyinfra-3.0b2 → pyinfra-3.0b3}/setup.cfg +0 -0
  148. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/__init__.py +0 -0
  149. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api.py +0 -0
  150. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_arguments.py +0 -0
  151. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_command.py +0 -0
  152. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_config.py +0 -0
  153. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_deploys.py +0 -0
  154. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_facts.py +0 -0
  155. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_host.py +0 -0
  156. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_inventory.py +0 -0
  157. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_operations.py +0 -0
  158. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_api/test_api_util.py +0 -0
  159. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/__init__.py +0 -0
  160. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/test_cli.py +0 -0
  161. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/test_cli_deploy.py +0 -0
  162. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/test_cli_exceptions.py +0 -0
  163. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/test_cli_util.py +0 -0
  164. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/test_context_objects.py +0 -0
  165. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_cli/util.py +0 -0
  166. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/__init__.py +0 -0
  167. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_chroot.py +0 -0
  168. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_docker.py +0 -0
  169. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_dockerssh.py +0 -0
  170. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_local.py +0 -0
  171. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_ssh.py +0 -0
  172. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_sshuserclient.py +0 -0
  173. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_terraform.py +0 -0
  174. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_util.py +0 -0
  175. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_connectors/test_vagrant.py +0 -0
  176. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_facts.py +0 -0
  177. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_global_arguments.py +0 -0
  178. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_operations.py +0 -0
  179. {pyinfra-3.0b2 → pyinfra-3.0b3}/tests/test_operations_utils.py +0 -0
@@ -1,4 +1,4 @@
1
- # v3.0.beta2
1
+ # v3.0.beta3
2
2
 
3
3
  Welcome to pyinfra v3! This version is the biggest overhaul of pyinfra since it was created back in 2015. Most v2 deployment code should be automatically compatible, but as always be aware. Major changes:
4
4
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinfra
3
- Version: 3.0b2
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.15; extra == "docs"
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.15; extra == "dev"
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))