pyinfra 3.1.1__py2.py3-none-any.whl → 3.3__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 (104) hide show
  1. pyinfra/api/arguments.py +9 -2
  2. pyinfra/api/arguments_typed.py +4 -5
  3. pyinfra/api/command.py +22 -3
  4. pyinfra/api/config.py +5 -2
  5. pyinfra/api/deploy.py +4 -2
  6. pyinfra/api/facts.py +3 -0
  7. pyinfra/api/host.py +15 -7
  8. pyinfra/api/operation.py +2 -1
  9. pyinfra/api/state.py +1 -1
  10. pyinfra/connectors/base.py +34 -8
  11. pyinfra/connectors/chroot.py +7 -2
  12. pyinfra/connectors/docker.py +24 -8
  13. pyinfra/connectors/dockerssh.py +7 -2
  14. pyinfra/connectors/local.py +7 -2
  15. pyinfra/connectors/ssh.py +9 -2
  16. pyinfra/connectors/sshuserclient/client.py +42 -14
  17. pyinfra/connectors/sshuserclient/config.py +2 -0
  18. pyinfra/connectors/terraform.py +1 -1
  19. pyinfra/connectors/util.py +13 -9
  20. pyinfra/context.py +9 -2
  21. pyinfra/facts/apk.py +8 -1
  22. pyinfra/facts/apt.py +68 -0
  23. pyinfra/facts/brew.py +13 -0
  24. pyinfra/facts/bsdinit.py +3 -0
  25. pyinfra/facts/cargo.py +5 -0
  26. pyinfra/facts/choco.py +6 -0
  27. pyinfra/facts/crontab.py +195 -0
  28. pyinfra/facts/deb.py +10 -0
  29. pyinfra/facts/dnf.py +5 -0
  30. pyinfra/facts/docker.py +16 -0
  31. pyinfra/facts/efibootmgr.py +113 -0
  32. pyinfra/facts/files.py +112 -7
  33. pyinfra/facts/flatpak.py +7 -0
  34. pyinfra/facts/freebsd.py +75 -0
  35. pyinfra/facts/gem.py +5 -0
  36. pyinfra/facts/git.py +12 -2
  37. pyinfra/facts/gpg.py +7 -0
  38. pyinfra/facts/hardware.py +13 -0
  39. pyinfra/facts/iptables.py +9 -1
  40. pyinfra/facts/launchd.py +5 -0
  41. pyinfra/facts/lxd.py +5 -0
  42. pyinfra/facts/mysql.py +9 -2
  43. pyinfra/facts/npm.py +5 -0
  44. pyinfra/facts/openrc.py +8 -0
  45. pyinfra/facts/opkg.py +245 -0
  46. pyinfra/facts/pacman.py +9 -1
  47. pyinfra/facts/pip.py +5 -0
  48. pyinfra/facts/pipx.py +82 -0
  49. pyinfra/facts/pkg.py +4 -0
  50. pyinfra/facts/pkgin.py +5 -0
  51. pyinfra/facts/podman.py +54 -0
  52. pyinfra/facts/postgres.py +10 -2
  53. pyinfra/facts/rpm.py +11 -0
  54. pyinfra/facts/runit.py +7 -0
  55. pyinfra/facts/selinux.py +16 -0
  56. pyinfra/facts/server.py +87 -79
  57. pyinfra/facts/snap.py +7 -0
  58. pyinfra/facts/systemd.py +5 -0
  59. pyinfra/facts/sysvinit.py +4 -0
  60. pyinfra/facts/upstart.py +5 -0
  61. pyinfra/facts/util/__init__.py +4 -1
  62. pyinfra/facts/util/units.py +30 -0
  63. pyinfra/facts/vzctl.py +5 -0
  64. pyinfra/facts/xbps.py +6 -1
  65. pyinfra/facts/yum.py +5 -0
  66. pyinfra/facts/zfs.py +41 -21
  67. pyinfra/facts/zypper.py +5 -0
  68. pyinfra/local.py +3 -2
  69. pyinfra/operations/apt.py +36 -22
  70. pyinfra/operations/crontab.py +189 -0
  71. pyinfra/operations/docker.py +61 -56
  72. pyinfra/operations/files.py +65 -1
  73. pyinfra/operations/freebsd/__init__.py +12 -0
  74. pyinfra/operations/freebsd/freebsd_update.py +70 -0
  75. pyinfra/operations/freebsd/pkg.py +219 -0
  76. pyinfra/operations/freebsd/service.py +116 -0
  77. pyinfra/operations/freebsd/sysrc.py +92 -0
  78. pyinfra/operations/git.py +23 -7
  79. pyinfra/operations/opkg.py +88 -0
  80. pyinfra/operations/pip.py +3 -2
  81. pyinfra/operations/pipx.py +90 -0
  82. pyinfra/operations/postgres.py +114 -27
  83. pyinfra/operations/runit.py +2 -0
  84. pyinfra/operations/server.py +9 -181
  85. pyinfra/operations/util/docker.py +44 -22
  86. pyinfra/operations/zfs.py +3 -3
  87. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/LICENSE.md +1 -1
  88. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/METADATA +25 -25
  89. pyinfra-3.3.dist-info/RECORD +187 -0
  90. pyinfra_cli/exceptions.py +5 -0
  91. pyinfra_cli/inventory.py +26 -9
  92. pyinfra_cli/log.py +3 -0
  93. pyinfra_cli/main.py +9 -8
  94. pyinfra_cli/prints.py +19 -4
  95. pyinfra_cli/util.py +3 -0
  96. pyinfra_cli/virtualenv.py +1 -1
  97. tests/test_cli/test_cli_deploy.py +15 -13
  98. tests/test_cli/test_cli_inventory.py +53 -0
  99. tests/test_connectors/test_ssh.py +302 -182
  100. tests/test_connectors/test_sshuserclient.py +68 -1
  101. pyinfra-3.1.1.dist-info/RECORD +0 -172
  102. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/WHEEL +0 -0
  103. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/entry_points.txt +0 -0
  104. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,116 @@
