pyinfra 3.1.1__py2.py3-none-any.whl → 3.2__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyinfra/api/arguments.py +9 -2
- pyinfra/api/deploy.py +4 -2
- pyinfra/api/host.py +5 -3
- pyinfra/connectors/docker.py +17 -6
- pyinfra/connectors/sshuserclient/client.py +26 -14
- pyinfra/facts/apk.py +3 -1
- pyinfra/facts/apt.py +60 -0
- pyinfra/facts/crontab.py +190 -0
- pyinfra/facts/docker.py +6 -0
- pyinfra/facts/efibootmgr.py +108 -0
- pyinfra/facts/files.py +93 -6
- pyinfra/facts/git.py +3 -2
- pyinfra/facts/mysql.py +1 -2
- pyinfra/facts/opkg.py +233 -0
- pyinfra/facts/pipx.py +74 -0
- pyinfra/facts/podman.py +47 -0
- pyinfra/facts/postgres.py +2 -0
- pyinfra/facts/server.py +39 -77
- pyinfra/facts/util/units.py +30 -0
- pyinfra/facts/zfs.py +22 -19
- pyinfra/local.py +3 -2
- pyinfra/operations/apt.py +27 -20
- pyinfra/operations/crontab.py +189 -0
- pyinfra/operations/docker.py +13 -12
- pyinfra/operations/files.py +18 -0
- pyinfra/operations/git.py +23 -7
- pyinfra/operations/opkg.py +88 -0
- pyinfra/operations/pip.py +3 -2
- pyinfra/operations/pipx.py +90 -0
- pyinfra/operations/postgres.py +15 -11
- pyinfra/operations/runit.py +2 -0
- pyinfra/operations/server.py +3 -177
- pyinfra/operations/zfs.py +3 -3
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/METADATA +11 -12
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/RECORD +45 -36
- pyinfra_cli/inventory.py +26 -9
- pyinfra_cli/prints.py +18 -3
- pyinfra_cli/util.py +3 -0
- tests/test_cli/test_cli_deploy.py +15 -13
- tests/test_cli/test_cli_inventory.py +53 -0
- tests/test_connectors/test_sshuserclient.py +68 -1
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/LICENSE.md +0 -0
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/WHEEL +0 -0
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/entry_points.txt +0 -0
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/top_level.txt +0 -0
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
pyinfra/__init__.py,sha256=7ZcKHGWk7_nYxsYrbFBB_vJr-J-Ddbc56ZS4sk5ArVw,535
|
|
2
2
|
pyinfra/__main__.py,sha256=aVd00glLz5CMJGXgt1XxbOvC2HluqaowoTOjxgIpBaA,47
|
|
3
3
|
pyinfra/context.py,sha256=S6DvGjjTEjM4u2m9oIAmAaV7kXIJzVwYf725P1muIuY,3395
|
|
4
|
-
pyinfra/local.py,sha256=
|
|
4
|
+
pyinfra/local.py,sha256=wT84xkJc9UBN5isvIVbNpm2fzZaadwE-dkbAwaFQdZk,2808
|
|
5
5
|
pyinfra/progress.py,sha256=X3hXZ4Flh_L9FE4ZEWxWoG0R4dA5UPd1FCO-Exd5Xtc,4193
|
|
6
6
|
pyinfra/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
pyinfra/version.py,sha256=LZf50PHDzEZv65w0G-iMICoQ9US0U5LWHAOEmNtkF3I,216
|
|
8
8
|
pyinfra/api/__init__.py,sha256=suGbKKM-qCduuXFYBEcyswlTqozewtYpdLRhK63PVn0,942
|
|
9
|
-
pyinfra/api/arguments.py,sha256=
|
|
9
|
+
pyinfra/api/arguments.py,sha256=GdkwOpBOUKCDer6gxXtChKmRD8iDr_1pQMXXv7jzfDw,10266
|
|
10
10
|
pyinfra/api/arguments_typed.py,sha256=WQKr0wDtlgJGq-Vkv_oPAz7f-LxjqQv3wJCdvVrePWk,2331
|
|
11
11
|
pyinfra/api/command.py,sha256=SyUlxvhYlXpgFpg0jua8bzQ2KPtVYQXHcvD6AUL2SCI,7226
|
|
12
12
|
pyinfra/api/config.py,sha256=wS6Pi4T4DxNkzO4llNY-ghLxyI5VBJ26uGvgMPZxIKY,9043
|
|
13
13
|
pyinfra/api/connect.py,sha256=Z9wusMLR_jBkKKk5D4AUOj8LHl3H5MsNO5FxAeR4jac,1416
|
|
14
14
|
pyinfra/api/connectors.py,sha256=nie7JuLxMSC6gqPjmjuCisQ11R-eAQDtMMWF6YbSQ48,659
|
|
15
|
-
pyinfra/api/deploy.py,sha256=
|
|
15
|
+
pyinfra/api/deploy.py,sha256=CllKp1QYLseeMStj35waZazB0hUuy3iGvDt7b_lH31M,3138
|
|
16
16
|
pyinfra/api/exceptions.py,sha256=cCbUp1qN1QO0d9aAvOAbRgYpLi0vUI5j7ZqSjcD1_P8,1861
|
|
17
17
|
pyinfra/api/facts.py,sha256=aMPtkB7vypyXRQDThjwJZzAnEgqjP0wrwyEhRHQf4Js,9449
|
|
18
|
-
pyinfra/api/host.py,sha256=
|
|
18
|
+
pyinfra/api/host.py,sha256=bsYWn_kNQdWsk6y5ExCJ4FYVq3KlgfHSPnciC0MsrqE,13853
|
|
19
19
|
pyinfra/api/inventory.py,sha256=nPITdNEJ7q71adIqS_OKHsMjD7amUuHEuTl6xzgh1Gk,7734
|
|
20
20
|
pyinfra/api/operation.py,sha256=Dp7pH9H3EYs7U1ZvquYUbOtWJPO9iIAa4H7GwXdxFxs,15170
|
|
21
21
|
pyinfra/api/operations.py,sha256=jvz9ISfwmQnAQVUKLnbrRdD9QHIAAfypo9l5b3fYG1w,10894
|
|
@@ -24,7 +24,7 @@ pyinfra/api/util.py,sha256=K4aFjGW7KAz2ZQqfRriRqyHMCQFFrX06WPola3epjaE,12410
|
|
|
24
24
|
pyinfra/connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
pyinfra/connectors/base.py,sha256=2fASiV-MvpXgcZAFLM_PUwYx5ax6EHai44ri_oEKeSE,3732
|
|
26
26
|
pyinfra/connectors/chroot.py,sha256=X5U3vi-zPm4DV-lHTwoHU8iEl8dppDiYJoWP_1mJVFY,5919
|
|
27
|
-
pyinfra/connectors/docker.py,sha256=
|
|
27
|
+
pyinfra/connectors/docker.py,sha256=GwWTx8Mx1AnKl0vUNSvbyoHTjQNPS0njR-tA4lNViAU,9196
|
|
28
28
|
pyinfra/connectors/dockerssh.py,sha256=VWHY--jqs3yf-RuPUZXav4vLeON9SzoVC9CUyOJo1rg,8919
|
|
29
29
|
pyinfra/connectors/local.py,sha256=eFDrBalS1yPCIUdQzh8h2HF3VP2nDn0uVFKOeeQZfiw,6888
|
|
30
30
|
pyinfra/connectors/ssh.py,sha256=Pr7W_aiGstkWQqrwm-cNENzhzTWTBLWM-XAAbF9uOEQ,21212
|
|
@@ -33,40 +33,45 @@ pyinfra/connectors/terraform.py,sha256=Tu59cbemll5CfqlIaQtOrLa0HKzl23c64ih0DZXJu
|
|
|
33
33
|
pyinfra/connectors/util.py,sha256=0bvoMsGMD-Tbfaer8NUhWJjBnaNKdmE83PDg48BYjcU,11374
|
|
34
34
|
pyinfra/connectors/vagrant.py,sha256=oEeRglzRmemRXW3vilsp_Xg9qnZMRprRJO9fd_C-f5M,4759
|
|
35
35
|
pyinfra/connectors/sshuserclient/__init__.py,sha256=Qc4RO2wknSWIiNTwOeQ0y2TeiuKHmyWDW2Dz4MOo9CE,44
|
|
36
|
-
pyinfra/connectors/sshuserclient/client.py,sha256=
|
|
36
|
+
pyinfra/connectors/sshuserclient/client.py,sha256=6tN-dJYxho0nmyfXRLeaEs6EtdPdDNNACROOuZSoUos,10037
|
|
37
37
|
pyinfra/connectors/sshuserclient/config.py,sha256=UMwkvTgAIS7__re6Wz_pwH6EU4kO1-uMQ5zuFakH0v4,2721
|
|
38
38
|
pyinfra/facts/__init__.py,sha256=myTXSOZmAqmU88Fyifn035h9Lr6Gj2mlka_jDcXyKGw,347
|
|
39
|
-
pyinfra/facts/apk.py,sha256=
|
|
40
|
-
pyinfra/facts/apt.py,sha256=
|
|
39
|
+
pyinfra/facts/apk.py,sha256=83kr2SpqFGXuH5Z0T5dF2zbn9rM3Eh7RzAQvOwMWuC8,717
|
|
40
|
+
pyinfra/facts/apt.py,sha256=Ou5FazOmJFzNtfveH-4vuZH8LdHlUBR7UGRgUX_7oEY,3904
|
|
41
41
|
pyinfra/facts/brew.py,sha256=qDz89ZlZOiCZv0GLOXOjgFtqq66SLaYXgCncYP2LwDs,2584
|
|
42
42
|
pyinfra/facts/bsdinit.py,sha256=hyESeGu0hPf8HY1D0bIFFFNFpXRdZB2R52aflVQPf9o,577
|
|
43
43
|
pyinfra/facts/cargo.py,sha256=OQF6nOulp2TIaFK1fiAEevsXnL5OMQUL6LkFHidb1yo,605
|
|
44
44
|
pyinfra/facts/choco.py,sha256=A0VCXnI5H9RocgO1IvaNWRIxnXiIZYEzIDG1F-ydJi4,790
|
|
45
|
+
pyinfra/facts/crontab.py,sha256=dTiAZPXV6DCi2BeEl33PP5k8c9ODkzgIDD6cPC5l2pA,5674
|
|
45
46
|
pyinfra/facts/deb.py,sha256=XGyxnow9wjpE8ZKTZDa1_SNChMyMcNgFeTG1ka5uky4,1922
|
|
46
47
|
pyinfra/facts/dnf.py,sha256=9rTBgLHewbk8XCJuikzAYCumfFAzbmmHMchlaXBhdWw,977
|
|
47
|
-
pyinfra/facts/docker.py,sha256=
|
|
48
|
-
pyinfra/facts/
|
|
48
|
+
pyinfra/facts/docker.py,sha256=tA-H2MP54iBkV5zgpCaTmPEZH6P-n59E5lRXplJ1iBk,2474
|
|
49
|
+
pyinfra/facts/efibootmgr.py,sha256=L8hIYLxRTNAgIdf7djZ3z-TylbnMnbagl7OuJQXHKWE,3473
|
|
50
|
+
pyinfra/facts/files.py,sha256=E4eS15wpNgCmlpNFWRYJuuy6DcPCzhzULwp1sJ-iyqg,15077
|
|
49
51
|
pyinfra/facts/flatpak.py,sha256=lnuFZYGPtPDe35YXTVgn5M0rhgcA5ys3fMI6EmvCnm4,1536
|
|
50
52
|
pyinfra/facts/gem.py,sha256=ktC2hofSwYX0fVcdWleU74ddjW1RPZvKMW-3wYp9lJE,572
|
|
51
|
-
pyinfra/facts/git.py,sha256
|
|
53
|
+
pyinfra/facts/git.py,sha256=-UhY10Jx8MO537Dstm_jsn42Ezx5SsUZT315Qx7b4C0,1347
|
|
52
54
|
pyinfra/facts/gpg.py,sha256=wYKoQl4aHXB1UqqbWCdVhUoa6N6Liz01AmH8fPjxR48,3813
|
|
53
55
|
pyinfra/facts/hardware.py,sha256=9UOgvzlJ7jx7hPfmkhldYEymKEiKMR3ZVFGfZXl_oe0,12023
|
|
54
56
|
pyinfra/facts/iptables.py,sha256=sUkywfHZUXnMZF_KshAnyJufrJvZ9fBYnERSRbwOCRE,3374
|
|
55
57
|
pyinfra/facts/launchd.py,sha256=D0RSpBQXt4a4zJOxwxIb5RYY34sAx6ZrWj8NxwYqhIc,767
|
|
56
58
|
pyinfra/facts/lxd.py,sha256=NeIMK9VsDXlRf7q3j17oYbdigL7XjLnhmqv63t6CrzQ,466
|
|
57
|
-
pyinfra/facts/mysql.py,sha256=
|
|
59
|
+
pyinfra/facts/mysql.py,sha256=w2XphDMeEFBrvWp5MK_DC0_7GoI1RYlheovjhuIiTBw,6123
|
|
58
60
|
pyinfra/facts/npm.py,sha256=rI3at2M6Rfwb4cPj5gTivMTss5POs5_W2Ilcrgv8XAA,756
|
|
59
61
|
pyinfra/facts/openrc.py,sha256=gAXl3evsDXSHcIKIj4E_CRerAugEFpGGNRxJBr0rBAk,1490
|
|
62
|
+
pyinfra/facts/opkg.py,sha256=bnVOPnLIevAKMb_dnpSohzRmTTbVmSo7R5H1XrChfgI,7210
|
|
60
63
|
pyinfra/facts/pacman.py,sha256=NJ43rYc5k4uhl5sFxd9sTg7yqTQhB3xsREsusS6BzwU,1198
|
|
61
64
|
pyinfra/facts/pip.py,sha256=ORyrVxu_-8eIr0uSCkI6x4MH-sorDEavml4aozcdrOA,738
|
|
65
|
+
pyinfra/facts/pipx.py,sha256=5iZSnhYq8ZZ8dOhEmek254_nXT1nEm4BulU56TPnGuM,1730
|
|
62
66
|
pyinfra/facts/pkg.py,sha256=iIvinsJxEaSYAaRw_5_WD5WlIlcy4ksKa0lwj0CFxco,520
|
|
63
67
|
pyinfra/facts/pkgin.py,sha256=Y4QfUwtWcBH-h77O-MtiixSepHK0p-lcebrqdmABfs8,580
|
|
64
|
-
pyinfra/facts/
|
|
68
|
+
pyinfra/facts/podman.py,sha256=4vk2qhfFFd66tAE9zVjNUkvDTbntjrTkMkhMEvjQIXo,1091
|
|
69
|
+
pyinfra/facts/postgres.py,sha256=K_2JCJhmulJeyVPE5jAJhpgPpM7OF5LVJcfgvRg6icg,4293
|
|
65
70
|
pyinfra/facts/postgresql.py,sha256=4nusMVvGhtku86KX4O4vjSxh_MamxZy_kmTQvvy0GhE,223
|
|
66
71
|
pyinfra/facts/rpm.py,sha256=SzHNCNUMA-j5uJl4PKRTFpckOvNZ2zpxNeQyOCl8Usk,2225
|
|
67
72
|
pyinfra/facts/runit.py,sha256=iarF_Tql8bkNeHsKGRANRKNyBWwMsflsTNXj1Wz14i8,2021
|
|
68
73
|
pyinfra/facts/selinux.py,sha256=as3AvC6p88er0rYBFTdIWf6k3w0pVjDqDAV9Ur5zY90,4443
|
|
69
|
-
pyinfra/facts/server.py,sha256=
|
|
74
|
+
pyinfra/facts/server.py,sha256=bJ9gg8q_wUKwdJgETbXhOVRiJ-4WKkZn30qeeFhQT9A,19064
|
|
70
75
|
pyinfra/facts/snap.py,sha256=6br9IMIoq88z_RS0FLXxfodIVjUmyPU9eZBa9zO8H1o,2027
|
|
71
76
|
pyinfra/facts/systemd.py,sha256=RS6pdcgpIvWMbQeT93O57EKXQzFzR0tF29lCAJQmaAk,4227
|
|
72
77
|
pyinfra/facts/sysvinit.py,sha256=RniaROHyeZD3jVOa_sISpZV4zx8ae8HkUQrtriLIlWc,1521
|
|
@@ -74,42 +79,46 @@ pyinfra/facts/upstart.py,sha256=HYR7vJ6oqtuRhxXQgzGDKYzyKqqVsjT-TtPPWOjBGdA,635
|
|
|
74
79
|
pyinfra/facts/vzctl.py,sha256=S9aclpDBF3DmBLwMltsd9j3B4QxQ5-1Kb1hybZodEqI,678
|
|
75
80
|
pyinfra/facts/xbps.py,sha256=4gAajBlTAg3bo7vRdx3b2TTi-vvU1y86WZqC0H9nUUk,573
|
|
76
81
|
pyinfra/facts/yum.py,sha256=i42g0FIZg62TZFqFcaUQWNekFFFo4G8vf5wyaKUuh8Q,938
|
|
77
|
-
pyinfra/facts/zfs.py,sha256=
|
|
82
|
+
pyinfra/facts/zfs.py,sha256=YC9_-BnDkmhDtUUGWy2NpE-rlz5Xzl89uvHNCFpPSYM,1501
|
|
78
83
|
pyinfra/facts/zypper.py,sha256=sAIZ5SqjsJ1Dc5e3pJrOoR5Gnu9BqZHpDFI8gKLts84,873
|
|
79
84
|
pyinfra/facts/util/__init__.py,sha256=f7HKu8z9_yFC899ajJ3RFiyivioaZeGfOI6nf9GviCs,521
|
|
80
85
|
pyinfra/facts/util/databases.py,sha256=EphGQApzRBXI2nG1FL9h8bozY-o4SgdQgpv9YcnCkxs,730
|
|
81
86
|
pyinfra/facts/util/packaging.py,sha256=4RzjDYb3HrRWZuuPlEfYHgbftLH4r1FOccN5QyIGkrk,1181
|
|
87
|
+
pyinfra/facts/util/units.py,sha256=SNHCisxGwZedCOqO9tfOWJpZ5Stc0Wcg9mZcXoKBY0A,714
|
|
82
88
|
pyinfra/facts/util/win_files.py,sha256=S_IQ5kJD6ZgkEcVHajgh7BIMolLV-1q1ghIcwAS-E1Q,2561
|
|
83
89
|
pyinfra/operations/__init__.py,sha256=SOcW337KXIzD_LH-iJJfq14BQcCs5JzwswJ0PIzDgF4,357
|
|
84
90
|
pyinfra/operations/apk.py,sha256=_0vOjbSiGx6EWv9rvTmQdGnRZQ_NA_Dyd3QW1cTzFgI,2111
|
|
85
|
-
pyinfra/operations/apt.py,sha256=
|
|
91
|
+
pyinfra/operations/apt.py,sha256=OKAMTcvS83KwF9lCmLZ09swKezwD7GdIUJu6dGTfRao,14152
|
|
86
92
|
pyinfra/operations/brew.py,sha256=aghLE4qyuhhRbt6fgSPV6_5fyWgTohA77Dc0gol19UU,5155
|
|
87
93
|
pyinfra/operations/bsdinit.py,sha256=okQUQDr2H8Z-cAdfdbPJiuGujsHLuV5gpuMZ1UlICEM,1648
|
|
88
94
|
pyinfra/operations/cargo.py,sha256=mXWd6pb0IR6kzJMmPHwXZN-VJ-B_y8AdOFlrRzDQOZI,1104
|
|
89
95
|
pyinfra/operations/choco.py,sha256=8nG0wc1tZEA0L0HTIjgR00IDiONARokyzHyKj-R3xmo,1515
|
|
96
|
+
pyinfra/operations/crontab.py,sha256=qhYzj9xh-A6p95sJ0i_DDKOIm7WoNgiPjcR43ZB9iv8,6454
|
|
90
97
|
pyinfra/operations/dnf.py,sha256=3154Rer6dejVB1AK-CqyJhpMVn_djaSDJrVMs62GNcE,5599
|
|
91
|
-
pyinfra/operations/docker.py,sha256=
|
|
92
|
-
pyinfra/operations/files.py,sha256=
|
|
98
|
+
pyinfra/operations/docker.py,sha256=6jkeZT0JW8f5df2N3ANai4JOkGBnGd3nETE39_Ho7kM,8441
|
|
99
|
+
pyinfra/operations/files.py,sha256=BkDn4hTsic2T1r_0GAHz-r1Nxd6FZMWoTFqf8izu6VU,54707
|
|
93
100
|
pyinfra/operations/flatpak.py,sha256=c2OAyuAvt3alVm9D8W6gCfmk5JFydcZD36gO_OhB8Bc,1891
|
|
94
101
|
pyinfra/operations/gem.py,sha256=2C85sOwIRMHGvmPg4uAlUVf6MokhiA7LLPqzdJRHsBg,1132
|
|
95
|
-
pyinfra/operations/git.py,sha256=
|
|
102
|
+
pyinfra/operations/git.py,sha256=ajyusK8rFeU_u3piR3glzJGLhomKF0IuzlbR667eyts,13035
|
|
96
103
|
pyinfra/operations/iptables.py,sha256=brYa4kMhZKFTu24BNds_1b6sOaG94EfqWEoWrScx-Ck,9341
|
|
97
104
|
pyinfra/operations/launchd.py,sha256=6HWvqoQ74idV_NStOEmFXwu0dmTv7YDvFtsK8An2Lu4,1177
|
|
98
105
|
pyinfra/operations/lxd.py,sha256=bKm9gsgZaruKYSL7OYFMiou-wGP4BzwIMWzjW4AZYrk,1742
|
|
99
106
|
pyinfra/operations/mysql.py,sha256=ctm2Z6MaB0mOArCNU4TsJzaXiKXQaa_ahmsC5Vvyi10,19857
|
|
100
107
|
pyinfra/operations/npm.py,sha256=bUmfQsClZ2YcHiihiC7k5widIXIi6lbfx_32iyaAKfo,1499
|
|
101
108
|
pyinfra/operations/openrc.py,sha256=GXFoCHEEKeyQyRvrZcNYx8og4fmgmtzTVAViBzt84TE,1580
|
|
109
|
+
pyinfra/operations/opkg.py,sha256=0P_YyXSvXUyVWJrHtnRxI_etya8yMIny7_LcgbQddEU,2627
|
|
102
110
|
pyinfra/operations/pacman.py,sha256=QMjmsBiiw362nhZY0rEDVQL5A32MG3u7GcmX4q4PzfI,1702
|
|
103
|
-
pyinfra/operations/pip.py,sha256=
|
|
111
|
+
pyinfra/operations/pip.py,sha256=MCmb9FPcyvg6M7lTlRtx2qpXHtyf-SwBVtHqcAkqVzQ,5925
|
|
112
|
+
pyinfra/operations/pipx.py,sha256=PFVXriRIk5gnJXWcFoghsCIVfTy7RvQyvDggjXHALQc,2188
|
|
104
113
|
pyinfra/operations/pkg.py,sha256=rORQBbKeb-6gS0LYu0a0VdiWcDZoovcUONCaf6KMdeQ,2298
|
|
105
114
|
pyinfra/operations/pkgin.py,sha256=zhUyGzKjnUfGoyHbMoYMbeeMzcsiOUpBz1zIzppigJ0,1992
|
|
106
|
-
pyinfra/operations/postgres.py,sha256=
|
|
115
|
+
pyinfra/operations/postgres.py,sha256=qQZxTfMinl6PNR0l6Gt0dxsbnyUOZxQ2OIPi9UQh_cQ,9867
|
|
107
116
|
pyinfra/operations/postgresql.py,sha256=agZjL2W4yxigk9ThIC0V_3wvmcWVdX308aJO24WkN6g,833
|
|
108
117
|
pyinfra/operations/puppet.py,sha256=eDe8D9jQbHYQ4_r4-dmEZfMASKQvj36BR8z_h8aDfw8,861
|
|
109
118
|
pyinfra/operations/python.py,sha256=u569cdPrPesrmzU09nwIPA3bk6TZ-Qv2QP0lJLcO_bw,2021
|
|
110
|
-
pyinfra/operations/runit.py,sha256
|
|
119
|
+
pyinfra/operations/runit.py,sha256=-K0GhORE56J4Gjh7PCriSx9zZI7XJV-cIb-LnnSuKdY,5162
|
|
111
120
|
pyinfra/operations/selinux.py,sha256=imZ4dbY4tl0GpBSkUgV983jbDDihWNs_OQkOBulT7FQ,5948
|
|
112
|
-
pyinfra/operations/server.py,sha256=
|
|
121
|
+
pyinfra/operations/server.py,sha256=iz8r8MdufX6LAE6Ua4BJTIp6zPHsmY0DT-B6ifA-L9g,30700
|
|
113
122
|
pyinfra/operations/snap.py,sha256=a-QtNE4Dlsavqq425TUIwpEJu4oGw8UlLRkdTFyT1F8,3049
|
|
114
123
|
pyinfra/operations/ssh.py,sha256=wocoaYDlOhhItItAVQCEfnVowTtkg3AP0hQ3mnpUnl0,5634
|
|
115
124
|
pyinfra/operations/systemd.py,sha256=hPHTjASj6N_fRAzLr3DNHnxxIbiiTIIT9UStSxKDkTk,3984
|
|
@@ -118,7 +127,7 @@ pyinfra/operations/upstart.py,sha256=pHb9RGnVhT14A_y6OezfOH-lmniKpiyJqpeoOJl0beE
|
|
|
118
127
|
pyinfra/operations/vzctl.py,sha256=2u2CDkuDjzHBRQ54HfyfLpLrsbT8U7_05EEjbbhKUiU,3110
|
|
119
128
|
pyinfra/operations/xbps.py,sha256=ru3_srMBUyUXGzAsPo7WwoomfM0AeDglFv8CDqB33B0,1508
|
|
120
129
|
pyinfra/operations/yum.py,sha256=Ig7AzQy1C7I8XM37lWbw0nI5lzFGMoX30P8FV8-V5uA,5600
|
|
121
|
-
pyinfra/operations/zfs.py,sha256=
|
|
130
|
+
pyinfra/operations/zfs.py,sha256=FnhiXreqf5qJBusC31dwrQsEi0MvH4qxzjBayHKFcYY,5283
|
|
122
131
|
pyinfra/operations/zypper.py,sha256=z1CWv2uwWBlCLIhHna7U5DojVoKZYoUYpezJ_FM_xK8,5555
|
|
123
132
|
pyinfra/operations/util/__init__.py,sha256=ZAHjeCXtLo0TIOSfZ9h0Sh5IXXRCspfHs3RR1l8tQCE,366
|
|
124
133
|
pyinfra/operations/util/docker.py,sha256=6CvQgeFAXH_lDqKb7RxWpMvlCDwEAXlBaDZoJ8LxrYg,4596
|
|
@@ -129,11 +138,11 @@ pyinfra_cli/__init__.py,sha256=G0X7tNdqT45uWuK3aHIKxMdDeCgJ7zHo6vbxoG6zy_8,284
|
|
|
129
138
|
pyinfra_cli/__main__.py,sha256=WlW7eP0rrL06eguuD_q2RAqgUjg3SW-QnmrayAh2mBQ,887
|
|
130
139
|
pyinfra_cli/commands.py,sha256=J-mCJYvDebJ8M7o3HreB2zToa871-xO6_KjVhPLeHho,1832
|
|
131
140
|
pyinfra_cli/exceptions.py,sha256=iptx9Zj1od7VgSbOyXs7P8tD4zAZ_fwrQFKPlpPrfS0,4806
|
|
132
|
-
pyinfra_cli/inventory.py,sha256=
|
|
141
|
+
pyinfra_cli/inventory.py,sha256=05Z0LpMW8Qt3c2X2sx-roYCQGxNlEDD0RgZILpXCcJs,11768
|
|
133
142
|
pyinfra_cli/log.py,sha256=7WEGtmf3ncF1BtXL2icUjyxeRKy-7XrCcQ2Hg4GWX5Y,2201
|
|
134
143
|
pyinfra_cli/main.py,sha256=5VTniMcbKuIfjPTzaUklad5fM1BW7CUEARoSV9tPf1U,19954
|
|
135
|
-
pyinfra_cli/prints.py,sha256=
|
|
136
|
-
pyinfra_cli/util.py,sha256=
|
|
144
|
+
pyinfra_cli/prints.py,sha256=wVsTZgqEoL7Q_9dPMRoQXFZdYTIPjSGHhtGjjndEaXg,10291
|
|
145
|
+
pyinfra_cli/util.py,sha256=9ehdJQ8pDNBLHXoFst9p696VDT-b_qo8UFMJrqdE1rE,6424
|
|
137
146
|
pyinfra_cli/virtualenv.py,sha256=6j9W54JkQLN02SrZZIVwszp0GxlaaDEUWFZjBDHIWNA,2466
|
|
138
147
|
tests/test_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
139
148
|
tests/test_api/test_api.py,sha256=Ig2ebkNACYbHcC4_zRkxS9vj5ZEogoPqGx30ErIKChg,2413
|
|
@@ -148,9 +157,9 @@ tests/test_api/test_api_operations.py,sha256=GUfnuHK2NoTAGdOT4AbytT9R8i3ZZIvGP7K
|
|
|
148
157
|
tests/test_api/test_api_util.py,sha256=uHv4oLpoy1_tzOoqFA1zpdvC74SvjitZbxQwp0dmjTs,1716
|
|
149
158
|
tests/test_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
150
159
|
tests/test_cli/test_cli.py,sha256=IeWuhkhLzIkRbOEx5-yaW6xV5l4Y8fxaGaDGlMcOyYE,6016
|
|
151
|
-
tests/test_cli/test_cli_deploy.py,sha256=
|
|
160
|
+
tests/test_cli/test_cli_deploy.py,sha256=vZC7twj8sPCy05loO50P-D_Xf73r6XN4m7yVj7TIFmo,5243
|
|
152
161
|
tests/test_cli/test_cli_exceptions.py,sha256=QaUv40q6Tp0HdcVEvPggFF4dsP2qdy57y9VAcGcR1So,3060
|
|
153
|
-
tests/test_cli/test_cli_inventory.py,sha256=
|
|
162
|
+
tests/test_cli/test_cli_inventory.py,sha256=feYSyxGBUsDGriOaqs0CBMbAIW7yUfoT1mktLwqMI0w,4036
|
|
154
163
|
tests/test_cli/test_cli_util.py,sha256=Fqn_9fnG6xNfw8OMHSZs9hQoW260aSt409S1bUAJs-Q,2500
|
|
155
164
|
tests/test_cli/test_context_objects.py,sha256=JiUTwQP7yvcqA47Kq9jtdsB_Z8nxGMZN46d9pR--FYA,2130
|
|
156
165
|
tests/test_cli/util.py,sha256=kp_-XsGnTyDgG6IHWorYzl5VD_WLe77dKOH007TDOUE,338
|
|
@@ -160,13 +169,13 @@ tests/test_connectors/test_docker.py,sha256=0EjkfhCHpLCfL4X-AIdMNw5ASaseY0tbRAn7
|
|
|
160
169
|
tests/test_connectors/test_dockerssh.py,sha256=MaC9IK1OZDiqoIsuLOZBJnPDglsMoPDoL19LQtXsyCE,9303
|
|
161
170
|
tests/test_connectors/test_local.py,sha256=N_FkejDZKu7XLnKeApqfBARYMyxf-hRXCQJrXLHvwRg,7442
|
|
162
171
|
tests/test_connectors/test_ssh.py,sha256=zYL0FbRXzqkYJslhmVeUgSkcHtozhmvZfRcaqDrYKvI,40386
|
|
163
|
-
tests/test_connectors/test_sshuserclient.py,sha256=
|
|
172
|
+
tests/test_connectors/test_sshuserclient.py,sha256=Rm1zxSODDXQXuQ3qdkCA12FCUj7Zkgwwe-s2UrsZAoE,8599
|
|
164
173
|
tests/test_connectors/test_terraform.py,sha256=RZInSjes394eR5CrGGEjzZEFY-UpQj47n4MZH0_ExyY,3779
|
|
165
174
|
tests/test_connectors/test_util.py,sha256=hQir0WyjH0LEF6xvIyHNyqdI5pkJX6qUR9287MgO2bY,4647
|
|
166
175
|
tests/test_connectors/test_vagrant.py,sha256=27qRB7ftjEPaj4ejBNZ-rR4Ou1AD1VyVcf2XjwZPG3M,3640
|
|
167
|
-
pyinfra-3.
|
|
168
|
-
pyinfra-3.
|
|
169
|
-
pyinfra-3.
|
|
170
|
-
pyinfra-3.
|
|
171
|
-
pyinfra-3.
|
|
172
|
-
pyinfra-3.
|
|
176
|
+
pyinfra-3.2.dist-info/LICENSE.md,sha256=gwC95tUll0gwB32tHNkTAasN7Sb6vjWzXa305NwClbI,1076
|
|
177
|
+
pyinfra-3.2.dist-info/METADATA,sha256=edyD-006yB75ALtE2Hz3_Oqj4DLFUJjEMvfs7fwQw44,8011
|
|
178
|
+
pyinfra-3.2.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
|
|
179
|
+
pyinfra-3.2.dist-info/entry_points.txt,sha256=BraEFyquy05M8ch33HZXOHoH_m2BTqejL3xX3NrpzOM,471
|
|
180
|
+
pyinfra-3.2.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
|
|
181
|
+
pyinfra-3.2.dist-info/RECORD,,
|
pyinfra_cli/inventory.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import socket
|
|
2
2
|
from collections import defaultdict
|
|
3
3
|
from os import listdir, path
|
|
4
|
-
from types import GeneratorType
|
|
5
4
|
from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union
|
|
6
5
|
|
|
7
6
|
from pyinfra import logger
|
|
@@ -23,18 +22,37 @@ def _is_inventory_group(key: str, value: Any):
|
|
|
23
22
|
Verify that a module-level variable (key = value) is a valid inventory group.
|
|
24
23
|
"""
|
|
25
24
|
|
|
26
|
-
if key.startswith("
|
|
25
|
+
if key.startswith("__"):
|
|
26
|
+
# Ignore __builtins__/__file__
|
|
27
|
+
return False
|
|
28
|
+
elif key.startswith("_"):
|
|
29
|
+
logger.debug(
|
|
30
|
+
'Ignoring variable "%s" in inventory file since it starts with a leading underscore',
|
|
31
|
+
key,
|
|
32
|
+
)
|
|
27
33
|
return False
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
if isinstance(value, list):
|
|
36
|
+
pass
|
|
37
|
+
elif isinstance(value, tuple):
|
|
38
|
+
# If the group is a tuple of (hosts, data), check the hosts
|
|
31
39
|
value = value[0]
|
|
40
|
+
else:
|
|
41
|
+
logger.debug(
|
|
42
|
+
'Ignoring variable "%s" in inventory file since it is not a list or tuple',
|
|
43
|
+
key,
|
|
44
|
+
)
|
|
45
|
+
return False
|
|
32
46
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
if not all(isinstance(item, ALLOWED_HOST_TYPES) for item in value):
|
|
48
|
+
logger.warning(
|
|
49
|
+
'Ignoring host group "%s". '
|
|
50
|
+
"Host groups may only contain strings (host) or tuples (host, data).",
|
|
51
|
+
key,
|
|
52
|
+
)
|
|
53
|
+
return False
|
|
36
54
|
|
|
37
|
-
return
|
|
55
|
+
return True
|
|
38
56
|
|
|
39
57
|
|
|
40
58
|
def _get_group_data(dirname_or_filename: str):
|
|
@@ -258,7 +276,6 @@ def make_inventory_from_files(
|
|
|
258
276
|
for hosts in groups.values():
|
|
259
277
|
# Groups can be a list of hosts or tuple of (hosts, data)
|
|
260
278
|
hosts = _get_any_tuple_first(hosts)
|
|
261
|
-
|
|
262
279
|
for host in hosts:
|
|
263
280
|
# Hosts can be a hostname or tuple of (hostname, data)
|
|
264
281
|
hostname = _get_any_tuple_first(host)
|
pyinfra_cli/prints.py
CHANGED
|
@@ -281,6 +281,8 @@ def print_results(state: "State"):
|
|
|
281
281
|
(logger.info, ["Operation", "Hosts", "Success", "Error", "No Change"]),
|
|
282
282
|
]
|
|
283
283
|
|
|
284
|
+
totals = {"hosts": 0, "success": 0, "error": 0, "no_change": 0}
|
|
285
|
+
|
|
284
286
|
for op_hash in state.get_op_order():
|
|
285
287
|
hosts_in_op = 0
|
|
286
288
|
hosts_in_op_success: list[str] = []
|
|
@@ -306,19 +308,32 @@ def print_results(state: "State"):
|
|
|
306
308
|
str(hosts_in_op),
|
|
307
309
|
]
|
|
308
310
|
|
|
311
|
+
totals["hosts"] += hosts_in_op
|
|
312
|
+
|
|
309
313
|
if hosts_in_op_success:
|
|
310
|
-
|
|
314
|
+
num_hosts_in_op_success = len(hosts_in_op_success)
|
|
315
|
+
row.append(str(num_hosts_in_op_success))
|
|
316
|
+
totals["success"] += num_hosts_in_op_success
|
|
311
317
|
else:
|
|
312
318
|
row.append("-")
|
|
319
|
+
|
|
313
320
|
if hosts_in_op_error:
|
|
314
|
-
|
|
321
|
+
num_hosts_in_op_error = len(hosts_in_op_error)
|
|
322
|
+
row.append(str(num_hosts_in_op_error))
|
|
323
|
+
totals["error"] += num_hosts_in_op_error
|
|
315
324
|
else:
|
|
316
325
|
row.append("-")
|
|
326
|
+
|
|
317
327
|
if hosts_in_op_no_change:
|
|
318
|
-
|
|
328
|
+
num_hosts_in_op_no_change = len(hosts_in_op_no_change)
|
|
329
|
+
row.append(str(num_hosts_in_op_no_change))
|
|
330
|
+
totals["no_change"] += num_hosts_in_op_no_change
|
|
319
331
|
else:
|
|
320
332
|
row.append("-")
|
|
321
333
|
|
|
322
334
|
rows.append((logger.info, row))
|
|
323
335
|
|
|
336
|
+
totals_row = ["Grand total"] + [str(i) if i else "-" for i in totals.values()]
|
|
337
|
+
rows.append((logger.info, totals_row))
|
|
338
|
+
|
|
324
339
|
print_rows(rows)
|
pyinfra_cli/util.py
CHANGED
|
@@ -43,26 +43,28 @@ class TestCliDeployState(PatchSSHTestCase):
|
|
|
43
43
|
assert executed is False
|
|
44
44
|
|
|
45
45
|
def test_deploy(self):
|
|
46
|
-
|
|
46
|
+
a_task_file_path = path.join("tasks", "a_task.py")
|
|
47
|
+
b_task_file_path = path.join("tasks", "b_task.py")
|
|
47
48
|
nested_task_path = path.join("tasks", "another_task.py")
|
|
48
49
|
correct_op_name_and_host_names = [
|
|
49
50
|
("First main operation", True), # true for all hosts
|
|
50
51
|
("Second main operation", ("somehost",)),
|
|
51
|
-
("{0} | First task operation".format(
|
|
52
|
-
("{0} | Task order loop 1".format(
|
|
53
|
-
("{0} | 2nd Task order loop 1".format(
|
|
54
|
-
("{0} | Task order loop 2".format(
|
|
55
|
-
("{0} | 2nd Task order loop 2".format(
|
|
52
|
+
("{0} | First task operation".format(a_task_file_path), ("anotherhost",)),
|
|
53
|
+
("{0} | Task order loop 1".format(a_task_file_path), ("anotherhost",)),
|
|
54
|
+
("{0} | 2nd Task order loop 1".format(a_task_file_path), ("anotherhost",)),
|
|
55
|
+
("{0} | Task order loop 2".format(a_task_file_path), ("anotherhost",)),
|
|
56
|
+
("{0} | 2nd Task order loop 2".format(a_task_file_path), ("anotherhost",)),
|
|
56
57
|
(
|
|
57
|
-
"{0} | {1} | Second task operation".format(
|
|
58
|
+
"{0} | {1} | Second task operation".format(a_task_file_path, nested_task_path),
|
|
58
59
|
("anotherhost",),
|
|
59
60
|
),
|
|
60
|
-
("{0} | First task operation".format(
|
|
61
|
-
("{0} | Task order loop 1".format(
|
|
62
|
-
("{0} | 2nd Task order loop 1".format(
|
|
63
|
-
("{0} | Task order loop 2".format(
|
|
64
|
-
("{0} | 2nd Task order loop 2".format(
|
|
65
|
-
("{0} | {1} | Second task operation".format(
|
|
61
|
+
("{0} | First task operation".format(a_task_file_path), True),
|
|
62
|
+
("{0} | Task order loop 1".format(a_task_file_path), True),
|
|
63
|
+
("{0} | 2nd Task order loop 1".format(a_task_file_path), True),
|
|
64
|
+
("{0} | Task order loop 2".format(a_task_file_path), True),
|
|
65
|
+
("{0} | 2nd Task order loop 2".format(a_task_file_path), True),
|
|
66
|
+
("{0} | {1} | Second task operation".format(a_task_file_path, nested_task_path), True),
|
|
67
|
+
("{0} | Important task operation".format(b_task_file_path), True),
|
|
66
68
|
("My deploy | First deploy operation", True),
|
|
67
69
|
("My deploy | My nested deploy | First nested deploy operation", True),
|
|
68
70
|
("My deploy | Second deploy operation", True),
|
|
@@ -64,3 +64,56 @@ class TestCliInventory(PatchSSHTestCase):
|
|
|
64
64
|
assert "leftover_data" in inventory.group_data
|
|
65
65
|
assert inventory.group_data["leftover_data"].get("still_parsed") == "never_used"
|
|
66
66
|
assert inventory.group_data["leftover_data"].get("_global_arg") == "gets_parsed"
|
|
67
|
+
|
|
68
|
+
def test_ignores_variables_with_leading_underscore(self):
|
|
69
|
+
ctx_state.reset()
|
|
70
|
+
ctx_inventory.reset()
|
|
71
|
+
|
|
72
|
+
result = run_cli(
|
|
73
|
+
path.join("tests", "test_cli", "inventories", "invalid.py"),
|
|
74
|
+
"exec",
|
|
75
|
+
"--debug",
|
|
76
|
+
"--",
|
|
77
|
+
"echo hi",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
assert result.exit_code == 0, result.stdout
|
|
81
|
+
assert (
|
|
82
|
+
'Ignoring variable "_hosts" in inventory file since it starts with a leading underscore'
|
|
83
|
+
in result.stdout
|
|
84
|
+
)
|
|
85
|
+
assert inventory.hosts == {}
|
|
86
|
+
|
|
87
|
+
def test_only_supports_list_and_tuples(self):
|
|
88
|
+
ctx_state.reset()
|
|
89
|
+
ctx_inventory.reset()
|
|
90
|
+
|
|
91
|
+
result = run_cli(
|
|
92
|
+
path.join("tests", "test_cli", "inventories", "invalid.py"),
|
|
93
|
+
"exec",
|
|
94
|
+
"--debug",
|
|
95
|
+
"--",
|
|
96
|
+
"echo hi",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
assert result.exit_code == 0, result.stdout
|
|
100
|
+
assert 'Ignoring variable "dict_hosts" in inventory file' in result.stdout, result.stdout
|
|
101
|
+
assert (
|
|
102
|
+
'Ignoring variable "generator_hosts" in inventory file' in result.stdout
|
|
103
|
+
), result.stdout
|
|
104
|
+
assert inventory.hosts == {}
|
|
105
|
+
|
|
106
|
+
def test_host_groups_may_only_contain_strings_or_tuples(self):
|
|
107
|
+
ctx_state.reset()
|
|
108
|
+
ctx_inventory.reset()
|
|
109
|
+
|
|
110
|
+
result = run_cli(
|
|
111
|
+
path.join("tests", "test_cli", "inventories", "invalid.py"),
|
|
112
|
+
"exec",
|
|
113
|
+
"--",
|
|
114
|
+
"echo hi",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
assert result.exit_code == 0, result.stdout
|
|
118
|
+
assert 'Ignoring host group "issue_662"' in result.stdout, result.stdout
|
|
119
|
+
assert inventory.hosts == {}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
from base64 import b64decode
|
|
1
2
|
from unittest import TestCase
|
|
2
3
|
from unittest.mock import mock_open, patch
|
|
3
4
|
|
|
4
|
-
from paramiko import ProxyCommand
|
|
5
|
+
from paramiko import PKey, ProxyCommand, SSHException
|
|
5
6
|
|
|
6
7
|
from pyinfra.connectors.sshuserclient import SSHClient
|
|
7
8
|
from pyinfra.connectors.sshuserclient.client import AskPolicy, get_ssh_config
|
|
@@ -41,6 +42,30 @@ LOOPING_SSH_CONFIG_DATA = """
|
|
|
41
42
|
Include other_file
|
|
42
43
|
"""
|
|
43
44
|
|
|
45
|
+
# To ensure that we don't remove things from users hostfiles
|
|
46
|
+
# we should test that all modifications only append to the
|
|
47
|
+
# hostfile, and don't delete any data or comments.
|
|
48
|
+
EXAMPLE_KEY_1 = (
|
|
49
|
+
"AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+"
|
|
50
|
+
"VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/"
|
|
51
|
+
"C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXk"
|
|
52
|
+
"E2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMj"
|
|
53
|
+
"A2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIE"
|
|
54
|
+
"s4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+Ej"
|
|
55
|
+
"qoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/Wnw"
|
|
56
|
+
"H6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk="
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
KNOWN_HOSTS_EXAMPLE_DATA = f"""
|
|
60
|
+
# this is an important comment
|
|
61
|
+
|
|
62
|
+
# another comment after the newline
|
|
63
|
+
|
|
64
|
+
@cert-authority example-domain.lan ssh-rsa {EXAMPLE_KEY_1}
|
|
65
|
+
|
|
66
|
+
192.168.1.222 ssh-rsa {EXAMPLE_KEY_1}
|
|
67
|
+
"""
|
|
68
|
+
|
|
44
69
|
|
|
45
70
|
class TestSSHUserConfigMissing(TestCase):
|
|
46
71
|
def setUp(self):
|
|
@@ -199,3 +224,45 @@ class TestSSHUserConfig(TestCase):
|
|
|
199
224
|
port=22,
|
|
200
225
|
test="kwarg",
|
|
201
226
|
)
|
|
227
|
+
|
|
228
|
+
def test_missing_hostkey(self):
|
|
229
|
+
client = SSHClient()
|
|
230
|
+
policy = AskPolicy()
|
|
231
|
+
example_hostname = "new_host"
|
|
232
|
+
example_keytype = "ecdsa-sha2-nistp256"
|
|
233
|
+
example_key = (
|
|
234
|
+
"AAAAE2VjZHNhLXNoYTItbmlzdHAyNT"
|
|
235
|
+
"YAAAAIbmlzdHAyNTYAAABBBHNp1NM"
|
|
236
|
+
"ZjxPBuuKwIPfkVJqWaH3oUtW137kIW"
|
|
237
|
+
"P4PlCyACt8zVIIimFhIpwRUidcf7jw"
|
|
238
|
+
"VWPAJvfBjEPqewDApnZQ="
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
key = PKey.from_type_string(
|
|
242
|
+
example_keytype,
|
|
243
|
+
b64decode(example_key),
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Check if AskPolicy respects not importing and properly raises SSHException
|
|
247
|
+
with self.subTest("Check user 'no'"):
|
|
248
|
+
with patch("builtins.input", return_value="n"):
|
|
249
|
+
self.assertRaises(
|
|
250
|
+
SSHException, lambda: policy.missing_host_key(client, example_hostname, key)
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Check if AskPolicy properly appends to hostfile
|
|
254
|
+
with self.subTest("Check user 'yes'"):
|
|
255
|
+
mock_data = mock_open(read_data=KNOWN_HOSTS_EXAMPLE_DATA)
|
|
256
|
+
# Read mock hostfile
|
|
257
|
+
with patch("pyinfra.connectors.sshuserclient.client.open", mock_data):
|
|
258
|
+
with patch("paramiko.hostkeys.open", mock_data):
|
|
259
|
+
with patch("builtins.input", return_value="y"):
|
|
260
|
+
policy.missing_host_key(client, "new_host", key)
|
|
261
|
+
|
|
262
|
+
# Assert that we appended correctly to the file
|
|
263
|
+
write_call_args = mock_data.return_value.write.call_args
|
|
264
|
+
# Ensure we only wrote once and then closed the handle.
|
|
265
|
+
assert len(write_call_args) == 2
|
|
266
|
+
# Ensure we wrote the correct content
|
|
267
|
+
correct_output = f"{example_hostname} {example_keytype} {example_key}\n"
|
|
268
|
+
assert write_call_args[0][0] == correct_output
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|