pyinfra 3.0.1__tar.gz → 3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyinfra-3.0.1 → pyinfra-3.1}/CHANGELOG.md +36 -0
- {pyinfra-3.0.1/pyinfra.egg-info → pyinfra-3.1}/PKG-INFO +1 -1
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/host.py +5 -2
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/operation.py +12 -10
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/util.py +4 -2
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/ssh.py +1 -1
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/terraform.py +30 -20
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/apt.py +1 -1
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/files.py +8 -1
- pyinfra-3.1/pyinfra/facts/flatpak.py +70 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/systemd.py +4 -1
- pyinfra-3.1/pyinfra/facts/zfs.py +57 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/docker.py +4 -4
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/files.py +9 -2
- pyinfra-3.1/pyinfra/operations/flatpak.py +79 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/mysql.py +5 -5
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/postgres.py +3 -3
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/server.py +9 -9
- pyinfra-3.1/pyinfra/operations/zfs.py +175 -0
- {pyinfra-3.0.1 → pyinfra-3.1/pyinfra.egg-info}/PKG-INFO +1 -1
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra.egg-info/SOURCES.txt +4 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra_cli/inventory.py +34 -2
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra_cli/main.py +8 -8
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra_cli/prints.py +17 -1
- {pyinfra-3.0.1 → pyinfra-3.1}/setup.cfg +1 -2
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/test_api_inventory.py +21 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_cli/test_cli_deploy.py +7 -2
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_connectors/test_terraform.py +6 -6
- {pyinfra-3.0.1 → pyinfra-3.1}/LICENSE.md +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/MANIFEST.in +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/README.md +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/__main__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/arguments.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/arguments_typed.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/command.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/config.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/connect.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/connectors.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/deploy.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/exceptions.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/facts.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/inventory.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/operations.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/api/state.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/base.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/chroot.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/docker.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/dockerssh.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/local.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/ssh_util.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/sshuserclient/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/sshuserclient/client.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/sshuserclient/config.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/util.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/connectors/vagrant.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/context.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/apk.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/brew.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/bsdinit.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/cargo.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/choco.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/deb.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/dnf.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/docker.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/gem.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/git.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/gpg.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/hardware.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/iptables.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/launchd.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/lxd.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/mysql.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/npm.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/openrc.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/pacman.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/pip.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/pkg.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/pkgin.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/postgres.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/postgresql.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/rpm.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/runit.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/selinux.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/server.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/snap.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/sysvinit.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/upstart.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/util/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/util/databases.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/util/packaging.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/util/win_files.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/vzctl.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/xbps.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/yum.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/facts/zypper.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/local.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/apk.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/apt.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/brew.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/bsdinit.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/cargo.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/choco.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/dnf.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/gem.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/git.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/iptables.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/launchd.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/lxd.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/npm.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/openrc.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/pacman.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/pip.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/pkg.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/pkgin.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/postgresql.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/puppet.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/python.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/runit.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/selinux.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/snap.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/ssh.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/systemd.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/sysvinit.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/upstart.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/util/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/util/docker.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/util/files.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/util/packaging.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/util/service.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/vzctl.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/xbps.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/yum.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/operations/zypper.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/progress.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/py.typed +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra/version.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra.egg-info/dependency_links.txt +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra.egg-info/entry_points.txt +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra.egg-info/requires.txt +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra.egg-info/top_level.txt +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra_cli/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra_cli/__main__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra_cli/commands.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra_cli/exceptions.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra_cli/log.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra_cli/util.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyinfra_cli/virtualenv.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/pyproject.toml +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/setup.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/test_api.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/test_api_arguments.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/test_api_command.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/test_api_config.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/test_api_deploys.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/test_api_facts.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/test_api_host.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/test_api_operations.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_api/test_api_util.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_cli/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_cli/test_cli.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_cli/test_cli_exceptions.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_cli/test_cli_inventory.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_cli/test_cli_util.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_cli/test_context_objects.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_cli/util.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_connectors/__init__.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_connectors/test_chroot.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_connectors/test_docker.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_connectors/test_dockerssh.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_connectors/test_local.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_connectors/test_ssh.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_connectors/test_sshuserclient.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_connectors/test_util.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_connectors/test_vagrant.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_facts.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_global_arguments.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_operations.py +0 -0
- {pyinfra-3.0.1 → pyinfra-3.1}/tests/test_operations_utils.py +0 -0
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
# v3.1
|
|
2
|
+
|
|
3
|
+
Here's pyinfra 3.1 - a release primarily driven by contributors new and old - a HUGE THANK YOU to all of you who dedicate time to work on pushing pyinfra forward. New stuff:
|
|
4
|
+
|
|
5
|
+
- Add `zfs` operations (`dataset`, `snapshot`, `volume`, `filesystem`) facts (`Pools`, `Datasets`, `Filesystems`, `Snapshots`, `Volumes`) (@taliaferro)
|
|
6
|
+
- Add `flatpak` operations (`packages`) and facts (`FlatpakPackage`, `FlatpakPackages`) (@JustScreaMy)
|
|
7
|
+
- Add `jinja_env_kwargs` argument to `files.template` operation (@DonDebonair)
|
|
8
|
+
- Add using dictionaries as `@terraform` output (map from group -> hosts)
|
|
9
|
+
- Add default `@terraform` output key - `pyinfra_inventory.value`, promote connector to beta
|
|
10
|
+
- Add support for multiple keys in each `server.authorized_keys` file (@matthijskooijman)
|
|
11
|
+
- Add print all dependency versions with `--support` flag (@kytta)
|
|
12
|
+
|
|
13
|
+
Fixes:
|
|
14
|
+
|
|
15
|
+
- Fix when `ssh_hostname` is set as override data, don't do inventory hostname check
|
|
16
|
+
- Fix `apt.AptSources` parsing special characters (@CondensedTea)
|
|
17
|
+
- Fix `server.reboot` connection detection (@bauen1 + @lemmi)
|
|
18
|
+
- Fix systemd flagging of sockets running (@bauen1)
|
|
19
|
+
- Fix mysql dump quoting (@simonhammes)
|
|
20
|
+
- Fix tilde expansion in files facts (@simonhammes)
|
|
21
|
+
- Fix host lookup check with SSH alias config (@simonhammes)
|
|
22
|
+
- Fix crontab comparison (@andrew-d)
|
|
23
|
+
|
|
24
|
+
Docs/internal tweaks:
|
|
25
|
+
|
|
26
|
+
- Improve operations documentation (@bauen1)
|
|
27
|
+
- Default to local machine if `user_name` set in systecmt (@bauen1)
|
|
28
|
+
- Improve efficiency of Docker operations (@apecnascimento)
|
|
29
|
+
- Shallow copy `host.data` data to mutation
|
|
30
|
+
|
|
31
|
+
# v3.0.2
|
|
32
|
+
|
|
33
|
+
- Fix `OperationMeta.did_change`: this is now a function as originally designed
|
|
34
|
+
- Add quick test for `host.when` context manager
|
|
35
|
+
- Remove extra detected changes note when not relevant
|
|
36
|
+
|
|
1
37
|
# v3.0.1
|
|
2
38
|
|
|
3
39
|
- Switch to `command -v` not `which` in `server.Which` fact (@lemmi)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from contextlib import contextmanager
|
|
4
|
+
from copy import copy
|
|
4
5
|
from typing import (
|
|
5
6
|
TYPE_CHECKING,
|
|
6
7
|
Any,
|
|
@@ -41,7 +42,6 @@ def extract_callable_datas(
|
|
|
41
42
|
# the data is stored on the state temporarily.
|
|
42
43
|
if callable(data):
|
|
43
44
|
data = data()
|
|
44
|
-
|
|
45
45
|
yield data
|
|
46
46
|
|
|
47
47
|
|
|
@@ -66,7 +66,10 @@ class HostData:
|
|
|
66
66
|
def __getattr__(self, key: str):
|
|
67
67
|
for data in extract_callable_datas(self.datas):
|
|
68
68
|
try:
|
|
69
|
-
|
|
69
|
+
# Take a shallow copy of the object here, we don't want modifications
|
|
70
|
+
# to host.data.<X> to stick, instead setting host.data.<Y> = is the
|
|
71
|
+
# correct way to achieve this (see __setattr__).
|
|
72
|
+
return copy(data[key])
|
|
70
73
|
except KeyError:
|
|
71
74
|
pass
|
|
72
75
|
|
|
@@ -60,7 +60,7 @@ class OperationMeta:
|
|
|
60
60
|
if self._commands is not None:
|
|
61
61
|
return (
|
|
62
62
|
"OperationMeta(executed=True, "
|
|
63
|
-
f"success={self.did_succeed}, hash={self._hash}, commands={len(self._commands)})"
|
|
63
|
+
f"success={self.did_succeed()}, hash={self._hash}, commands={len(self._commands)})"
|
|
64
64
|
)
|
|
65
65
|
return (
|
|
66
66
|
"OperationMeta(executed=False, "
|
|
@@ -87,6 +87,12 @@ class OperationMeta:
|
|
|
87
87
|
if not self.is_complete():
|
|
88
88
|
raise RuntimeError("Cannot evaluate operation result before execution")
|
|
89
89
|
|
|
90
|
+
@property
|
|
91
|
+
def executed(self) -> bool:
|
|
92
|
+
if self._commands is None:
|
|
93
|
+
return False
|
|
94
|
+
return len(self._commands) > 0
|
|
95
|
+
|
|
90
96
|
@property
|
|
91
97
|
def will_change(self) -> bool:
|
|
92
98
|
if self._maybe_is_change is not None:
|
|
@@ -100,16 +106,12 @@ class OperationMeta:
|
|
|
100
106
|
self._maybe_is_change = False
|
|
101
107
|
return False
|
|
102
108
|
|
|
103
|
-
def
|
|
109
|
+
def did_change(self) -> bool:
|
|
110
|
+
self._raise_if_not_complete()
|
|
104
111
|
return bool(self._success and len(self._commands or []) > 0)
|
|
105
112
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return context.host.when(self._did_change)
|
|
109
|
-
|
|
110
|
-
@property
|
|
111
|
-
def did_not_change(self):
|
|
112
|
-
return context.host.when(lambda: not self._did_change())
|
|
113
|
+
def did_not_change(self) -> bool:
|
|
114
|
+
return not self.did_change()
|
|
113
115
|
|
|
114
116
|
def did_succeed(self, _raise_if_not_complete=True) -> bool:
|
|
115
117
|
if _raise_if_not_complete:
|
|
@@ -124,7 +126,7 @@ class OperationMeta:
|
|
|
124
126
|
@property
|
|
125
127
|
def changed(self) -> bool:
|
|
126
128
|
if self.is_complete():
|
|
127
|
-
return self.
|
|
129
|
+
return self.did_change()
|
|
128
130
|
return self.will_change
|
|
129
131
|
|
|
130
132
|
@property
|
|
@@ -139,12 +139,13 @@ def get_operation_order_from_stack(state: "State"):
|
|
|
139
139
|
return line_numbers
|
|
140
140
|
|
|
141
141
|
|
|
142
|
-
def get_template(filename_or_io: str | IO):
|
|
142
|
+
def get_template(filename_or_io: str | IO, jinja_env_kwargs: dict[str, Any] | None = None):
|
|
143
143
|
"""
|
|
144
144
|
Gets a jinja2 ``Template`` object for the input filename or string, with caching
|
|
145
145
|
based on the filename of the template, or the SHA1 of the input string.
|
|
146
146
|
"""
|
|
147
|
-
|
|
147
|
+
if jinja_env_kwargs is None:
|
|
148
|
+
jinja_env_kwargs = {}
|
|
148
149
|
file_data = get_file_io(filename_or_io, mode="r")
|
|
149
150
|
cache_key = file_data.cache_key
|
|
150
151
|
|
|
@@ -158,6 +159,7 @@ def get_template(filename_or_io: str | IO):
|
|
|
158
159
|
undefined=StrictUndefined,
|
|
159
160
|
keep_trailing_newline=True,
|
|
160
161
|
loader=FileSystemLoader(getcwd()),
|
|
162
|
+
**jinja_env_kwargs,
|
|
161
163
|
).from_string(template_string)
|
|
162
164
|
|
|
163
165
|
if cache_key:
|
|
@@ -10,16 +10,15 @@ from .base import BaseConnector
|
|
|
10
10
|
|
|
11
11
|
@memoize
|
|
12
12
|
def show_warning():
|
|
13
|
-
logger.warning("The @terraform connector is in
|
|
13
|
+
logger.warning("The @terraform connector is in beta!")
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def _flatten_dict_gen(d, parent_key, sep):
|
|
17
17
|
for k, v in d.items():
|
|
18
18
|
new_key = parent_key + sep + k if parent_key else k
|
|
19
|
+
yield new_key, v
|
|
19
20
|
if isinstance(v, dict):
|
|
20
21
|
yield from _flatten_dict(v, new_key, sep=sep).items()
|
|
21
|
-
else:
|
|
22
|
-
yield new_key, v
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
def _flatten_dict(d: dict, parent_key: str = "", sep: str = "."):
|
|
@@ -82,7 +81,9 @@ class TerraformInventoryConnector(BaseConnector):
|
|
|
82
81
|
show_warning()
|
|
83
82
|
|
|
84
83
|
if not name:
|
|
85
|
-
|
|
84
|
+
# This is the default which allows one to create a Terraform output
|
|
85
|
+
# "pyinfra" and directly call: pyinfra @terraform ...
|
|
86
|
+
name = "pyinfra_inventory.value"
|
|
86
87
|
|
|
87
88
|
with progress_spinner({"fetch terraform output"}):
|
|
88
89
|
tf_output_raw = local.shell("terraform output -json")
|
|
@@ -96,27 +97,36 @@ class TerraformInventoryConnector(BaseConnector):
|
|
|
96
97
|
keys = "\n".join(f" - {k}" for k in tf_output.keys())
|
|
97
98
|
raise InventoryError(f"No Terraform output with key: `{name}`, valid keys:\n{keys}")
|
|
98
99
|
|
|
99
|
-
if not isinstance(tf_output_value, list):
|
|
100
|
+
if not isinstance(tf_output_value, (list, dict)):
|
|
100
101
|
raise InventoryError(
|
|
101
102
|
"Invalid Terraform output type, should be `list`, got "
|
|
102
103
|
f"`{type(tf_output_value).__name__}`",
|
|
103
104
|
)
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
raise InventoryError(
|
|
110
|
-
"Invalid Terraform list item, missing `name` or `ssh_hostname` keys",
|
|
111
|
-
)
|
|
112
|
-
yield f"@terraform/{name}", ssh_target, ["@terraform"]
|
|
113
|
-
|
|
114
|
-
elif isinstance(ssh_target, str):
|
|
115
|
-
data = {"ssh_hostname": ssh_target}
|
|
116
|
-
yield f"@terraform/{ssh_target}", data, ["@terraform"]
|
|
106
|
+
if isinstance(tf_output_value, list):
|
|
107
|
+
tf_output_value = {
|
|
108
|
+
"all": tf_output_value,
|
|
109
|
+
}
|
|
117
110
|
|
|
118
|
-
|
|
111
|
+
for group_name, hosts in tf_output_value.items():
|
|
112
|
+
if not isinstance(hosts, list):
|
|
119
113
|
raise InventoryError(
|
|
120
|
-
"Invalid Terraform
|
|
121
|
-
f"`{type(
|
|
114
|
+
"Invalid Terraform map value type, all values should be `list`, got "
|
|
115
|
+
f"`{type(hosts).__name__}`",
|
|
122
116
|
)
|
|
117
|
+
for host in hosts:
|
|
118
|
+
if isinstance(host, dict):
|
|
119
|
+
name = host.pop("name", host.get("ssh_hostname"))
|
|
120
|
+
if name is None:
|
|
121
|
+
raise InventoryError(
|
|
122
|
+
"Invalid Terraform list item, missing `name` or `ssh_hostname` keys",
|
|
123
|
+
)
|
|
124
|
+
yield f"@terraform/{name}", host, ["@terraform", group_name]
|
|
125
|
+
elif isinstance(host, str):
|
|
126
|
+
data = {"ssh_hostname": host}
|
|
127
|
+
yield f"@terraform/{host}", data, ["@terraform", group_name]
|
|
128
|
+
else:
|
|
129
|
+
raise InventoryError(
|
|
130
|
+
"Invalid Terraform list item, should be `dict` or `str` got "
|
|
131
|
+
f"`{type(host).__name__}`",
|
|
132
|
+
)
|
|
@@ -9,7 +9,7 @@ from .util import make_cat_files_command
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def parse_apt_repo(name):
|
|
12
|
-
regex = r"^(deb(?:-src)?)(?:\s+\[([^\]]+)\])?\s+([^\s]+)\s+([
|
|
12
|
+
regex = r"^(deb(?:-src)?)(?:\s+\[([^\]]+)\])?\s+([^\s]+)\s+([^\s]+)\s+([a-z-\s]*)$"
|
|
13
13
|
|
|
14
14
|
matches = re.match(regex, name)
|
|
15
15
|
|
|
@@ -5,6 +5,7 @@ The files facts provide information about the filesystem and it's contents on th
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import re
|
|
8
|
+
import shlex
|
|
8
9
|
import stat
|
|
9
10
|
from datetime import datetime
|
|
10
11
|
from typing import TYPE_CHECKING, List, Optional, Tuple, Union
|
|
@@ -109,13 +110,19 @@ class File(FactBase[Union[FileDict, Literal[False], None]]):
|
|
|
109
110
|
type = "file"
|
|
110
111
|
|
|
111
112
|
def command(self, path):
|
|
113
|
+
if path.startswith("~/"):
|
|
114
|
+
# Do not quote leading tilde to ensure that it gets properly expanded by the shell
|
|
115
|
+
path = f"~/{shlex.quote(path[2:])}"
|
|
116
|
+
else:
|
|
117
|
+
path = QuoteString(path)
|
|
118
|
+
|
|
112
119
|
return make_formatted_string_command(
|
|
113
120
|
(
|
|
114
121
|
# only stat if the path exists (file or symlink)
|
|
115
122
|
"! (test -e {0} || test -L {0} ) || "
|
|
116
123
|
"( {linux_stat_command} {0} 2> /dev/null || {bsd_stat_command} {0} )"
|
|
117
124
|
),
|
|
118
|
-
|
|
125
|
+
path,
|
|
119
126
|
linux_stat_command=LINUX_STAT_COMMAND,
|
|
120
127
|
bsd_stat_command=BSD_STAT_COMMAND,
|
|
121
128
|
)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from pyinfra.api import FactBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FlatpakBaseFact(FactBase):
|
|
9
|
+
abstract = True
|
|
10
|
+
|
|
11
|
+
def requires_command(self, *args, **kwargs) -> str:
|
|
12
|
+
return "flatpak"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FlatpakPackage(FlatpakBaseFact):
|
|
16
|
+
"""
|
|
17
|
+
Returns information for an installed flatpak package
|
|
18
|
+
|
|
19
|
+
.. code:: python
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
"id": "org.signal.Signal",
|
|
23
|
+
"ref": "app/org.signal.Signal/x86_64/stable",
|
|
24
|
+
"version": "7.12.0"
|
|
25
|
+
}
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
default = dict
|
|
29
|
+
_regexes = {
|
|
30
|
+
"id": "^[ ]+ID:[ ]+(.*)$",
|
|
31
|
+
"ref": r"^[ ]+Ref:[ ]+(.*)$",
|
|
32
|
+
"version": r"^[ ]+Version:[ ]+([\w\d.-]+).*$",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def command(self, package):
|
|
36
|
+
return f"flatpak info {package}"
|
|
37
|
+
|
|
38
|
+
def process(self, output):
|
|
39
|
+
data = {}
|
|
40
|
+
for line in output:
|
|
41
|
+
for regex_name, regex in self._regexes.items():
|
|
42
|
+
matches = re.match(regex, line)
|
|
43
|
+
if matches:
|
|
44
|
+
data[regex_name] = matches.group(1)
|
|
45
|
+
|
|
46
|
+
return data
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class FlatpakPackages(FlatpakBaseFact):
|
|
50
|
+
"""
|
|
51
|
+
Returns a list of installed flatpak packages:
|
|
52
|
+
|
|
53
|
+
.. code:: python
|
|
54
|
+
|
|
55
|
+
[
|
|
56
|
+
"org.gnome.Platform",
|
|
57
|
+
"org.kde.Platform",
|
|
58
|
+
"org.kde.Sdk",
|
|
59
|
+
"org.libreoffice.LibreOffice",
|
|
60
|
+
"org.videolan.VLC"
|
|
61
|
+
]
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
default = list
|
|
65
|
+
|
|
66
|
+
def command(self):
|
|
67
|
+
return "flatpak list --columns=application"
|
|
68
|
+
|
|
69
|
+
def process(self, output):
|
|
70
|
+
return [flatpak for flatpak in output[1:]]
|
|
@@ -32,6 +32,9 @@ def _make_systemctl_cmd(user_mode=False, machine=None, user_name=None):
|
|
|
32
32
|
systemctl_cmd.append("--machine={1}@{0}".format(machine, user_name))
|
|
33
33
|
else:
|
|
34
34
|
systemctl_cmd.append("--machine={0}".format(machine))
|
|
35
|
+
elif user_name is not None:
|
|
36
|
+
# If only the user is given, assume that the connection should be made to the local machine
|
|
37
|
+
systemctl_cmd.append("--machine={0}@.host".format(user_name))
|
|
35
38
|
|
|
36
39
|
return StringCommand(*systemctl_cmd)
|
|
37
40
|
|
|
@@ -58,7 +61,7 @@ class SystemdStatus(FactBase[Dict[str, bool]]):
|
|
|
58
61
|
default = dict
|
|
59
62
|
|
|
60
63
|
state_key = "SubState"
|
|
61
|
-
state_values = ["running", "waiting", "exited"]
|
|
64
|
+
state_values = ["running", "waiting", "exited", "listening"]
|
|
62
65
|
|
|
63
66
|
def command(
|
|
64
67
|
self,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage ZFS filesystems.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pyinfra.api import FactBase, ShortFactBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _process_zfs_props_table(output):
|
|
9
|
+
datasets: dict = {}
|
|
10
|
+
for line in output:
|
|
11
|
+
dataset, property, value, source = tuple(line.split("\t"))
|
|
12
|
+
if dataset not in datasets:
|
|
13
|
+
datasets[dataset] = {}
|
|
14
|
+
datasets[dataset][property] = value
|
|
15
|
+
return datasets
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Pools(FactBase):
|
|
19
|
+
def command(self):
|
|
20
|
+
return "zpool get -H all"
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def process(output):
|
|
24
|
+
return _process_zfs_props_table(output)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Datasets(FactBase):
|
|
28
|
+
def command(self):
|
|
29
|
+
return "zfs get -H all"
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def process(output):
|
|
33
|
+
return _process_zfs_props_table(output)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Filesystems(ShortFactBase):
|
|
37
|
+
fact = Datasets
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def process_data(data):
|
|
41
|
+
return {name: props for name, props in data.items() if props.get("type") == "filesystem"}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Snapshots(ShortFactBase):
|
|
45
|
+
fact = Datasets
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def process_data(data):
|
|
49
|
+
return {name: props for name, props in data.items() if props.get("type") == "snapshot"}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Volumes(ShortFactBase):
|
|
53
|
+
fact = Datasets
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def process_data(data):
|
|
57
|
+
return {name: props for name, props in data.items() if props.get("type") == "volume"}
|
|
@@ -4,7 +4,7 @@ Manager Docker Containers, Volumes and Networks
|
|
|
4
4
|
|
|
5
5
|
from pyinfra import host
|
|
6
6
|
from pyinfra.api import operation
|
|
7
|
-
from pyinfra.facts.docker import
|
|
7
|
+
from pyinfra.facts.docker import DockerContainer, DockerNetwork, DockerVolume
|
|
8
8
|
|
|
9
9
|
from .util.docker import handle_docker
|
|
10
10
|
|
|
@@ -68,7 +68,7 @@ def container(
|
|
|
68
68
|
)
|
|
69
69
|
"""
|
|
70
70
|
|
|
71
|
-
existent_container =
|
|
71
|
+
existent_container = host.get_fact(DockerContainer, object_id=container)
|
|
72
72
|
|
|
73
73
|
if force:
|
|
74
74
|
if existent_container:
|
|
@@ -183,7 +183,7 @@ def volume(volume, driver="", labels=None, present=True):
|
|
|
183
183
|
)
|
|
184
184
|
"""
|
|
185
185
|
|
|
186
|
-
existent_volume =
|
|
186
|
+
existent_volume = host.get_fact(DockerVolume, object_id=volume)
|
|
187
187
|
|
|
188
188
|
if present:
|
|
189
189
|
|
|
@@ -257,7 +257,7 @@ def network(
|
|
|
257
257
|
present=True,
|
|
258
258
|
)
|
|
259
259
|
"""
|
|
260
|
-
existent_network =
|
|
260
|
+
existent_network = host.get_fact(DockerNetwork, object_id=network)
|
|
261
261
|
|
|
262
262
|
if present:
|
|
263
263
|
if existent_network:
|
|
@@ -915,7 +915,8 @@ def template(
|
|
|
915
915
|
user: str | None = None,
|
|
916
916
|
group: str | None = None,
|
|
917
917
|
mode: str | None = None,
|
|
918
|
-
create_remote_dir=True,
|
|
918
|
+
create_remote_dir: bool = True,
|
|
919
|
+
jinja_env_kwargs: dict[str, Any] | None = None,
|
|
919
920
|
**data,
|
|
920
921
|
):
|
|
921
922
|
'''
|
|
@@ -927,12 +928,18 @@ def template(
|
|
|
927
928
|
+ group: group to own the files
|
|
928
929
|
+ mode: permissions of the files
|
|
929
930
|
+ create_remote_dir: create the remote directory if it doesn't exist
|
|
931
|
+
+ jinja_env_kwargs: keyword arguments to be passed into the jinja Environment()
|
|
930
932
|
|
|
931
933
|
``create_remote_dir``:
|
|
932
934
|
If the remote directory does not exist it will be created using the same
|
|
933
935
|
user & group as passed to ``files.put``. The mode will *not* be copied over,
|
|
934
936
|
if this is required call ``files.directory`` separately.
|
|
935
937
|
|
|
938
|
+
``jinja_env_kwargs``:
|
|
939
|
+
To have more control over how jinja2 renders your template, you can pass
|
|
940
|
+
a dict with arguments that will be passed as keyword args to the jinja2
|
|
941
|
+
`Environment() <https://jinja.palletsprojects.com/en/3.0.x/api/#jinja2.Environment>`_.
|
|
942
|
+
|
|
936
943
|
Notes:
|
|
937
944
|
Common convention is to store templates in a "templates" directory and
|
|
938
945
|
have a filename suffix with '.j2' (for jinja2).
|
|
@@ -1002,7 +1009,7 @@ def template(
|
|
|
1002
1009
|
|
|
1003
1010
|
# Render and make file-like it's output
|
|
1004
1011
|
try:
|
|
1005
|
-
output = get_template(src).render(data)
|
|
1012
|
+
output = get_template(src, jinja_env_kwargs).render(data)
|
|
1006
1013
|
except (TemplateRuntimeError, TemplateSyntaxError, UndefinedError) as e:
|
|
1007
1014
|
trace_frames = [
|
|
1008
1015
|
frame
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage flatpak packages. See https://www.flatpak.org/
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pyinfra import host
|
|
8
|
+
from pyinfra.api import operation
|
|
9
|
+
from pyinfra.facts.flatpak import FlatpakPackages
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@operation()
|
|
13
|
+
def packages(
|
|
14
|
+
packages: str | list[str] | None = None,
|
|
15
|
+
present=True,
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Install/remove a flatpak package
|
|
19
|
+
|
|
20
|
+
+ packages: List of packages
|
|
21
|
+
+ present: whether the package should be installed
|
|
22
|
+
|
|
23
|
+
**Examples:**
|
|
24
|
+
|
|
25
|
+
.. code:: python
|
|
26
|
+
|
|
27
|
+
# Install vlc flatpak
|
|
28
|
+
flatpak.package(
|
|
29
|
+
name="Install vlc",
|
|
30
|
+
packages="org.videolan.VLC",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Install multiple flatpaks
|
|
34
|
+
flatpak.package(
|
|
35
|
+
name="Install vlc and kodi",
|
|
36
|
+
packages=["org.videolan.VLC", "tv.kodi.Kodi"],
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Remove vlc
|
|
40
|
+
flatpak.package(
|
|
41
|
+
name="Remove vlc",
|
|
42
|
+
packages="org.videolan.VLC",
|
|
43
|
+
present=False,
|
|
44
|
+
)
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
if packages is None:
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
if isinstance(packages, str):
|
|
51
|
+
packages = [packages]
|
|
52
|
+
|
|
53
|
+
flatpak_packages = host.get_fact(FlatpakPackages)
|
|
54
|
+
|
|
55
|
+
install_packages = []
|
|
56
|
+
remove_packages = []
|
|
57
|
+
|
|
58
|
+
for package in packages:
|
|
59
|
+
# it's installed
|
|
60
|
+
if package in flatpak_packages:
|
|
61
|
+
if not present:
|
|
62
|
+
# we don't want it
|
|
63
|
+
remove_packages.append(package)
|
|
64
|
+
|
|
65
|
+
# it's not installed
|
|
66
|
+
if package not in flatpak_packages:
|
|
67
|
+
# we want it
|
|
68
|
+
if present:
|
|
69
|
+
install_packages.append(package)
|
|
70
|
+
|
|
71
|
+
# we don't want it
|
|
72
|
+
else:
|
|
73
|
+
host.noop(f"flatpak package {package} is not installed")
|
|
74
|
+
|
|
75
|
+
if install_packages:
|
|
76
|
+
yield " ".join(["flatpak", "install", "--noninteractive"] + install_packages)
|
|
77
|
+
|
|
78
|
+
if remove_packages:
|
|
79
|
+
yield " ".join(["flatpak", "uninstall", "--noninteractive"] + remove_packages)
|
|
@@ -554,7 +554,7 @@ def dump(
|
|
|
554
554
|
)
|
|
555
555
|
"""
|
|
556
556
|
|
|
557
|
-
yield
|
|
557
|
+
yield StringCommand(
|
|
558
558
|
make_mysql_command(
|
|
559
559
|
executable="mysqldump",
|
|
560
560
|
database=database,
|
|
@@ -563,7 +563,8 @@ def dump(
|
|
|
563
563
|
host=mysql_host,
|
|
564
564
|
port=mysql_port,
|
|
565
565
|
),
|
|
566
|
-
|
|
566
|
+
">",
|
|
567
|
+
QuoteString(dest),
|
|
567
568
|
)
|
|
568
569
|
|
|
569
570
|
|
|
@@ -595,7 +596,7 @@ def load(
|
|
|
595
596
|
)
|
|
596
597
|
"""
|
|
597
598
|
|
|
598
|
-
|
|
599
|
+
yield StringCommand(
|
|
599
600
|
make_mysql_command(
|
|
600
601
|
database=database,
|
|
601
602
|
user=mysql_user,
|
|
@@ -605,5 +606,4 @@ def load(
|
|
|
605
606
|
),
|
|
606
607
|
"<",
|
|
607
608
|
QuoteString(src),
|
|
608
|
-
|
|
609
|
-
yield StringCommand(*commands_bits)
|
|
609
|
+
)
|
|
@@ -16,7 +16,7 @@ See example/postgresql.py for detailed example
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
18
|
from pyinfra import host
|
|
19
|
-
from pyinfra.api import MaskString, StringCommand, operation
|
|
19
|
+
from pyinfra.api import MaskString, QuoteString, StringCommand, operation
|
|
20
20
|
from pyinfra.facts.postgres import (
|
|
21
21
|
PostgresDatabases,
|
|
22
22
|
PostgresRoles,
|
|
@@ -302,7 +302,7 @@ def dump(
|
|
|
302
302
|
port=psql_port,
|
|
303
303
|
),
|
|
304
304
|
">",
|
|
305
|
-
dest,
|
|
305
|
+
QuoteString(dest),
|
|
306
306
|
)
|
|
307
307
|
|
|
308
308
|
|
|
@@ -345,5 +345,5 @@ def load(
|
|
|
345
345
|
port=psql_port,
|
|
346
346
|
),
|
|
347
347
|
"<",
|
|
348
|
-
src,
|
|
348
|
+
QuoteString(src),
|
|
349
349
|
)
|