1
+ """
2
+ Manage FreeBSD services.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing_extensions import List, Optional, Union
8
+
9
+ from pyinfra import host
10
+ from pyinfra.api import QuoteString, StringCommand, operation
11
+ from pyinfra.api.exceptions import OperationValueError
12
+ from pyinfra.facts.freebsd import ServiceScript, ServiceStatus
13
+
14
+ SRV_STARTED: str = "started"
15
+ SRV_STOPPED: str = "stopped"
16
+ SRV_RESTARTED: str = "restarted"
17
+ SRV_RELOADED: str = "reloaded"
18
+ SRV_CUSTOM: str = "custom"
19
+
20
+
21
+ @operation()
22
+ def service(
23
+ srvname: str,
24
+ jail: Optional[str] = None,
25
+ srvstate: str = SRV_STARTED,
26
+ command: Optional[Union[str, List[str]]] = None,
27
+ environment: Optional[List[str]] = None,
28
+ verbose: bool = False,
29
+ ):
30
+ """
31
+ Control (start/stop/etc.) ``rc(8)`` scripts.
32
+
33
+ + srvname: Service.
34
+ + jail: See ``-j`` in ``service(8)``.
35
+ + srvstate: Desire state of the service.
36
+ + command: When ``srvstate`` is ``custom``, the command to execute.
37
+ + environment: See ``-E`` in ``service(8)``.
38
+ + verbose: See ``-v`` in ``service(8)``.
39
+
40
+ States:
41
+ There are a few states you can use to manipulate the service:
42
+
43
+ - started: The service must be started.
44
+ - stopped: The service must be stopped.
45
+ - restarted: The service must be restarted.
46
+ - reloaded: The service must be reloaded.
47
+ - custom: Run a custom command for this service.
48
+
49
+ **Examples:**
50
+
51
+ .. code:: python
52
+
53
+ # Start a service.
54
+ service.service(
55
+ "beanstalkd",
56
+ srvstate="started"
57
+ )
58
+
59
+ # Execute a custom command.
60
+ service.service(
61
+ "sopel",
62
+ srvstate="custom",
63
+ command="configure"
64
+ )
65
+ """
66
+
67
+ if not host.get_fact(ServiceScript, srvname=srvname, jail=jail):
68
+ host.noop(f"Cannot find rc(8) script '{srvname}'")
69
+ return
70
+
71
+ args: List[Union[str, "QuoteString"]] = []
72
+
73
+ args.append("service")
74
+
75
+ if verbose:
76
+ args.append("-v")
77
+
78
+ if jail is not None:
79
+ args.extend(["-j", QuoteString(jail)])
80
+
81
+ if environment is not None:
82
+ for env_var in environment:
83
+ args.extend(["-E", QuoteString(env_var)])
84
+
85
+ if srvstate == SRV_STARTED:
86
+ if host.get_fact(ServiceStatus, srvname=srvname, jail=jail):
87
+ host.noop(f"Service '{srvname}' already started")
88
+ return
89
+
90
+ args.extend([QuoteString(srvname), "start"])
91
+
92
+ elif srvstate == SRV_STOPPED:
93
+ if not host.get_fact(ServiceStatus, srvname=srvname, jail=jail):
94
+ host.noop(f"Service '{srvname}' already stopped")
95
+ return
96
+
97
+ args.extend([QuoteString(srvname), "stop"])
98
+ elif srvstate == SRV_RESTARTED:
99
+ args.extend([QuoteString(srvname), "restart"])
100
+
101
+ elif srvstate == SRV_RELOADED:
102
+ args.extend([QuoteString(srvname), "reload"])
103
+
104
+ elif srvstate == SRV_CUSTOM:
105
+ args.append(QuoteString(srvname))
106
+
107
+ if command is not None:
108
+ if isinstance(command, str):
109
+ command = [command]
110
+
111
+ args.extend([QuoteString(c) for c in command])
112
+
113
+ else:
114
+ raise OperationValueError(f"Invalid service command: {srvstate}")
115
+
116
+ yield StringCommand(*args)
@@ -0,0 +1,92 @@
1
+ """
2
+ Manipulate system rc files.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing_extensions import List, Optional, Union
8
+
9
+ from pyinfra import host
10
+ from pyinfra.api import QuoteString, StringCommand, operation
11
+ from pyinfra.api.exceptions import OperationValueError
12
+ from pyinfra.facts.freebsd import Sysrc
13
+
14
+ SYSRC_ADD: str = "add"
15
+ SYSRC_SUB: str = "sub"
16
+ SYSRC_SET: str = "set"
17
+ SYSRC_DEL: str = "del"
18
+
19
+
20
+ @operation()
21
+ def sysrc(
22
+ parameter: str,
23
+ value: str,
24
+ jail: Optional[str] = None,
25
+ command: str = SYSRC_SET,
26
+ overwrite: bool = False,
27
+ ):
28
+ """
29
+ Safely edit system rc files.
30
+
31
+ + parameter: Parameter to manipulate.
32
+ + value: Value, if the parameter requires it.
33
+ + jail: See ``-j`` in ``sysrc(8)``.
34
+ + command: Desire state of the parameter.
35
+ + overwrite: Overwrite the value of the parameter when ``command`` is set to ``set``.
36
+
37
+ Commands:
38
+ There are a few commands you can use to manipulate the rc file:
39
+
40
+ - add: Adds the value to the parameter.
41
+ - sub: Delete the parameter value.
42
+ - set: Change the parameter value. If the parameter already has a value
43
+ set, the changes will not be applied unless ``overwrite`` is set
44
+ to ``True``.
45
+ - del: Delete the parameter.
46
+
47
+ **Example:**
48
+
49
+ .. code:: python
50
+
51
+ sysrc.sysrc(
52
+ "beanstalkd_enable",
53
+ "YES",
54
+ command="set"
55
+ )
56
+ """
57
+
58
+ args: List[Union[str, "QuoteString"]] = []
59
+
60
+ args.extend(["sysrc", "-i"])
61
+
62
+ if command == SYSRC_DEL:
63
+ sign = "="
64
+
65
+ if not host.get_fact(Sysrc, parameter=parameter, jail=jail):
66
+ host.noop(f"Cannot find sysrc(8) parameter '{parameter}'")
67
+ return
68
+
69
+ args.append("-x")
70
+
71
+ elif command == SYSRC_SET:
72
+ sign = "="
73
+
74
+ if not overwrite and host.get_fact(Sysrc, parameter=parameter, jail=jail):
75
+ host.noop(f"sysrc(8) parameter '{parameter}' already set")
76
+ return
77
+
78
+ elif command == SYSRC_ADD:
79
+ sign = "+="
80
+
81
+ elif command == SYSRC_SUB:
82
+ sign = "-="
83
+
84
+ else:
85
+ raise OperationValueError(f"Invalid sysrc command: {command}")
86
+
87
+ if jail is not None:
88
+ args.extend(["-j", QuoteString(jail)])
89
+
90
+ args.extend(["--", QuoteString(f"{parameter}{sign}{value}")])
91
+
92
+ yield StringCommand(*args)
pyinfra/operations/git.py CHANGED
@@ -16,24 +16,40 @@ from .util.files import chown, unix_path_join
16
16
 
