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.
Files changed (156) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +265 -253
  3. pyinfra/api/arguments_typed.py +80 -0
  4. pyinfra/api/command.py +68 -53
  5. pyinfra/api/config.py +139 -32
  6. pyinfra/api/connect.py +1 -1
  7. pyinfra/api/connectors.py +7 -26
  8. pyinfra/api/deploy.py +21 -52
  9. pyinfra/api/exceptions.py +33 -8
  10. pyinfra/api/facts.py +102 -137
  11. pyinfra/api/host.py +150 -82
  12. pyinfra/api/inventory.py +21 -25
  13. pyinfra/api/operation.py +240 -198
  14. pyinfra/api/operations.py +102 -148
  15. pyinfra/api/state.py +137 -79
  16. pyinfra/api/util.py +79 -86
  17. pyinfra/connectors/base.py +147 -0
  18. pyinfra/connectors/chroot.py +160 -169
  19. pyinfra/connectors/docker.py +220 -237
  20. pyinfra/connectors/dockerssh.py +231 -253
  21. pyinfra/connectors/local.py +196 -208
  22. pyinfra/connectors/ssh.py +530 -613
  23. pyinfra/connectors/ssh_util.py +114 -0
  24. pyinfra/connectors/sshuserclient/client.py +5 -3
  25. pyinfra/connectors/terraform.py +86 -65
  26. pyinfra/connectors/util.py +211 -137
  27. pyinfra/connectors/vagrant.py +60 -53
  28. pyinfra/context.py +4 -2
  29. pyinfra/facts/apk.py +2 -0
  30. pyinfra/facts/apt.py +2 -0
  31. pyinfra/facts/brew.py +2 -0
  32. pyinfra/facts/bsdinit.py +2 -0
  33. pyinfra/facts/cargo.py +2 -0
  34. pyinfra/facts/choco.py +2 -0
  35. pyinfra/facts/deb.py +7 -2
  36. pyinfra/facts/dnf.py +2 -0
  37. pyinfra/facts/docker.py +19 -0
  38. pyinfra/facts/files.py +47 -32
  39. pyinfra/facts/gem.py +2 -0
  40. pyinfra/facts/git.py +3 -1
  41. pyinfra/facts/gpg.py +3 -1
  42. pyinfra/facts/hardware.py +34 -24
  43. pyinfra/facts/iptables.py +5 -3
  44. pyinfra/facts/launchd.py +2 -0
  45. pyinfra/facts/lxd.py +2 -0
  46. pyinfra/facts/mysql.py +13 -6
  47. pyinfra/facts/npm.py +1 -0
  48. pyinfra/facts/openrc.py +2 -0
  49. pyinfra/facts/pacman.py +6 -2
  50. pyinfra/facts/pip.py +2 -0
  51. pyinfra/facts/pkg.py +2 -0
  52. pyinfra/facts/pkgin.py +2 -0
  53. pyinfra/facts/postgres.py +168 -0
  54. pyinfra/facts/postgresql.py +6 -160
  55. pyinfra/facts/rpm.py +12 -9
  56. pyinfra/facts/runit.py +68 -0
  57. pyinfra/facts/selinux.py +3 -1
  58. pyinfra/facts/server.py +80 -36
  59. pyinfra/facts/snap.py +2 -0
  60. pyinfra/facts/systemd.py +31 -12
  61. pyinfra/facts/sysvinit.py +10 -10
  62. pyinfra/facts/upstart.py +2 -0
  63. pyinfra/facts/util/packaging.py +7 -4
  64. pyinfra/facts/vzctl.py +2 -0
  65. pyinfra/facts/xbps.py +2 -0
  66. pyinfra/facts/yum.py +2 -0
  67. pyinfra/facts/zypper.py +2 -0
  68. pyinfra/local.py +4 -5
  69. pyinfra/operations/apk.py +6 -4
  70. pyinfra/operations/apt.py +46 -65
  71. pyinfra/operations/brew.py +17 -22
  72. pyinfra/operations/bsdinit.py +9 -7
  73. pyinfra/operations/cargo.py +4 -2
  74. pyinfra/operations/choco.py +4 -2
  75. pyinfra/operations/dnf.py +19 -23
  76. pyinfra/operations/docker.py +339 -0
  77. pyinfra/operations/files.py +188 -386
  78. pyinfra/operations/gem.py +4 -2
  79. pyinfra/operations/git.py +24 -53
  80. pyinfra/operations/iptables.py +29 -35
  81. pyinfra/operations/launchd.py +6 -7
  82. pyinfra/operations/lxd.py +8 -13
  83. pyinfra/operations/mysql.py +62 -81
  84. pyinfra/operations/npm.py +9 -2
  85. pyinfra/operations/openrc.py +6 -4
  86. pyinfra/operations/pacman.py +7 -8
  87. pyinfra/operations/pip.py +25 -24
  88. pyinfra/operations/pkg.py +4 -2
  89. pyinfra/operations/pkgin.py +6 -4
  90. pyinfra/operations/postgres.py +349 -0
  91. pyinfra/operations/postgresql.py +18 -379
  92. pyinfra/operations/puppet.py +3 -1
  93. pyinfra/operations/python.py +8 -19
  94. pyinfra/operations/runit.py +182 -0
  95. pyinfra/operations/selinux.py +47 -44
  96. pyinfra/operations/server.py +111 -127
  97. pyinfra/operations/snap.py +4 -4
  98. pyinfra/operations/ssh.py +20 -33
  99. pyinfra/operations/systemd.py +19 -15
  100. pyinfra/operations/sysvinit.py +9 -16
  101. pyinfra/operations/upstart.py +9 -7
  102. pyinfra/operations/util/__init__.py +12 -0
  103. pyinfra/operations/util/docker.py +177 -0
  104. pyinfra/operations/util/files.py +24 -16
  105. pyinfra/operations/util/packaging.py +55 -57
  106. pyinfra/operations/util/service.py +39 -51
  107. pyinfra/operations/vzctl.py +12 -10
  108. pyinfra/operations/xbps.py +6 -4
  109. pyinfra/operations/yum.py +18 -22
  110. pyinfra/operations/zypper.py +12 -13
  111. pyinfra/version.py +5 -2
  112. {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/METADATA +40 -41
  113. pyinfra-3.0.dist-info/RECORD +167 -0
  114. {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/WHEEL +1 -1
  115. pyinfra-3.0.dist-info/entry_points.txt +11 -0
  116. pyinfra_cli/__main__.py +4 -3
  117. pyinfra_cli/commands.py +7 -2
  118. pyinfra_cli/exceptions.py +78 -42
  119. pyinfra_cli/inventory.py +40 -6
  120. pyinfra_cli/log.py +17 -3
  121. pyinfra_cli/main.py +133 -90
  122. pyinfra_cli/prints.py +95 -127
  123. pyinfra_cli/util.py +62 -29
  124. tests/test_api/test_api.py +2 -0
  125. tests/test_api/test_api_arguments.py +13 -13
  126. tests/test_api/test_api_deploys.py +28 -29
  127. tests/test_api/test_api_facts.py +60 -98
  128. tests/test_api/test_api_operations.py +101 -201
  129. tests/test_cli/test_cli.py +18 -49
  130. tests/test_cli/test_cli_deploy.py +11 -37
  131. tests/test_cli/test_cli_exceptions.py +50 -19
  132. tests/test_cli/util.py +1 -1
  133. tests/test_connectors/test_chroot.py +6 -6
  134. tests/test_connectors/test_docker.py +4 -4
  135. tests/test_connectors/test_dockerssh.py +38 -50
  136. tests/test_connectors/test_local.py +11 -12
  137. tests/test_connectors/test_ssh.py +105 -93
  138. tests/test_connectors/test_terraform.py +9 -15
  139. tests/test_connectors/test_util.py +24 -46
  140. tests/test_connectors/test_vagrant.py +7 -7
  141. pyinfra/api/operation.pyi +0 -117
  142. pyinfra/connectors/ansible.py +0 -171
  143. pyinfra/connectors/mech.py +0 -186
  144. pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
  145. pyinfra/connectors/winrm.py +0 -320
  146. pyinfra/facts/windows.py +0 -366
  147. pyinfra/facts/windows_files.py +0 -90
  148. pyinfra/operations/windows.py +0 -59
  149. pyinfra/operations/windows_files.py +0 -551
  150. pyinfra-2.9.1.dist-info/RECORD +0 -170
  151. pyinfra-2.9.1.dist-info/entry_points.txt +0 -14
  152. tests/test_connectors/test_ansible.py +0 -64
  153. tests/test_connectors/test_mech.py +0 -126
  154. tests/test_connectors/test_winrm.py +0 -76
  155. {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/LICENSE.md +0 -0
  156. {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, 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
@@ -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 = state.get_temp_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
- )
@@ -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
- @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))
@@ -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
  )
@@ -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)
@@ -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
- parts = list(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
  """