pyinfra 3.0.dev0__py2.py3-none-any.whl → 3.0.2__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.
Files changed (148) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +115 -97
  3. pyinfra/api/arguments_typed.py +80 -0
  4. pyinfra/api/command.py +5 -3
  5. pyinfra/api/config.py +139 -39
  6. pyinfra/api/connectors.py +5 -2
  7. pyinfra/api/deploy.py +19 -19
  8. pyinfra/api/exceptions.py +35 -4
  9. pyinfra/api/facts.py +62 -86
  10. pyinfra/api/host.py +102 -15
  11. pyinfra/api/inventory.py +4 -0
  12. pyinfra/api/operation.py +188 -120
  13. pyinfra/api/operations.py +66 -113
  14. pyinfra/api/state.py +53 -34
  15. pyinfra/api/util.py +64 -33
  16. pyinfra/connectors/base.py +65 -20
  17. pyinfra/connectors/chroot.py +15 -13
  18. pyinfra/connectors/docker.py +62 -72
  19. pyinfra/connectors/dockerssh.py +20 -19
  20. pyinfra/connectors/local.py +32 -22
  21. pyinfra/connectors/ssh.py +162 -86
  22. pyinfra/connectors/sshuserclient/client.py +1 -1
  23. pyinfra/connectors/terraform.py +57 -39
  24. pyinfra/connectors/util.py +26 -27
  25. pyinfra/connectors/vagrant.py +27 -26
  26. pyinfra/context.py +1 -0
  27. pyinfra/facts/apk.py +7 -2
  28. pyinfra/facts/apt.py +15 -7
  29. pyinfra/facts/brew.py +28 -13
  30. pyinfra/facts/bsdinit.py +9 -6
  31. pyinfra/facts/cargo.py +6 -3
  32. pyinfra/facts/choco.py +8 -4
  33. pyinfra/facts/deb.py +21 -9
  34. pyinfra/facts/dnf.py +11 -6
  35. pyinfra/facts/docker.py +30 -5
  36. pyinfra/facts/files.py +49 -33
  37. pyinfra/facts/gem.py +7 -2
  38. pyinfra/facts/git.py +14 -21
  39. pyinfra/facts/gpg.py +4 -1
  40. pyinfra/facts/hardware.py +186 -138
  41. pyinfra/facts/launchd.py +7 -2
  42. pyinfra/facts/lxd.py +8 -2
  43. pyinfra/facts/mysql.py +19 -12
  44. pyinfra/facts/npm.py +3 -1
  45. pyinfra/facts/openrc.py +8 -2
  46. pyinfra/facts/pacman.py +13 -5
  47. pyinfra/facts/pip.py +2 -0
  48. pyinfra/facts/pkg.py +5 -1
  49. pyinfra/facts/pkgin.py +7 -2
  50. pyinfra/facts/postgres.py +170 -0
  51. pyinfra/facts/postgresql.py +5 -162
  52. pyinfra/facts/rpm.py +21 -15
  53. pyinfra/facts/runit.py +70 -0
  54. pyinfra/facts/selinux.py +12 -4
  55. pyinfra/facts/server.py +240 -82
  56. pyinfra/facts/snap.py +8 -2
  57. pyinfra/facts/systemd.py +37 -13
  58. pyinfra/facts/sysvinit.py +7 -4
  59. pyinfra/facts/upstart.py +7 -2
  60. pyinfra/facts/util/packaging.py +3 -2
  61. pyinfra/facts/vzctl.py +8 -4
  62. pyinfra/facts/xbps.py +7 -2
  63. pyinfra/facts/yum.py +10 -5
  64. pyinfra/facts/zypper.py +9 -4
  65. pyinfra/operations/apk.py +5 -3
  66. pyinfra/operations/apt.py +28 -25
  67. pyinfra/operations/brew.py +60 -29
  68. pyinfra/operations/bsdinit.py +6 -4
  69. pyinfra/operations/cargo.py +3 -1
  70. pyinfra/operations/choco.py +3 -1
  71. pyinfra/operations/dnf.py +16 -20
  72. pyinfra/operations/docker.py +339 -0
  73. pyinfra/operations/files.py +187 -168
  74. pyinfra/operations/gem.py +3 -1
  75. pyinfra/operations/git.py +23 -25
  76. pyinfra/operations/iptables.py +33 -25
  77. pyinfra/operations/launchd.py +5 -6
  78. pyinfra/operations/lxd.py +7 -4
  79. pyinfra/operations/mysql.py +59 -55
  80. pyinfra/operations/npm.py +8 -1
  81. pyinfra/operations/openrc.py +5 -3
  82. pyinfra/operations/pacman.py +6 -7
  83. pyinfra/operations/pip.py +19 -12
  84. pyinfra/operations/pkg.py +3 -1
  85. pyinfra/operations/pkgin.py +5 -3
  86. pyinfra/operations/postgres.py +349 -0
  87. pyinfra/operations/postgresql.py +18 -335
  88. pyinfra/operations/puppet.py +3 -1
  89. pyinfra/operations/python.py +8 -19
  90. pyinfra/operations/runit.py +182 -0
  91. pyinfra/operations/selinux.py +47 -29
  92. pyinfra/operations/server.py +138 -67
  93. pyinfra/operations/snap.py +3 -1
  94. pyinfra/operations/ssh.py +18 -16
  95. pyinfra/operations/systemd.py +18 -12
  96. pyinfra/operations/sysvinit.py +7 -5
  97. pyinfra/operations/upstart.py +7 -5
  98. pyinfra/operations/util/__init__.py +12 -0
  99. pyinfra/operations/util/docker.py +177 -0
  100. pyinfra/operations/util/files.py +24 -16
  101. pyinfra/operations/util/packaging.py +54 -38
  102. pyinfra/operations/util/service.py +39 -47
  103. pyinfra/operations/vzctl.py +12 -10
  104. pyinfra/operations/xbps.py +5 -3
  105. pyinfra/operations/yum.py +15 -19
  106. pyinfra/operations/zypper.py +9 -10
  107. pyinfra/version.py +5 -2
  108. {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.2.dist-info}/METADATA +51 -58
  109. pyinfra-3.0.2.dist-info/RECORD +168 -0
  110. {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.2.dist-info}/WHEEL +1 -1
  111. {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.2.dist-info}/entry_points.txt +0 -3
  112. pyinfra_cli/__main__.py +4 -3
  113. pyinfra_cli/commands.py +3 -2
  114. pyinfra_cli/exceptions.py +75 -43
  115. pyinfra_cli/inventory.py +52 -31
  116. pyinfra_cli/log.py +10 -2
  117. pyinfra_cli/main.py +88 -65
  118. pyinfra_cli/prints.py +37 -109
  119. pyinfra_cli/util.py +15 -10
  120. tests/test_api/test_api.py +2 -0
  121. tests/test_api/test_api_arguments.py +9 -9
  122. tests/test_api/test_api_deploys.py +15 -19
  123. tests/test_api/test_api_facts.py +4 -5
  124. tests/test_api/test_api_operations.py +18 -20
  125. tests/test_api/test_api_util.py +41 -2
  126. tests/test_cli/test_cli.py +14 -50
  127. tests/test_cli/test_cli_deploy.py +17 -14
  128. tests/test_cli/test_cli_exceptions.py +50 -19
  129. tests/test_cli/test_cli_inventory.py +66 -0
  130. tests/test_cli/util.py +1 -1
  131. tests/test_connectors/test_dockerssh.py +11 -8
  132. tests/test_connectors/test_ssh.py +88 -23
  133. tests/test_connectors/test_sshuserclient.py +1 -1
  134. tests/test_connectors/test_terraform.py +11 -8
  135. tests/test_connectors/test_vagrant.py +6 -6
  136. pyinfra/connectors/ansible.py +0 -175
  137. pyinfra/connectors/mech.py +0 -189
  138. pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
  139. pyinfra/connectors/winrm.py +0 -312
  140. pyinfra/facts/windows.py +0 -366
  141. pyinfra/facts/windows_files.py +0 -90
  142. pyinfra/operations/windows.py +0 -59
  143. pyinfra/operations/windows_files.py +0 -538
  144. pyinfra-3.0.dev0.dist-info/RECORD +0 -170
  145. tests/test_connectors/test_ansible.py +0 -64
  146. tests/test_connectors/test_mech.py +0 -126
  147. {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.2.dist-info}/LICENSE.md +0 -0
  148. {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.2.dist-info}/top_level.txt +0 -0