17
17
 
18
18
  @operation()
19
- def config(key: str, value: str, multi_value=False, repo: str | None = None):
19
+ def config(key: str, value: str, multi_value=False, repo: str | None = None, system=False):
20
20
  """
21
- Manage git config for a repository or globally.
21
+ Manage git config at repository, user or system level.
22
22
 
23
23
  + key: the key of the config to ensure
24
24
  + value: the value this key should have
25
25
  + multi_value: Add the value rather than set it for settings that can have multiple values
26
26
  + repo: specify the git repo path to edit local config (defaults to global)
27
+ + system: whether, when ``repo`` is unspecified, to work at system level (or default to global)
27
28
 
28
- **Example:**
29
+ **Examples:**
29
30
 
30
31
  .. code:: python
31
32
 
32
33
  git.config(
33
- name="Ensure user name is set for a repo",
34
+ name="Always prune specified repo",
35
+ key="fetch.prune",
36
+ value="true",
37
+ repo="/usr/local/src/pyinfra",
38
+ )
39
+
40
+ git.config(
41
+ name="Ensure user name is set for all repos of specified user",
34
42
  key="user.name",
35
43
  value="Anon E. Mouse",
36
- repo="/usr/local/src/pyinfra",
44
+ _sudo=True,
45
+ _sudo_user="anon"
46
+ )
47
+
48
+ git.config(
49
+ name="Ensure same date format for all users",
50
+ key="log.date",
51
+ value="iso",
52
+ system=True
37
53
  )
38
54
 
39
55
  """
