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.
- pyinfra/api/__init__.py +3 -0
- pyinfra/api/arguments.py +265 -253
- pyinfra/api/arguments_typed.py +80 -0
- pyinfra/api/command.py +68 -53
- pyinfra/api/config.py +139 -32
- pyinfra/api/connect.py +1 -1
- pyinfra/api/connectors.py +7 -26
- pyinfra/api/deploy.py +21 -52
- pyinfra/api/exceptions.py +33 -8
- pyinfra/api/facts.py +102 -137
- pyinfra/api/host.py +150 -82
- pyinfra/api/inventory.py +21 -25
- pyinfra/api/operation.py +240 -198
- pyinfra/api/operations.py +102 -148
- pyinfra/api/state.py +137 -79
- pyinfra/api/util.py +79 -86
- pyinfra/connectors/base.py +147 -0
- pyinfra/connectors/chroot.py +160 -169
- pyinfra/connectors/docker.py +220 -237
- pyinfra/connectors/dockerssh.py +231 -253
- pyinfra/connectors/local.py +196 -208
- pyinfra/connectors/ssh.py +530 -613
- pyinfra/connectors/ssh_util.py +114 -0
- pyinfra/connectors/sshuserclient/client.py +5 -3
- pyinfra/connectors/terraform.py +86 -65
- pyinfra/connectors/util.py +211 -137
- pyinfra/connectors/vagrant.py +60 -53
- pyinfra/context.py +4 -2
- pyinfra/facts/apk.py +2 -0
- pyinfra/facts/apt.py +2 -0
- pyinfra/facts/brew.py +2 -0
- pyinfra/facts/bsdinit.py +2 -0
- pyinfra/facts/cargo.py +2 -0
- pyinfra/facts/choco.py +2 -0
- pyinfra/facts/deb.py +7 -2
- pyinfra/facts/dnf.py +2 -0
- pyinfra/facts/docker.py +19 -0
- pyinfra/facts/files.py +47 -32
- pyinfra/facts/gem.py +2 -0
- pyinfra/facts/git.py +3 -1
- pyinfra/facts/gpg.py +3 -1
- pyinfra/facts/hardware.py +34 -24
- pyinfra/facts/iptables.py +5 -3
- pyinfra/facts/launchd.py +2 -0
- pyinfra/facts/lxd.py +2 -0
- pyinfra/facts/mysql.py +13 -6
- pyinfra/facts/npm.py +1 -0
- pyinfra/facts/openrc.py +2 -0
- pyinfra/facts/pacman.py +6 -2
- pyinfra/facts/pip.py +2 -0
- pyinfra/facts/pkg.py +2 -0
- pyinfra/facts/pkgin.py +2 -0
- pyinfra/facts/postgres.py +168 -0
- pyinfra/facts/postgresql.py +6 -160
- pyinfra/facts/rpm.py +12 -9
- pyinfra/facts/runit.py +68 -0
- pyinfra/facts/selinux.py +3 -1
- pyinfra/facts/server.py +80 -36
- pyinfra/facts/snap.py +2 -0
- pyinfra/facts/systemd.py +31 -12
- pyinfra/facts/sysvinit.py +10 -10
- pyinfra/facts/upstart.py +2 -0
- pyinfra/facts/util/packaging.py +7 -4
- pyinfra/facts/vzctl.py +2 -0
- pyinfra/facts/xbps.py +2 -0
- pyinfra/facts/yum.py +2 -0
- pyinfra/facts/zypper.py +2 -0
- pyinfra/local.py +4 -5
- pyinfra/operations/apk.py +6 -4
- pyinfra/operations/apt.py +46 -65
- pyinfra/operations/brew.py +17 -22
- pyinfra/operations/bsdinit.py +9 -7
- pyinfra/operations/cargo.py +4 -2
- pyinfra/operations/choco.py +4 -2
- pyinfra/operations/dnf.py +19 -23
- pyinfra/operations/docker.py +339 -0
- pyinfra/operations/files.py +188 -386
- pyinfra/operations/gem.py +4 -2
- pyinfra/operations/git.py +24 -53
- pyinfra/operations/iptables.py +29 -35
- pyinfra/operations/launchd.py +6 -7
- pyinfra/operations/lxd.py +8 -13
- pyinfra/operations/mysql.py +62 -81
- pyinfra/operations/npm.py +9 -2
- pyinfra/operations/openrc.py +6 -4
- pyinfra/operations/pacman.py +7 -8
- pyinfra/operations/pip.py +25 -24
- pyinfra/operations/pkg.py +4 -2
- pyinfra/operations/pkgin.py +6 -4
- pyinfra/operations/postgres.py +349 -0
- pyinfra/operations/postgresql.py +18 -379
- pyinfra/operations/puppet.py +3 -1
- pyinfra/operations/python.py +8 -19
- pyinfra/operations/runit.py +182 -0
- pyinfra/operations/selinux.py +47 -44
- pyinfra/operations/server.py +111 -127
- pyinfra/operations/snap.py +4 -4
- pyinfra/operations/ssh.py +20 -33
- pyinfra/operations/systemd.py +19 -15
- pyinfra/operations/sysvinit.py +9 -16
- pyinfra/operations/upstart.py +9 -7
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/docker.py +177 -0
- pyinfra/operations/util/files.py +24 -16
- pyinfra/operations/util/packaging.py +55 -57
- pyinfra/operations/util/service.py +39 -51
- pyinfra/operations/vzctl.py +12 -10
- pyinfra/operations/xbps.py +6 -4
- pyinfra/operations/yum.py +18 -22
- pyinfra/operations/zypper.py +12 -13
- pyinfra/version.py +5 -2
- {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/METADATA +40 -41
- pyinfra-3.0.dist-info/RECORD +167 -0
- {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/WHEEL +1 -1
- pyinfra-3.0.dist-info/entry_points.txt +11 -0
- pyinfra_cli/__main__.py +4 -3
- pyinfra_cli/commands.py +7 -2
- pyinfra_cli/exceptions.py +78 -42
- pyinfra_cli/inventory.py +40 -6
- pyinfra_cli/log.py +17 -3
- pyinfra_cli/main.py +133 -90
- pyinfra_cli/prints.py +95 -127
- pyinfra_cli/util.py +62 -29
- tests/test_api/test_api.py +2 -0
- tests/test_api/test_api_arguments.py +13 -13
- tests/test_api/test_api_deploys.py +28 -29
- tests/test_api/test_api_facts.py +60 -98
- tests/test_api/test_api_operations.py +101 -201
- tests/test_cli/test_cli.py +18 -49
- tests/test_cli/test_cli_deploy.py +11 -37
- tests/test_cli/test_cli_exceptions.py +50 -19
- tests/test_cli/util.py +1 -1
- tests/test_connectors/test_chroot.py +6 -6
- tests/test_connectors/test_docker.py +4 -4
- tests/test_connectors/test_dockerssh.py +38 -50
- tests/test_connectors/test_local.py +11 -12
- tests/test_connectors/test_ssh.py +105 -93
- tests/test_connectors/test_terraform.py +9 -15
- tests/test_connectors/test_util.py +24 -46
- tests/test_connectors/test_vagrant.py +7 -7
- pyinfra/api/operation.pyi +0 -117
- pyinfra/connectors/ansible.py +0 -171
- pyinfra/connectors/mech.py +0 -186
- pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
- pyinfra/connectors/winrm.py +0 -320
- pyinfra/facts/windows.py +0 -366
- pyinfra/facts/windows_files.py +0 -90
- pyinfra/operations/windows.py +0 -59
- pyinfra/operations/windows_files.py +0 -551
- pyinfra-2.9.1.dist-info/RECORD +0 -170
- pyinfra-2.9.1.dist-info/entry_points.txt +0 -14
- tests/test_connectors/test_ansible.py +0 -64
- tests/test_connectors/test_mech.py +0 -126
- tests/test_connectors/test_winrm.py +0 -76
- {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/LICENSE.md +0 -0
- {pyinfra-2.9.1.dist-info → pyinfra-3.0.dist-info}/top_level.txt +0 -0
|
@@ -3,16 +3,10 @@ from unittest import TestCase
|
|
|
3
3
|
from unittest.mock import patch
|
|
4
4
|
|
|
5
5
|
from pyinfra.api.exceptions import InventoryError
|
|
6
|
-
from pyinfra.connectors.terraform import
|
|
6
|
+
from pyinfra.connectors.terraform import TerraformInventoryConnector
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
10
|
-
def test_make_names_data_no_output_key(self):
|
|
11
|
-
with self.assertRaises(InventoryError) as context:
|
|
12
|
-
list(make_names_data())
|
|
13
|
-
|
|
14
|
-
assert context.exception.args[0] == "No Terraform output key!"
|
|
15
|
-
|
|
9
|
+
class TestTerraformConnector(TestCase):
|
|
16
10
|
@patch("pyinfra.connectors.terraform.local.shell")
|
|
17
11
|
def test_make_names_data_no_output(self, fake_shell):
|
|
18
12
|
fake_shell.return_value = json.dumps(
|
|
@@ -24,7 +18,7 @@ class TestVagrantConnector(TestCase):
|
|
|
24
18
|
)
|
|
25
19
|
|
|
26
20
|
with self.assertRaises(InventoryError) as context:
|
|
27
|
-
list(make_names_data("output_key"))
|
|
21
|
+
list(TerraformInventoryConnector.make_names_data("output_key"))
|
|
28
22
|
|
|
29
23
|
assert (
|
|
30
24
|
context.exception.args[0]
|
|
@@ -36,7 +30,7 @@ class TestVagrantConnector(TestCase):
|
|
|
36
30
|
fake_shell.return_value = json.dumps({"output_key": "wrongvalue"})
|
|
37
31
|
|
|
38
32
|
with self.assertRaises(InventoryError) as context:
|
|
39
|
-
list(make_names_data("output_key"))
|
|
33
|
+
list(TerraformInventoryConnector.make_names_data("output_key"))
|
|
40
34
|
|
|
41
35
|
assert (
|
|
42
36
|
context.exception.args[0]
|
|
@@ -48,7 +42,7 @@ class TestVagrantConnector(TestCase):
|
|
|
48
42
|
fake_shell.return_value = json.dumps({"output_key": [None]})
|
|
49
43
|
|
|
50
44
|
with self.assertRaises(InventoryError) as context:
|
|
51
|
-
list(make_names_data("output_key"))
|
|
45
|
+
list(TerraformInventoryConnector.make_names_data("output_key"))
|
|
52
46
|
|
|
53
47
|
assert (
|
|
54
48
|
context.exception.args[0]
|
|
@@ -58,7 +52,7 @@ class TestVagrantConnector(TestCase):
|
|
|
58
52
|
@patch("pyinfra.connectors.terraform.local.shell")
|
|
59
53
|
def test_make_names_data(self, fake_shell):
|
|
60
54
|
fake_shell.return_value = json.dumps({"output_key": ["somehost"]})
|
|
61
|
-
data = list(make_names_data("output_key"))
|
|
55
|
+
data = list(TerraformInventoryConnector.make_names_data("output_key"))
|
|
62
56
|
|
|
63
57
|
assert data == [
|
|
64
58
|
(
|
|
@@ -71,7 +65,7 @@ class TestVagrantConnector(TestCase):
|
|
|
71
65
|
@patch("pyinfra.connectors.terraform.local.shell")
|
|
72
66
|
def test_make_names_data_nested(self, fake_shell):
|
|
73
67
|
fake_shell.return_value = json.dumps({"output_key": {"nested_key": ["somehost"]}})
|
|
74
|
-
data = list(make_names_data("output_key.nested_key"))
|
|
68
|
+
data = list(TerraformInventoryConnector.make_names_data("output_key.nested_key"))
|
|
75
69
|
|
|
76
70
|
assert data == [
|
|
77
71
|
(
|
|
@@ -88,7 +82,7 @@ class TestVagrantConnector(TestCase):
|
|
|
88
82
|
"ssh_hostname": "hostname",
|
|
89
83
|
}
|
|
90
84
|
fake_shell.return_value = json.dumps({"output_key": [host]})
|
|
91
|
-
data = list(make_names_data("output_key"))
|
|
85
|
+
data = list(TerraformInventoryConnector.make_names_data("output_key"))
|
|
92
86
|
|
|
93
87
|
assert data == [
|
|
94
88
|
(
|
|
@@ -106,7 +100,7 @@ class TestVagrantConnector(TestCase):
|
|
|
106
100
|
fake_shell.return_value = json.dumps({"output_key": [host]})
|
|
107
101
|
|
|
108
102
|
with self.assertRaises(InventoryError) as context:
|
|
109
|
-
list(make_names_data("output_key"))
|
|
103
|
+
list(TerraformInventoryConnector.make_names_data("output_key"))
|
|
110
104
|
|
|
111
105
|
assert (
|
|
112
106
|
context.exception.args[0]
|
|
@@ -3,90 +3,68 @@
|
|
|
3
3
|
from unittest import TestCase
|
|
4
4
|
|
|
5
5
|
from pyinfra.api import Config, State
|
|
6
|
-
from pyinfra.connectors.util import
|
|
7
|
-
make_unix_command,
|
|
8
|
-
make_unix_command_for_host,
|
|
9
|
-
split_combined_output,
|
|
10
|
-
)
|
|
6
|
+
from pyinfra.connectors.util import make_unix_command, make_unix_command_for_host
|
|
11
7
|
|
|
12
8
|
from ..util import make_inventory
|
|
13
9
|
|
|
14
10
|
|
|
15
|
-
class TestConnectorUtil(TestCase):
|
|
16
|
-
def test_split_combined_output_works(self):
|
|
17
|
-
results = split_combined_output(
|
|
18
|
-
[
|
|
19
|
-
("stdout", "stdout1"),
|
|
20
|
-
("stdout", "stdout2"),
|
|
21
|
-
("stderr", "stderr1"),
|
|
22
|
-
("stdout", "stdout3"),
|
|
23
|
-
],
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
assert results == (["stdout1", "stdout2", "stdout3"], ["stderr1"])
|
|
27
|
-
|
|
28
|
-
def test_split_combined_output_raises(self):
|
|
29
|
-
with self.assertRaises(ValueError):
|
|
30
|
-
split_combined_output(["nope", ""])
|
|
31
|
-
|
|
32
|
-
|
|
33
11
|
class TestMakeUnixCommandConnectorUtil(TestCase):
|
|
34
12
|
def test_command(self):
|
|
35
13
|
command = make_unix_command("echo Šablony")
|
|
36
14
|
assert command.get_raw_value() == "sh -c 'echo Šablony'"
|
|
37
15
|
|
|
38
16
|
def test_doas_command(self):
|
|
39
|
-
command = make_unix_command("uptime",
|
|
17
|
+
command = make_unix_command("uptime", _doas=True)
|
|
40
18
|
assert command.get_raw_value() == "doas -n sh -c uptime"
|
|
41
19
|
|
|
42
20
|
def test_doas_user_command(self):
|
|
43
|
-
command = make_unix_command("uptime",
|
|
21
|
+
command = make_unix_command("uptime", _doas=True, _doas_user="pyinfra")
|
|
44
22
|
assert command.get_raw_value() == "doas -n -u pyinfra sh -c uptime"
|
|
45
23
|
|
|
46
24
|
def test_sudo_command(self):
|
|
47
|
-
command = make_unix_command("uptime",
|
|
25
|
+
command = make_unix_command("uptime", _sudo=True)
|
|
48
26
|
assert command.get_raw_value() == "sudo -H -n sh -c uptime"
|
|
49
27
|
|
|
50
28
|
def test_sudo_multi_arg_command(self):
|
|
51
|
-
command = make_unix_command("echo hi",
|
|
29
|
+
command = make_unix_command("echo hi", _sudo=True, _preserve_sudo_env=True)
|
|
52
30
|
assert command.get_raw_value() == "sudo -H -n -E sh -c 'echo hi'"
|
|
53
31
|
|
|
54
32
|
def test_sudo_preserve_env_command(self):
|
|
55
|
-
command = make_unix_command("uptime",
|
|
33
|
+
command = make_unix_command("uptime", _sudo=True, _preserve_sudo_env=True)
|
|
56
34
|
assert command.get_raw_value() == "sudo -H -n -E sh -c uptime"
|
|
57
35
|
|
|
58
36
|
def test_use_sudo_login_command(self):
|
|
59
|
-
command = make_unix_command("uptime",
|
|
37
|
+
command = make_unix_command("uptime", _sudo=True, _use_sudo_login=True)
|
|
60
38
|
assert command.get_raw_value() == "sudo -H -n -i sh -c uptime"
|
|
61
39
|
|
|
62
40
|
def test_sudo_user_command(self):
|
|
63
|
-
command = make_unix_command("uptime",
|
|
41
|
+
command = make_unix_command("uptime", _sudo=True, _sudo_user="pyinfra")
|
|
64
42
|
assert command.get_raw_value() == "sudo -H -n -u pyinfra sh -c uptime"
|
|
65
43
|
|
|
66
44
|
def test_su_command(self):
|
|
67
|
-
command = make_unix_command("uptime",
|
|
45
|
+
command = make_unix_command("uptime", _su_user="pyinfra")
|
|
68
46
|
assert command.get_raw_value() == "su pyinfra -c 'sh -c uptime'"
|
|
69
47
|
|
|
70
48
|
def test_su_multi_arg_command(self):
|
|
71
|
-
command = make_unix_command("echo hi",
|
|
49
|
+
command = make_unix_command("echo hi", _su_user="pyinfra")
|
|
72
50
|
assert command.get_raw_value() == "su pyinfra -c 'sh -c '\"'\"'echo hi'\"'\"''"
|
|
73
51
|
|
|
74
52
|
def test_use_su_login_command(self):
|
|
75
|
-
command = make_unix_command("uptime",
|
|
53
|
+
command = make_unix_command("uptime", _su_user="pyinfra", _use_su_login=True)
|
|
76
54
|
assert command.get_raw_value() == "su -l pyinfra -c 'sh -c uptime'"
|
|
77
55
|
|
|
78
56
|
def test_preserve_su_env_command(self):
|
|
79
|
-
command = make_unix_command("uptime",
|
|
57
|
+
command = make_unix_command("uptime", _su_user="pyinfra", _preserve_su_env=True)
|
|
80
58
|
assert command.get_raw_value() == "su -m pyinfra -c 'sh -c uptime'"
|
|
81
59
|
|
|
82
60
|
def test_su_shell_command(self):
|
|
83
|
-
command = make_unix_command("uptime",
|
|
61
|
+
command = make_unix_command("uptime", _su_user="pyinfra", _su_shell="bash")
|
|
84
62
|
assert command.get_raw_value() == "su -s `which bash` pyinfra -c 'sh -c uptime'"
|
|
85
63
|
|
|
86
64
|
def test_command_env(self):
|
|
87
65
|
command = make_unix_command(
|
|
88
66
|
"uptime",
|
|
89
|
-
|
|
67
|
+
_env={
|
|
90
68
|
"key": "value",
|
|
91
69
|
"anotherkey": "anothervalue",
|
|
92
70
|
},
|
|
@@ -97,23 +75,23 @@ class TestMakeUnixCommandConnectorUtil(TestCase):
|
|
|
97
75
|
]
|
|
98
76
|
|
|
99
77
|
def test_command_chdir(self):
|
|
100
|
-
command = make_unix_command("uptime",
|
|
78
|
+
command = make_unix_command("uptime", _chdir="/opt/somedir")
|
|
101
79
|
assert command.get_raw_value() == "sh -c 'cd /opt/somedir && uptime'"
|
|
102
80
|
|
|
103
81
|
def test_custom_shell_command(self):
|
|
104
|
-
command = make_unix_command("uptime",
|
|
82
|
+
command = make_unix_command("uptime", _shell_executable="bash")
|
|
105
83
|
assert command.get_raw_value() == "bash -c uptime"
|
|
106
84
|
|
|
107
85
|
def test_mixed_command(self):
|
|
108
86
|
command = make_unix_command(
|
|
109
87
|
"echo hi",
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
88
|
+
_chdir="/opt/somedir",
|
|
89
|
+
_env={"key": "value"},
|
|
90
|
+
_sudo=True,
|
|
91
|
+
_sudo_user="root",
|
|
92
|
+
_preserve_sudo_env=True,
|
|
93
|
+
_su_user="pyinfra",
|
|
94
|
+
_shell_executable="bash",
|
|
117
95
|
)
|
|
118
96
|
assert command.get_raw_value() == (
|
|
119
97
|
"sudo -H -n -E -u root " # sudo bit
|
|
@@ -125,7 +103,7 @@ class TestMakeUnixCommandConnectorUtil(TestCase):
|
|
|
125
103
|
def test_command_exists_su_config_only(self):
|
|
126
104
|
"""
|
|
127
105
|
This tests covers a bug that appeared when `make_unix_command` is called
|
|
128
|
-
with `
|
|
106
|
+
with `_su_user=False` (default) but `SU_USER` set on the config object,
|
|
129
107
|
resulting in an empty command output.
|
|
130
108
|
"""
|
|
131
109
|
state = State(make_inventory(), Config(SU_USER=True))
|
|
@@ -3,7 +3,7 @@ from unittest import TestCase
|
|
|
3
3
|
from unittest.mock import mock_open, patch
|
|
4
4
|
|
|
5
5
|
from pyinfra.api.exceptions import InventoryError
|
|
6
|
-
from pyinfra.connectors.vagrant import
|
|
6
|
+
from pyinfra.connectors.vagrant import VagrantInventoryConnector, get_vagrant_options
|
|
7
7
|
|
|
8
8
|
FAKE_VAGRANT_OPTIONS = {
|
|
9
9
|
"groups": {
|
|
@@ -69,13 +69,13 @@ class TestVagrantConnector(TestCase):
|
|
|
69
69
|
)
|
|
70
70
|
@patch("pyinfra.connectors.vagrant.path.exists", lambda path: True)
|
|
71
71
|
def test_make_names_data_with_options(self):
|
|
72
|
-
data = make_names_data()
|
|
72
|
+
data = list(VagrantInventoryConnector.make_names_data())
|
|
73
73
|
|
|
74
74
|
assert data == [
|
|
75
75
|
(
|
|
76
76
|
"@vagrant/ubuntu16",
|
|
77
77
|
{
|
|
78
|
-
"ssh_port":
|
|
78
|
+
"ssh_port": 2222,
|
|
79
79
|
"ssh_user": "vagrant",
|
|
80
80
|
"ssh_hostname": "127.0.0.1",
|
|
81
81
|
"ssh_key": "path/to/key",
|
|
@@ -85,7 +85,7 @@ class TestVagrantConnector(TestCase):
|
|
|
85
85
|
(
|
|
86
86
|
"@vagrant/centos7",
|
|
87
87
|
{
|
|
88
|
-
"ssh_port":
|
|
88
|
+
"ssh_port": 2200,
|
|
89
89
|
"ssh_user": "vagrant",
|
|
90
90
|
"ssh_hostname": "127.0.0.1",
|
|
91
91
|
"ssh_key": "path/to/key",
|
|
@@ -103,13 +103,13 @@ class TestVagrantConnector(TestCase):
|
|
|
103
103
|
]
|
|
104
104
|
|
|
105
105
|
def test_make_names_data_with_limit(self):
|
|
106
|
-
data = make_names_data(
|
|
106
|
+
data = list(VagrantInventoryConnector.make_names_data(name=("ubuntu16",)))
|
|
107
107
|
|
|
108
108
|
assert data == [
|
|
109
109
|
(
|
|
110
110
|
"@vagrant/ubuntu16",
|
|
111
111
|
{
|
|
112
|
-
"ssh_port":
|
|
112
|
+
"ssh_port": 2222,
|
|
113
113
|
"ssh_user": "vagrant",
|
|
114
114
|
"ssh_hostname": "127.0.0.1",
|
|
115
115
|
"ssh_key": "path/to/key",
|
|
@@ -120,4 +120,4 @@ class TestVagrantConnector(TestCase):
|
|
|
120
120
|
|
|
121
121
|
def test_make_names_data_no_matches(self):
|
|
122
122
|
with self.assertRaises(InventoryError):
|
|
123
|
-
make_names_data(
|
|
123
|
+
list(VagrantInventoryConnector.make_names_data(name="nope"))
|
pyinfra/api/operation.pyi
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
from typing import (
|
|
2
|
-
Callable,
|
|
3
|
-
Dict,
|
|
4
|
-
Generator,
|
|
5
|
-
Generic,
|
|
6
|
-
Iterable,
|
|
7
|
-
List,
|
|
8
|
-
Mapping,
|
|
9
|
-
Protocol,
|
|
10
|
-
Tuple,
|
|
11
|
-
overload,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
from typing_extensions import ParamSpec
|
|
15
|
-
|
|
16
|
-
from pyinfra.api.command import (
|
|
17
|
-
FileDownloadCommand,
|
|
18
|
-
FileUploadCommand,
|
|
19
|
-
FunctionCommand,
|
|
20
|
-
StringCommand,
|
|
21
|
-
)
|
|
22
|
-
from pyinfra.api.host import Host
|
|
23
|
-
from pyinfra.api.state import State
|
|
24
|
-
|
|
25
|
-
P = ParamSpec("P")
|
|
26
|
-
|
|
27
|
-
Command = str | StringCommand | FileDownloadCommand | FileUploadCommand | FunctionCommand
|
|
28
|
-
|
|
29
|
-
class OperationMeta:
|
|
30
|
-
changed: bool
|
|
31
|
-
commands: List[str] | None
|
|
32
|
-
hash: str | None
|
|
33
|
-
|
|
34
|
-
stdout_lines: List[str]
|
|
35
|
-
stdout: str
|
|
36
|
-
stderr_lines: List[str]
|
|
37
|
-
stderr: str
|
|
38
|
-
|
|
39
|
-
class Operation(Generic[P], Protocol):
|
|
40
|
-
def __call__(
|
|
41
|
-
self,
|
|
42
|
-
_sudo: bool | None = None,
|
|
43
|
-
_sudo_user: str | None = None,
|
|
44
|
-
_use_sudo_login: bool | None = None,
|
|
45
|
-
_use_sudo_password: bool | None = None,
|
|
46
|
-
_preserve_sudo_env: bool | None = None,
|
|
47
|
-
_su_user: str | None = None,
|
|
48
|
-
_use_su_login: bool | None = None,
|
|
49
|
-
_preserve_su_env: bool | None = None,
|
|
50
|
-
_su_shell: str | None = None,
|
|
51
|
-
_doas: bool | None = None,
|
|
52
|
-
_doas_user: str | None = None,
|
|
53
|
-
_shell_executable: str | None = None,
|
|
54
|
-
_chdir: str | None = None,
|
|
55
|
-
_env: Mapping[str, str] | None = None,
|
|
56
|
-
_success_exit_codes: Iterable[int] | None = None,
|
|
57
|
-
_timeout: int | None = None,
|
|
58
|
-
_get_pty: bool | None = None,
|
|
59
|
-
_stdin: str | List[str] | Tuple[str, ...] | None = None,
|
|
60
|
-
name: str | None = None,
|
|
61
|
-
_ignore_errors: bool | None = None,
|
|
62
|
-
_continue_on_error: bool | None = None,
|
|
63
|
-
_precondition: str | None = None,
|
|
64
|
-
_postcondition: str | None = None,
|
|
65
|
-
_on_success: Callable[[State, Host, str], None] | None = None,
|
|
66
|
-
_on_error: Callable[[State, Host, str], None] | None = None,
|
|
67
|
-
_parallel: int | None = None,
|
|
68
|
-
_run_once: bool | None = None,
|
|
69
|
-
_serial: bool | None = None,
|
|
70
|
-
*args: P.args,
|
|
71
|
-
**kwargs: P.kwargs,
|
|
72
|
-
) -> OperationMeta: ...
|
|
73
|
-
|
|
74
|
-
def add_op(
|
|
75
|
-
state: State,
|
|
76
|
-
op_func: Operation[P],
|
|
77
|
-
_sudo: bool | None = None,
|
|
78
|
-
_sudo_user: str | None = None,
|
|
79
|
-
_use_sudo_login: bool | None = None,
|
|
80
|
-
_use_sudo_password: bool | None = None,
|
|
81
|
-
_preserve_sudo_env: bool | None = None,
|
|
82
|
-
_su_user: str | None = None,
|
|
83
|
-
_use_su_login: bool | None = None,
|
|
84
|
-
_preserve_su_env: bool | None = None,
|
|
85
|
-
_su_shell: str | None = None,
|
|
86
|
-
_doas: bool | None = None,
|
|
87
|
-
_doas_user: str | None = None,
|
|
88
|
-
_shell_executable: str | None = None,
|
|
89
|
-
_chdir: str | None = None,
|
|
90
|
-
_env: Mapping[str, str] | None = None,
|
|
91
|
-
_success_exit_codes: Iterable[int] | None = None,
|
|
92
|
-
_timeout: int | None = None,
|
|
93
|
-
_get_pty: bool | None = None,
|
|
94
|
-
_stdin: str | List[str] | Tuple[str, ...] | None = None,
|
|
95
|
-
name: str | None = None,
|
|
96
|
-
_ignore_errors: bool | None = None,
|
|
97
|
-
_continue_on_error: bool | None = None,
|
|
98
|
-
_precondition: str | None = None,
|
|
99
|
-
_postcondition: str | None = None,
|
|
100
|
-
_on_success: Callable[[State, Host, str], None] | None = None,
|
|
101
|
-
_on_error: Callable[[State, Host, str], None] | None = None,
|
|
102
|
-
_parallel: int | None = None,
|
|
103
|
-
_run_once: bool | None = None,
|
|
104
|
-
_serial: bool | None = None,
|
|
105
|
-
host: Iterable[Host] | Host | None = None,
|
|
106
|
-
*args: P.args,
|
|
107
|
-
**kwargs: P.kwargs,
|
|
108
|
-
) -> Dict[Host, OperationMeta]: ...
|
|
109
|
-
@overload
|
|
110
|
-
def operation(func: Callable[P, Generator[Command, None, None]]) -> Operation[P]: ...
|
|
111
|
-
@overload
|
|
112
|
-
def operation(
|
|
113
|
-
pipeline_facts=None,
|
|
114
|
-
is_idempotent: bool = True,
|
|
115
|
-
idempotent_notice=None,
|
|
116
|
-
frame_offset=1,
|
|
117
|
-
) -> Callable[[Callable[P, Generator[Command, None, None]]], Operation[P]]: ...
|
pyinfra/connectors/ansible.py
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
**Note**: this connector is a work in progress! While it parses the list of
|
|
3
|
-
hosts OK, it doesn't handle nested groups properly yet.
|
|
4
|
-
|
|
5
|
-
The `@ansible` connector can be used to parse Ansible inventory files.
|
|
6
|
-
|
|
7
|
-
.. code:: python
|
|
8
|
-
|
|
9
|
-
# Load an Ansible inventory relative to the current directory
|
|
10
|
-
pyinfra @ansible/path/to/inventory
|
|
11
|
-
|
|
12
|
-
# Load using an absolute path
|
|
13
|
-
pyinfra @ansible//absolute/path/to/inventory
|
|
14
|
-
"""
|
|
15
|
-
import json
|
|
16
|
-
import re
|
|
17
|
-
from collections import defaultdict
|
|
18
|
-
from configparser import ConfigParser
|
|
19
|
-
from os import path
|
|
20
|
-
from typing import TYPE_CHECKING, Optional
|
|
21
|
-
|
|
22
|
-
from pyinfra import logger
|
|
23
|
-
from pyinfra.api.exceptions import InventoryError
|
|
24
|
-
from pyinfra.api.util import memoize
|
|
25
|
-
|
|
26
|
-
if TYPE_CHECKING:
|
|
27
|
-
from pyinfra.api.host import Host
|
|
28
|
-
|
|
29
|
-
try:
|
|
30
|
-
import yaml
|
|
31
|
-
except ImportError:
|
|
32
|
-
yaml = None # type: ignore
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@memoize
|
|
36
|
-
def show_warning():
|
|
37
|
-
logger.warning("The @ansible connector is in alpha!")
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def make_names_data(inventory_filename: Optional[str] = None):
|
|
41
|
-
show_warning()
|
|
42
|
-
|
|
43
|
-
if not inventory_filename:
|
|
44
|
-
raise InventoryError("No Ansible inventory filename provided!")
|
|
45
|
-
|
|
46
|
-
if not path.exists(inventory_filename):
|
|
47
|
-
raise InventoryError(
|
|
48
|
-
("Could not find Ansible inventory file: {0}").format(inventory_filename),
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
return parse_inventory(inventory_filename)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def parse_inventory(inventory_filename: str):
|
|
55
|
-
# fallback to INI if no extension
|
|
56
|
-
extension = inventory_filename.split(".")[-1] if "." in inventory_filename else "ini"
|
|
57
|
-
|
|
58
|
-
# host:set(groups) mapping
|
|
59
|
-
host_to_groups = {}
|
|
60
|
-
|
|
61
|
-
if extension in ["ini"]:
|
|
62
|
-
host_to_groups = parse_inventory_ini(inventory_filename)
|
|
63
|
-
elif extension in ["json"]:
|
|
64
|
-
with open(inventory_filename, encoding="utf-8") as inventory_file:
|
|
65
|
-
inventory_tree = json.load(inventory_file)
|
|
66
|
-
# close file early
|
|
67
|
-
host_to_groups = parse_inventory_tree(inventory_tree)
|
|
68
|
-
elif extension in ["yaml", "yml"]:
|
|
69
|
-
if yaml is None:
|
|
70
|
-
raise Exception(
|
|
71
|
-
(
|
|
72
|
-
"To parse YAML Ansible inventories requires `pyyaml`. "
|
|
73
|
-
"Install it with `pip install pyyaml`."
|
|
74
|
-
),
|
|
75
|
-
)
|
|
76
|
-
with open(inventory_filename, encoding="utf-8") as inventory_file:
|
|
77
|
-
inventory_tree = yaml.safe_load(inventory_file)
|
|
78
|
-
# close file early
|
|
79
|
-
host_to_groups = parse_inventory_tree(inventory_tree)
|
|
80
|
-
else:
|
|
81
|
-
raise InventoryError(("Ansible inventory file format not supported: {0}").format(extension))
|
|
82
|
-
|
|
83
|
-
return [(host, {}, sorted(list(host_to_groups.get(host, [])))) for host in host_to_groups]
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def parse_inventory_ini(inventory_filename: str):
|
|
87
|
-
config = ConfigParser(
|
|
88
|
-
delimiters=(" "), # we only handle the hostnames for now
|
|
89
|
-
allow_no_value=True, # we don't by default have = values
|
|
90
|
-
interpolation=None, # remove any Python interpolation
|
|
91
|
-
)
|
|
92
|
-
config.read(inventory_filename)
|
|
93
|
-
|
|
94
|
-
host_to_groups = defaultdict(set)
|
|
95
|
-
group_to_hosts = defaultdict(set)
|
|
96
|
-
hosts = []
|
|
97
|
-
|
|
98
|
-
# First pass - load hosts/groups of hosts
|
|
99
|
-
for section in config.sections():
|
|
100
|
-
if ":" in section: # ignore :children and :vars sections this time
|
|
101
|
-
continue
|
|
102
|
-
|
|
103
|
-
options = config.options(section)
|
|
104
|
-
for host in _parse_ansible_hosts(options):
|
|
105
|
-
hosts.append(host)
|
|
106
|
-
host_to_groups[host].add(section)
|
|
107
|
-
group_to_hosts[section].add(host)
|
|
108
|
-
|
|
109
|
-
# Second pass - load any children groups
|
|
110
|
-
for section in config.sections():
|
|
111
|
-
if not section.endswith(":children"): # we only support :children for now
|
|
112
|
-
continue
|
|
113
|
-
|
|
114
|
-
group_name = section.replace(":children", "")
|
|
115
|
-
|
|
116
|
-
options = config.options(section)
|
|
117
|
-
for sub_group_name in options:
|
|
118
|
-
sub_group_hosts = group_to_hosts[sub_group_name]
|
|
119
|
-
for host in sub_group_hosts:
|
|
120
|
-
host_to_groups[host].add(group_name)
|
|
121
|
-
|
|
122
|
-
return host_to_groups
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def _parse_ansible_hosts(hosts):
|
|
126
|
-
for host in hosts:
|
|
127
|
-
expand_match = re.search(r"\[[0-9:]+\]", host)
|
|
128
|
-
if expand_match:
|
|
129
|
-
expand_string = host[expand_match.start() : expand_match.end()]
|
|
130
|
-
bits = expand_string[1:-1].split(":") # remove the [] either side
|
|
131
|
-
|
|
132
|
-
zfill = 0
|
|
133
|
-
if bits[0].startswith("0"):
|
|
134
|
-
zfill = len(bits[0])
|
|
135
|
-
|
|
136
|
-
start, end = int(bits[0]), int(bits[1])
|
|
137
|
-
step = int(bits[2]) if len(bits) > 2 else 1
|
|
138
|
-
|
|
139
|
-
for n in range(start, end + 1, step):
|
|
140
|
-
number_as_string = "{0}".format(n)
|
|
141
|
-
if zfill:
|
|
142
|
-
number_as_string = number_as_string.zfill(zfill)
|
|
143
|
-
|
|
144
|
-
hostname = host.replace(expand_string, number_as_string)
|
|
145
|
-
yield hostname
|
|
146
|
-
else:
|
|
147
|
-
yield host
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def parse_inventory_tree(inventory_tree, host_to_groups=dict(), group_stack=set()):
|
|
151
|
-
for group in inventory_tree:
|
|
152
|
-
# set logic adds tolerance for duplicate group names
|
|
153
|
-
groups = group_stack.union({group})
|
|
154
|
-
|
|
155
|
-
if "hosts" in inventory_tree[group]:
|
|
156
|
-
for host in inventory_tree[group]["hosts"]:
|
|
157
|
-
append_groups_to_host(host, groups, host_to_groups)
|
|
158
|
-
|
|
159
|
-
if "children" in inventory_tree[group]:
|
|
160
|
-
# recursively parse inventory tree
|
|
161
|
-
parse_inventory_tree(inventory_tree[group]["children"], host_to_groups, groups)
|
|
162
|
-
|
|
163
|
-
return host_to_groups
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def append_groups_to_host(host: "Host", groups, host_to_groups):
|
|
167
|
-
if host in host_to_groups:
|
|
168
|
-
# set logic handles de-duplication
|
|
169
|
-
host_to_groups[host] = host_to_groups[host].union(groups)
|
|
170
|
-
else:
|
|
171
|
-
host_to_groups[host] = groups
|