@@ -2,6 +2,8 @@
2
2
  Manage snap packages. See https://snapcraft.io/
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  from pyinfra import host
6
8
  from pyinfra.api import operation
7
9
  from pyinfra.facts.snap import SnapPackage, SnapPackages
@@ -9,7 +11,7 @@ from pyinfra.facts.snap import SnapPackage, SnapPackages
9
11
 
10
12
  @operation()
11
13
  def package(
12
- packages=None,
14
+ packages: str | list[str] | None = None,
13
15
  channel="latest/stable",
14
16
  classic=False,
15
17
  present=True,
pyinfra/operations/ssh.py CHANGED
@@ -4,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, state
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
@@ -15,7 +17,7 @@ from . import files
15
17
 
16
18
 
17
19
  @operation()
18
- def keyscan(hostname, force=False, port=22):
20
+ def keyscan(hostname: str, force=False, port=22):
19
21
  """
20
22
  Check/add hosts to the ``~/.ssh/known_hosts`` file.
21
23
 
@@ -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
  )
@@ -63,7 +65,7 @@ def keyscan(hostname, force=False, port=22):
63
65
 
64
66
 
65
67
  @operation(is_idempotent=False)
66
- def command(hostname, command, user=None, port=22):
68
+ def command(hostname: str, command: str, user: str | None = None, port=22):
67
69
  """
68
70
  Execute commands on other servers over SSH.
69
71
 
@@ -95,11 +97,11 @@ def command(hostname, command, user=None, port=22):
95
97
 
96
98
  @operation(is_idempotent=False)
97
99
  def upload(
98
- hostname,
99
- filename,
100
- remote_filename=None,
100
+ hostname: str,
101
+ filename: str,
102
+ remote_filename: str | None = None,
101
103
  port=22,
102
- user=None,
104
+ user: str | None = None,
103
105
  use_remote_sudo=False,
104
106
  ssh_keyscan=False,
105
107
  ):
@@ -123,7 +125,7 @@ def upload(
123
125
  connection_target = "@".join((user, hostname))
124
126
 
125
127
  if ssh_keyscan:
126
- yield from keyscan(hostname)
128
+ yield from keyscan._inner(hostname)
127
129
 
128
130
  # If we're not using sudo on the remote side, just scp the file over
129
131
  if not use_remote_sudo:
@@ -136,7 +138,7 @@ def upload(
136
138
 
137
139
  else:
138
140
  # Otherwise - we need a temporary location for the file
139
- temp_remote_filename = state.get_temp_filename()
141
+ temp_remote_filename = host.get_temp_filename()
140
142
 
141
143
  # scp it to the temporary location
142
144
  upload_cmd = "scp -P {0} {1} {2}:{3}".format(
@@ -149,7 +151,7 @@ def upload(
149
151
  yield upload_cmd
150
152
 
151
153
  # And sudo sudo to move it
152
- yield from command(
154
+ yield from command._inner(
153
155
  hostname=hostname,
154
156
  command="sudo mv {0} {1}".format(temp_remote_filename, remote_filename),
155
157
  port=port,
@@ -159,12 +161,12 @@ def upload(
159
161
 
160
162
  @operation()
161
163
  def download(
162
- hostname,
163
- filename,
164
- local_filename=None,
164
+ hostname: str,
165
+ filename: str,
166
+ local_filename: str | None = None,
165
167
  force=False,
166
168
  port=22,
167
- user=None,
169
+ user: str | None = None,
168
170
  ssh_keyscan=False,
169
171
  ):
170
172
  """
@@ -203,7 +205,7 @@ def download(
203
205
  connection_target = "@".join((user, hostname))
204
206
 
205
207
  if ssh_keyscan:
206
- yield from keyscan(hostname)
208
+ yield from keyscan._inner(hostname)
207
209
 
208
210
  # Download the file with scp
209
211
  yield "scp -P {0} {1}:{2} {3}".format(
@@ -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 "{0} daemon-reload".format(systemctl_cmd)
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
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,13 +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)
145
+ yield "{0} enable {1}".format(systemctl_cmd, shlex.quote(service))
140
146
 
141
147
  # Is enabled and want disabled?
142
148
  elif is_enabled and enabled is False:
143
- yield "{0} disable {1}".format(systemctl_cmd, service)
149
+ yield "{0} disable {1}".format(systemctl_cmd, shlex.quote(service))
@@ -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
@@ -14,12 +16,12 @@ from .util.service import handle_service_control
14
16
 
15
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.
@@ -95,7 +97,7 @@ def service(
95
97
 
96
98
  @operation()
97
99
  def enable(
98
- service,
100
+ service: str,
99
101
  start_priority=20,
100
102
  stop_priority=80,
101
103
  start_levels=(2, 3, 4, 5),
@@ -133,7 +135,7 @@ def enable(
133
135
 
134
136
  # Ensure all the new links exist
135
137
  for link in links:
136
- yield from files.link(
138
+ yield from files.link._inner(
137
139
  path=link,
138
140
  target="/etc/init.d/{0}".format(service),
139
141
  )
@@ -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
@@ -14,12 +16,12 @@ from .util.service import handle_service_control
14
16
 
15
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,7 +54,7 @@ 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(
57
+ yield from files.file._inner(
56
58
  path="/etc/init/{0}.override".format(service),
57
59
  present=False,
58
60
  )
@@ -60,7 +62,7 @@ def service(
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)
@@ -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(*path_parts):
8
- parts = list(path_parts)
9
- parts[0:-1] = [part.rstrip("/") for part in parts[0:-1]]
10
- return "/".join(parts)
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
- flags = "".join(flags) if flags else ""
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, flags)
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(target, user, group=None, recursive=False, dereference=True):
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
  """