@@ -41,14 +57,14 @@ def config(key: str, value: str, multi_value=False, repo: str | None = None):
41
57
  existing_config = {}
42
58
 
43
59
  if not repo:
44
- existing_config = host.get_fact(GitConfig)
60
+ existing_config = host.get_fact(GitConfig, system=system)
45
61
 
46
62
  # Only get the config if the repo exists at this stage
47
63
  elif host.get_fact(Directory, path=unix_path_join(repo, ".git")):
48
64
  existing_config = host.get_fact(GitConfig, repo=repo)
49
65
 
50
66
  if repo is None:
51
- base_command = "git config --global"
67
+ base_command = "git config" + (" --system" if system else " --global")
52
68
  else:
53
69
  base_command = "cd {0} && git config --local".format(repo)
54
70
 
@@ -0,0 +1,88 @@
1
+ """
2
+ Manage packages on OpenWrt using opkg
3
+ + ``update`` - update local copy of package information
4
+ + ``packages`` - install and remove packages
5
+
6
+ see https://openwrt.org/docs/guide-user/additional-software/opkg
7
+ OpenWrt recommends against upgrading all packages thus there is no ``opkg.upgrade`` function
8
+ """
9
+
10
+ from typing import List, Union
11
+
12
+ from pyinfra import host
13
+ from pyinfra.api import StringCommand, operation
14
+ from pyinfra.facts.opkg import OpkgPackages
15
+ from pyinfra.operations.util.packaging import ensure_packages
16
+
17
+ EQUALS = "="
18
+
19
+
20
+ @operation(is_idempotent=False)
21
+ def update():
22
+ """
23
+ Update the local opkg information.
24
+ """
25
+
26
+ yield StringCommand("opkg update")
27
+
28
+
29
+ _update = update
30
+
31
+
32
+ @operation()
33
+ def packages(
34
+ packages: Union[str, List[str]] = "",
35
+ present: bool = True,
36
+ latest: bool = False,
37
+ update: bool = True,
38
+ ):
39
+ """
40
+ Add/remove/update opkg packages.
41
+
42
+ + packages: package or list of packages to that must/must not be present
43
+ + present: whether the package(s) should be installed (default True) or removed
44
+ + latest: whether to attempt to upgrade the specified package(s) (default False)
45
+ + update: run ``opkg update`` before installing packages (default True)
46
+
47
+ Not Supported:
48
+ Opkg does not support version pinning, i.e. ``<pkg>=<version>`` is not allowed
49
+ and will cause an exception.
50
+
51
+ **Examples:**
52
+
53
+ .. code:: python
54
+
55
+ # Ensure packages are installed∂ (will not force package upgrade)
56
+ opkg.packages(['asterisk', 'vim'], name="Install Asterisk and Vim")
57
+
58
+ # Install the latest versions of packages (always check)
59
+ opkg.packages(
60
+ 'vim',
61
+ latest=True,
62
+ name="Ensure we have the latest version of Vim"
63
+ )
64
+ """
65
+ if str(packages) == "" or (
66
+ isinstance(packages, list) and (len(packages) < 1 or all(len(p) < 1 for p in packages))
67
+ ):
68
+ host.noop("empty or invalid package list provided to opkg.packages")
69
+ return
70
+
71
+ pkg_list = packages if isinstance(packages, list) else [packages]
72
+ have_equals = ",".join([pkg.split(EQUALS)[0] for pkg in pkg_list if EQUALS in pkg])
73
+ if len(have_equals) > 0:
74
+ raise ValueError(f"opkg does not support version pinning but found for: '{have_equals}'")
75
+
76
+ if update:
77
+ yield from _update._inner()
78
+
79
+ yield from ensure_packages(
80
+ host,
81
+ pkg_list,
82
+ host.get_fact(OpkgPackages),
83
+ present,
84
+ install_command="opkg install",
85
+ upgrade_command="opkg upgrade",
86
+ uninstall_command="opkg remove",
87
+ latest=latest,
88
+ )
pyinfra/operations/pip.py CHANGED
@@ -174,12 +174,13 @@ def packages(
174
174
  install_command_args.append(extra_install_args)
175
175
  install_command = " ".join(install_command_args)
176
176
 
177
+ upgrade_command = "{0} --upgrade".format(install_command)
177
178
  uninstall_command = " ".join([pip, "uninstall", "--yes"])
178
179
 
179
180
  # (un)Install requirements
180
181
  if requirements is not None:
181
182
  if present:
182
- yield "{0} -r {1}".format(install_command, requirements)
183
+ yield "{0} -r {1}".format(upgrade_command if latest else install_command, requirements)
183
184
  else:
184
185
  yield "{0} -r {1}".format(uninstall_command, requirements)
185
186
 
@@ -199,7 +200,7 @@ def packages(
199
200
  present,
200
201
  install_command=install_command,
201
202
  uninstall_command=uninstall_command,
202
- upgrade_command="{0} --upgrade".format(install_command),
203
+ upgrade_command=upgrade_command,
203
204
  version_join="==",
204
205
  latest=latest,
205
206
  )
@@ -0,0 +1,90 @@
1
+ """
2
+ Manage pipx (python) applications.
3
+ """
4
+
5
+ from pyinfra import host
6
+ from pyinfra.api import operation
7
+ from pyinfra.facts.pipx import PipxEnvironment, PipxPackages
8
+ from pyinfra.facts.server import Path
9
+
10
+ from .util.packaging import ensure_packages
11
+
12
+
13
+ @operation()
14
+ def packages(
15
+ packages=None,
16
+ present=True,
17
+ latest=False,
18
+ extra_args=None,
19
+ ):
20
+ """
21
+ Install/remove/update pipx packages.
22
+
23
+ + packages: list of packages to ensure
24
+ + present: whether the packages should be installed
25
+ + latest: whether to upgrade packages without a specified version
26
+ + extra_args: additional arguments to the pipx command
27
+
28
+ Versions:
29
+ Package versions can be pinned like pip: ``<pkg>==<version>``.
30
+
31
+ **Example:**
32
+
33
+ .. code:: python
34
+
35
+ pipx.packages(
36
+ name="Install ",
37
+ packages=["pyinfra"],
38
+ )
39
+ """
40
+
41
+ prep_install_command = ["pipx", "install"]
42
+
43
+ if extra_args:
44
+ prep_install_command.append(extra_args)
45
+ install_command = " ".join(prep_install_command)
46
+
47
+ uninstall_command = "pipx uninstall"
48
+ upgrade_command = "pipx upgrade"
49
+
50
+ current_packages = host.get_fact(PipxPackages)
51
+
52
+ # pipx support only one package name at a time
53
+ for package in packages:
54
+ yield from ensure_packages(
55
+ host,
56
+ [package],
57
+ current_packages,
58
+ present,
59
+ install_command=install_command,
60
+ uninstall_command=uninstall_command,
61
+ upgrade_command=upgrade_command,
62
+ version_join="==",
63
+ latest=latest,
64
+ )
65
+
66
+
67
+ @operation()
68
+ def upgrade_all():
69
+ """
70
+ Upgrade all pipx packages.
71
+ """
72
+ yield "pipx upgrade-all"
73
+
74
+
75
+ @operation()
76
+ def ensure_path():
77
+ """
78
+ Ensure pipx bin dir is in the PATH.
79
+ """
80
+
81
+ # Fetch the current user's PATH
82
+ path = host.get_fact(Path)
83
+ # Fetch the pipx environment variables
84
+ pipx_env = host.get_fact(PipxEnvironment)
85
+
86
+ # If the pipx bin dir is already in the user's PATH, we're done
87
+ if "PIPX_BIN_DIR" in pipx_env and pipx_env["PIPX_BIN_DIR"] in path.split(":"):
88
+ host.noop("pipx bin dir is already in the PATH")
89
+ else:
90
+ yield "pipx ensurepath"