pyinfra 3.0.2__py2.py3-none-any.whl → 3.1.1__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/operations/git.py CHANGED
@@ -184,7 +184,7 @@ def worktree(
184
184
  + from_remote_branch: a 2-tuple ``(remote, branch)`` that identifies a remote branch
185
185
  + present: whether the working tree should exist
186
186
  + assume_repo_exists: whether to assume the main repo exists
187
- + force: remove unclean working tree if should not exist
187
+ + force: whether to use ``--force`` when adding/removing worktrees
188
188
  + user: chown files to this user after
189
189
  + group: chown files to this group after
190
190
 
@@ -205,6 +205,14 @@ def worktree(
205
205
  commitish="4e091aa0"
206
206
  )
207
207
 
208
+ git.worktree(
209
+ name="Create a worktree from the tag `4e091aa0`, even if already registered",
210
+ repo="/usr/local/src/pyinfra/master",
211
+ worktree="/usr/local/src/pyinfra/2.x",
212
+ commitish="2.x",
213
+ force=True
214
+ )
215
+
208
216
  git.worktree(
209
217
  name="Create a worktree with a new local branch `v1.0`",
210
218
  repo="/usr/local/src/pyinfra/master",
@@ -250,6 +258,15 @@ def worktree(
250
258
  commitish="v1.0"
251
259
  )
252
260
 
261
+ git.worktree(
262
+ name="Idempotent worktree creation, never pulls",
263
+ repo="/usr/local/src/pyinfra/master",
264
+ worktree="/usr/local/src/pyinfra/hotfix",
265
+ new_branch="v1.0",
266
+ commitish="v1.0",
267
+ pull=False
268
+ )
269
+
253
270
  git.worktree(
254
271
  name="Pull an existing worktree already linked to a tracking branch",
255
272
  repo="/usr/local/src/pyinfra/master",
@@ -295,6 +312,9 @@ def worktree(
295
312
  elif detached:
296
313
  command_parts.append("--detach")
297
314
 
315
+ if force:
316
+ command_parts.append("--force")
317
+
298
318
  command_parts.append(worktree)
299
319
 
300
320
  if commitish:
@@ -317,9 +337,12 @@ def worktree(
317
337
 
318
338
  # It exists and we still want it => pull/rebase it
319
339
  elif host.get_fact(Directory, path=worktree) and present:
340
+ if not pull:
341
+ host.noop("Pull is disabled")
342
+
320
343
  # pull the worktree only if it's already linked to a tracking branch or
321
344
  # if a remote branch is set
322
- if host.get_fact(GitTrackingBranch, repo=worktree) or from_remote_branch:
345
+ elif host.get_fact(GitTrackingBranch, repo=worktree) or from_remote_branch:
323
346
  command = "cd {0} && git pull".format(worktree)
324
347
 
325
348
  if rebase:
@@ -554,7 +554,7 @@ def dump(
554
554
  )
555
555
  """
556
556
 
557
- yield "{0} > {1}".format(
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
- dest,
566
+ ">",
567
+ QuoteString(dest),
567
568
  )
568
569
 
569
570
 
@@ -595,7 +596,7 @@ def load(
595
596
  )
596
597
  """
597
598
 
598
- commands_bits = [
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
  )
@@ -89,12 +89,12 @@ def reboot(delay=10, interval=1, reboot_timeout=300):
89
89
  sleep(delay)
90
90
  max_retries = round(reboot_timeout / interval)
91
91
 
92
- host.connection = None # remove the connection object
92
+ host.disconnect() # make sure we are properly disconnected
93
93
  retries = 0
94
94
 
95
95
  while True:
96
96
  host.connect(show_errors=False)
97
- if host.connection:
97
+ if host.connected:
98
98
  break
99
99
 
100
100
  if retries > max_retries:
@@ -723,11 +723,11 @@ def crontab(
723
723
  if any(
724
724
  (
725
725
  special_time != existing_crontab.get("special_time"),
726
- minute != existing_crontab.get("minute"),
727
- hour != existing_crontab.get("hour"),
728
- month != existing_crontab.get("month"),
729
- day_of_week != existing_crontab.get("day_of_week"),
730
- day_of_month != existing_crontab.get("day_of_month"),
726
+ try_int(minute) != existing_crontab.get("minute"),
727
+ try_int(hour) != existing_crontab.get("hour"),
728
+ try_int(month) != existing_crontab.get("month"),
729
+ try_int(day_of_week) != existing_crontab.get("day_of_week"),
730
+ try_int(day_of_month) != existing_crontab.get("day_of_month"),
731
731
  existing_crontab_command != command,
732
732
  ),
733
733
  ):
@@ -881,11 +881,11 @@ def user_authorized_keys(
881
881
 
882
882
  if path.exists(try_path):
883
883
  with open(try_path, "r") as f:
884
- return f.read().strip()
884
+ return [key.strip() for key in f.readlines()]
885
885
 
886
- return key.strip()
886
+ return [key.strip()]
887
887
 
888
- public_keys = list(map(read_any_pub_key_file, public_keys))
888
+ public_keys = [key for key_or_file in public_keys for key in read_any_pub_key_file(key_or_file)]
889
889
 
890
890
  # Ensure .ssh directory
891
891
  # note that this always outputs commands unless the SSH user has access to the
@@ -0,0 +1,175 @@
1
+ """
2
+ Manage ZFS filesystems.
3
+ """
4
+
5
+ from pyinfra import host
6
+ from pyinfra.api import operation
7
+ from pyinfra.facts.zfs import Datasets, Snapshots
8
+
9
+
10
+ @operation()
11
+ def dataset(
12
+ dataset_name,
13
+ present=True,
14
+ recursive=False,
15
+ sparse=None,
16
+ volume_size=None,
17
+ properties={},
18
+ **extra_props,
19
+ ):
20
+ """
21
+ Create, destroy or set properties on a ZFS dataset (e.g. filesystem,
22
+ volume, snapshot).
23
+
24
+ + dataset_name: name of the filesystem to operate on
25
+ + present: whether the named filesystem should exist
26
+ + recursive: whether to create parent datasets, or destroy child datasets
27
+ + sparse: for volumes, whether to create a sparse volume with no allocation
28
+ + volume_size: the size of the volume
29
+ + properties: the ZFS properties that should be set on the dataset.
30
+ + **extra_props: additional props; merged with `properties` for convenience
31
+
32
+ **Examples:**
33
+
34
+ .. code:: python
35
+
36
+ zfs.dataset(
37
+ "tank/srv",
38
+ mountpoint="/srv",
39
+ compression="lz4",
40
+ properties={"com.sun:auto_snapshot": "true"}
41
+ )
42
+ zfs.dataset("tank/vm-disks/db_srv_04", volume_size="32G") # creates a volume
43
+ zfs.dataset("tank/home@old_version", present=False)
44
+
45
+ """
46
+
47
+ noop_msg = "{0} is already {1}".format(dataset_name, "present" if present else "absent")
48
+
49
+ properties.update(extra_props)
50
+
51
+ datasets = host.get_fact(Datasets)
52
+
53
+ existing_dataset = datasets.get(dataset_name)
54
+
55
+ if present and not existing_dataset:
56
+ args = ["-o {0}={1}".format(prop, value) for prop, value in properties.items()]
57
+ if recursive:
58
+ args.append("-p")
59
+ if sparse:
60
+ args.append("-s")
61
+ if volume_size:
62
+ args.append("-V {0}".format(volume_size))
63
+
64
+ args.sort() # dicts are unordered, so make sure the test results are deterministic
65
+
66
+ yield "zfs create {0} {1}".format(" ".join(args), dataset_name)
67
+
68
+ elif present and existing_dataset:
69
+ prop_args = [
70
+ "{0}={1}".format(prop, value)
71
+ for prop, value in properties.items() - existing_dataset.items()
72
+ ]
73
+ prop_args.sort()
74
+ if prop_args:
75
+ yield "zfs set {0} {1}".format(" ".join(prop_args), dataset_name)
76
+ else:
77
+ host.noop(noop_msg)
78
+
79
+ elif existing_dataset and not present:
80
+ recursive_arg = "-r" if recursive else ""
81
+ yield "zfs destroy {0} {1}".format(recursive_arg, dataset_name)
82
+
83
+ else:
84
+ host.noop(noop_msg)
85
+
86
+
87
+ @operation()
88
+ def snapshot(snapshot_name, present=True, recursive=False, properties={}, **extra_props):
89
+ """
90
+ Create or destroy a ZFS snapshot, or modify its properties.
91
+
92
+ + dataset_name: name of the filesystem to operate on
93
+ + present: whether the named filesystem should exist
94
+ + recursive: whether to snapshot child datasets
95
+ + properties: the ZFS properties that should be set on the snapshot.
96
+ + **extra_props: additional props; merged with `properties` for convenience
97
+
98
+ **Examples:**
99
+
100
+ .. code:: python
101
+
102
+ zfs.snapshot("tank/home@weekly_backup")
103
+
104
+ """
105
+ properties.update(extra_props)
106
+ snapshots = host.get_fact(Snapshots)
107
+
108
+ if snapshot_name in snapshots or not present:
109
+ yield from dataset._inner(snapshot_name, present=present, properties=properties)
110
+
111
+ else:
112
+ args = ["-o {0}={1}".format(prop, value) for prop, value in properties.items()]
113
+ if recursive:
114
+ args.append("-r")
115
+ yield "zfs snap {0} {1}".format(" ".join(args), snapshot_name)
116
+
117
+
118
+ @operation()
119
+ def volume(
120
+ volume_name, size, sparse=False, present=True, recursive=False, properties={}, **extra_props
121
+ ):
122
+ """
123
+ Create or destroy a ZFS volume, or modify its properties.
124
+
125
+ + volume_name: name of the volume to operate on
126
+ + size: the size of the volume
127
+ + sparse: create a sparse volume
128
+ + present: whether the named volume should exist
129
+ + recursive: whether to create parent datasets or destroy child datasets
130
+ + properties: the ZFS properties that should be set on the snapshot.
131
+ + **extra_props: additional props; merged with `properties` for convenience
132
+
133
+ **Examples:**
134
+
135
+ .. code:: python
136
+
137
+ zfs.volume("tank/vm-disks/db_srv_04", "32G")
138
+
139
+ """
140
+ properties.update(extra_props)
141
+ yield from dataset._inner(
142
+ volume_name,
143
+ volume_size=size,
144
+ present=present,
145
+ sparse=sparse,
146
+ recursive=recursive,
147
+ properties=properties,
148
+ )
149
+
150
+
151
+ @operation()
152
+ def filesystem(fs_name, present=True, recursive=False, properties={}, **extra_props):
153
+ """
154
+ Create or destroy a ZFS filesystem, or modify its properties.
155
+
156
+ + fs_name: name of the volume to operate on
157
+ + present: whether the named volume should exist
158
+ + recursive: whether to create parent datasets or destroy child datasets
159
+ + properties: the ZFS properties that should be set on the snapshot.
160
+ + **extra_props: additional props; merged with `properties` for convenience
161
+
162
+ **Examples:**
163
+
164
+ .. code:: python
165
+
166
+ zfs.filesystem("tank/vm-disks/db_srv_04", "32G")
167
+
168
+ """
169
+ properties.update(extra_props)
170
+ yield from dataset._inner(
171
+ fs_name,
172
+ present=present,
173
+ recursive=recursive,
174
+ properties=properties,
175
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyinfra
3
- Version: 3.0.2
3
+ Version: 3.1.1
4
4
  Summary: pyinfra automates/provisions/manages/deploys infrastructure.
5
5
  Home-page: https://pyinfra.com
6
6
  Author: Nick / Fizzadar
@@ -6,30 +6,30 @@ 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=8BhuJiljFJzaCi9lM8HoEBB9mcnCBilv4Zwdx_349g8,9941
9
+ pyinfra/api/arguments.py,sha256=51cMf5GHBAodg5SNuGAqafNYuWtCciKR6n8bn664TVI,9943
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=xo4F7URUf3xzIChRHZn4zwqs_WTjLjZNC9i9eQjAFk8,2756
15
+ pyinfra/api/deploy.py,sha256=GbXtjhmUYpnzf7WNlqwIrFw6tOMiP6WjJBmJ4CyvNYM,3053
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=3lRhlZDRKvCNvpziaTglExy2Ep1wd4YdmGDNY4emAdA,13466
18
+ pyinfra/api/host.py,sha256=ehTWAAEwpWJ8hQU6lU1kcR0bP-WFnrB12QvnMaaSGu4,13752
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
22
22
  pyinfra/api/state.py,sha256=3dXRjeZJXnzLcbP9E4aogkRPwIg3_kK1h4Tf4FVZock,12622
23
- pyinfra/api/util.py,sha256=qbrC-Hjvzre2_U0l3hHo4wzZBDFN-zcWBNoWQgIyXxE,12272
23
+ 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
- pyinfra/connectors/chroot.py,sha256=Xd72I8T58KIwKOoc0LXCw91AoEIaiHfRLDcDVTHGJ0o,5931
26
+ pyinfra/connectors/chroot.py,sha256=X5U3vi-zPm4DV-lHTwoHU8iEl8dppDiYJoWP_1mJVFY,5919
27
27
  pyinfra/connectors/docker.py,sha256=2UNHhXS4hpLo7I19ixDeSd7JR8SNo43VgqsaUIZQZJ4,8741
28
28
  pyinfra/connectors/dockerssh.py,sha256=VWHY--jqs3yf-RuPUZXav4vLeON9SzoVC9CUyOJo1rg,8919
29
- pyinfra/connectors/local.py,sha256=vYOBQS_5rf-dVaPeG4dJlLwBHqkxAzLjj3aDEgbAsx8,6900
30
- pyinfra/connectors/ssh.py,sha256=QvXrmg3G47VwGEiPP-8Nse-9Yostc5N4PxwiiSlhlwo,21124
29
+ pyinfra/connectors/local.py,sha256=eFDrBalS1yPCIUdQzh8h2HF3VP2nDn0uVFKOeeQZfiw,6888
30
+ pyinfra/connectors/ssh.py,sha256=Pr7W_aiGstkWQqrwm-cNENzhzTWTBLWM-XAAbF9uOEQ,21212
31
31
  pyinfra/connectors/ssh_util.py,sha256=CN_5AdTA3RpiWCnXTrRBjez1NsN59hITDzQmXIkZvoE,3683
32
- pyinfra/connectors/terraform.py,sha256=G7lK168Fz0jNFetc_7_bPT-RnoaRDksJat0R26fqkUk,3617
32
+ pyinfra/connectors/terraform.py,sha256=Tu59cbemll5CfqlIaQtOrLa0HKzl23c64ih0DZXJu1I,4227
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
@@ -37,7 +37,7 @@ pyinfra/connectors/sshuserclient/client.py,sha256=24KWAAqIaUPQIod-CSeXKkA_WhQnII
37
37
  pyinfra/connectors/sshuserclient/config.py,sha256=UMwkvTgAIS7__re6Wz_pwH6EU4kO1-uMQ5zuFakH0v4,2721
38
38
  pyinfra/facts/__init__.py,sha256=myTXSOZmAqmU88Fyifn035h9Lr6Gj2mlka_jDcXyKGw,347
39
39
  pyinfra/facts/apk.py,sha256=q76WdaCNZGKzYia5vMscCsOi4WlnBhcj_9c7Jj-2LqQ,581
40
- pyinfra/facts/apt.py,sha256=Cs5AyfEoxWmKU2wU9pYeTDeikA4mxU4TehwwKr5i57g,2173
40
+ pyinfra/facts/apt.py,sha256=l_ABTyUFlh6C2W6oDdv-9x4MPDJ4M47RRtx8qsTSpJc,2175
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
@@ -45,11 +45,12 @@ pyinfra/facts/choco.py,sha256=A0VCXnI5H9RocgO1IvaNWRIxnXiIZYEzIDG1F-ydJi4,790
45
45
  pyinfra/facts/deb.py,sha256=XGyxnow9wjpE8ZKTZDa1_SNChMyMcNgFeTG1ka5uky4,1922
46
46
  pyinfra/facts/dnf.py,sha256=9rTBgLHewbk8XCJuikzAYCumfFAzbmmHMchlaXBhdWw,977
47
47
  pyinfra/facts/docker.py,sha256=CVSsUEiBaQrNvM1mggoKCXj5DdzwmcbufUY96koqKBw,2250
48
- pyinfra/facts/files.py,sha256=cHbK2S8jJCLB7pDRBTqekHAkBo3_HnlqVcRdiZu8_VU,11536
48
+ pyinfra/facts/files.py,sha256=qJw5_xnPxS5orFy2Ch4oUjmiFUFGGe71UEXoXdZBSek,11765
49
+ pyinfra/facts/flatpak.py,sha256=lnuFZYGPtPDe35YXTVgn5M0rhgcA5ys3fMI6EmvCnm4,1536
49
50
  pyinfra/facts/gem.py,sha256=ktC2hofSwYX0fVcdWleU74ddjW1RPZvKMW-3wYp9lJE,572
50
51
  pyinfra/facts/git.py,sha256=6e_2GjDT2oAxdtkHTyeMYQ9N64gZDorLTTVeZhFel18,1276
51
52
  pyinfra/facts/gpg.py,sha256=wYKoQl4aHXB1UqqbWCdVhUoa6N6Liz01AmH8fPjxR48,3813
52
- pyinfra/facts/hardware.py,sha256=YUK1rZ2tygry9_m7HjV3TQ_GZOW7WHqTlonsOT-zRxk,12022
53
+ pyinfra/facts/hardware.py,sha256=9UOgvzlJ7jx7hPfmkhldYEymKEiKMR3ZVFGfZXl_oe0,12023
53
54
  pyinfra/facts/iptables.py,sha256=sUkywfHZUXnMZF_KshAnyJufrJvZ9fBYnERSRbwOCRE,3374
54
55
  pyinfra/facts/launchd.py,sha256=D0RSpBQXt4a4zJOxwxIb5RYY34sAx6ZrWj8NxwYqhIc,767
55
56
  pyinfra/facts/lxd.py,sha256=NeIMK9VsDXlRf7q3j17oYbdigL7XjLnhmqv63t6CrzQ,466
@@ -64,15 +65,16 @@ pyinfra/facts/postgres.py,sha256=7cg0Nq8wNIWnKw3B8dJpgjSFZ7q90-_NhwEw2NsSJm8,422
64
65
  pyinfra/facts/postgresql.py,sha256=4nusMVvGhtku86KX4O4vjSxh_MamxZy_kmTQvvy0GhE,223
65
66
  pyinfra/facts/rpm.py,sha256=SzHNCNUMA-j5uJl4PKRTFpckOvNZ2zpxNeQyOCl8Usk,2225
66
67
  pyinfra/facts/runit.py,sha256=iarF_Tql8bkNeHsKGRANRKNyBWwMsflsTNXj1Wz14i8,2021
67
- pyinfra/facts/selinux.py,sha256=zzh7o0SU8ocMRJzcYMuAv6ss93onfojpa1pAN1azgeU,4433
68
+ pyinfra/facts/selinux.py,sha256=as3AvC6p88er0rYBFTdIWf6k3w0pVjDqDAV9Ur5zY90,4443
68
69
  pyinfra/facts/server.py,sha256=fDXSNNlZghJTGqr9CWRDob-_N-8xxb-KUZlTf5No-M0,20439
69
70
  pyinfra/facts/snap.py,sha256=6br9IMIoq88z_RS0FLXxfodIVjUmyPU9eZBa9zO8H1o,2027
70
- pyinfra/facts/systemd.py,sha256=eRbhK2A1jWy-jrXFdKVm-VkiCfT_PQP5xR6QbP3pQuc,4012
71
+ pyinfra/facts/systemd.py,sha256=RS6pdcgpIvWMbQeT93O57EKXQzFzR0tF29lCAJQmaAk,4227
71
72
  pyinfra/facts/sysvinit.py,sha256=RniaROHyeZD3jVOa_sISpZV4zx8ae8HkUQrtriLIlWc,1521
72
73
  pyinfra/facts/upstart.py,sha256=HYR7vJ6oqtuRhxXQgzGDKYzyKqqVsjT-TtPPWOjBGdA,635
73
74
  pyinfra/facts/vzctl.py,sha256=S9aclpDBF3DmBLwMltsd9j3B4QxQ5-1Kb1hybZodEqI,678
74
75
  pyinfra/facts/xbps.py,sha256=4gAajBlTAg3bo7vRdx3b2TTi-vvU1y86WZqC0H9nUUk,573
75
76
  pyinfra/facts/yum.py,sha256=i42g0FIZg62TZFqFcaUQWNekFFFo4G8vf5wyaKUuh8Q,938
77
+ pyinfra/facts/zfs.py,sha256=MKqh1AEa5Bpa0KDT673e92TiOb8K7YHoEg-Xo424De8,1311
76
78
  pyinfra/facts/zypper.py,sha256=sAIZ5SqjsJ1Dc5e3pJrOoR5Gnu9BqZHpDFI8gKLts84,873
77
79
  pyinfra/facts/util/__init__.py,sha256=f7HKu8z9_yFC899ajJ3RFiyivioaZeGfOI6nf9GviCs,521
78
80
  pyinfra/facts/util/databases.py,sha256=EphGQApzRBXI2nG1FL9h8bozY-o4SgdQgpv9YcnCkxs,730
@@ -80,33 +82,34 @@ pyinfra/facts/util/packaging.py,sha256=4RzjDYb3HrRWZuuPlEfYHgbftLH4r1FOccN5QyIGk
80
82
  pyinfra/facts/util/win_files.py,sha256=S_IQ5kJD6ZgkEcVHajgh7BIMolLV-1q1ghIcwAS-E1Q,2561
81
83
  pyinfra/operations/__init__.py,sha256=SOcW337KXIzD_LH-iJJfq14BQcCs5JzwswJ0PIzDgF4,357
82
84
  pyinfra/operations/apk.py,sha256=_0vOjbSiGx6EWv9rvTmQdGnRZQ_NA_Dyd3QW1cTzFgI,2111
83
- pyinfra/operations/apt.py,sha256=YAVZXzCE5zvPQb6FMTcQVVKhdKHUKkV8Ra6fRhD8rYA,13887
85
+ pyinfra/operations/apt.py,sha256=JztxgKeSTienk8pFWCK9vfH-Gg7i1AtUCFbTZDptcuA,13884
84
86
  pyinfra/operations/brew.py,sha256=aghLE4qyuhhRbt6fgSPV6_5fyWgTohA77Dc0gol19UU,5155
85
87
  pyinfra/operations/bsdinit.py,sha256=okQUQDr2H8Z-cAdfdbPJiuGujsHLuV5gpuMZ1UlICEM,1648
86
88
  pyinfra/operations/cargo.py,sha256=mXWd6pb0IR6kzJMmPHwXZN-VJ-B_y8AdOFlrRzDQOZI,1104
87
89
  pyinfra/operations/choco.py,sha256=8nG0wc1tZEA0L0HTIjgR00IDiONARokyzHyKj-R3xmo,1515
88
90
  pyinfra/operations/dnf.py,sha256=3154Rer6dejVB1AK-CqyJhpMVn_djaSDJrVMs62GNcE,5599
89
- pyinfra/operations/docker.py,sha256=Mra-m2iayXkc2LgCk2tuE6M7lZHhOGNJD3WQIPM9t2I,8396
90
- pyinfra/operations/files.py,sha256=9O_HKgmVD_z74jtSivY4pKBPrCDKKHDSy0jAB9QERHU,53639
91
+ pyinfra/operations/docker.py,sha256=RMkrVpS-eeN5zwGnpb3WeeOAoGvFma-A1aPvjE9M1KY,8336
92
+ pyinfra/operations/files.py,sha256=M3Dj9dfBpqNnHxrm0gjDq5kNK_8ERL6iV-TEMme2S0M,54092
93
+ pyinfra/operations/flatpak.py,sha256=c2OAyuAvt3alVm9D8W6gCfmk5JFydcZD36gO_OhB8Bc,1891
91
94
  pyinfra/operations/gem.py,sha256=2C85sOwIRMHGvmPg4uAlUVf6MokhiA7LLPqzdJRHsBg,1132
92
- pyinfra/operations/git.py,sha256=b26tQF_4hykTy0FtxiuCkqPk9i8JdZaz-RBhH4X96yw,11789
95
+ pyinfra/operations/git.py,sha256=HXBq07YtEQSo-3bkjBvMem5WzeemQ4omHkh6txQxQKs,12487
93
96
  pyinfra/operations/iptables.py,sha256=brYa4kMhZKFTu24BNds_1b6sOaG94EfqWEoWrScx-Ck,9341
94
97
  pyinfra/operations/launchd.py,sha256=6HWvqoQ74idV_NStOEmFXwu0dmTv7YDvFtsK8An2Lu4,1177
95
98
  pyinfra/operations/lxd.py,sha256=bKm9gsgZaruKYSL7OYFMiou-wGP4BzwIMWzjW4AZYrk,1742
96
- pyinfra/operations/mysql.py,sha256=QcYvEQDlPESzDDoJ-HFwJFzN7ftsbsP892LMRZrmaLQ,19873
99
+ pyinfra/operations/mysql.py,sha256=ctm2Z6MaB0mOArCNU4TsJzaXiKXQaa_ahmsC5Vvyi10,19857
97
100
  pyinfra/operations/npm.py,sha256=bUmfQsClZ2YcHiihiC7k5widIXIi6lbfx_32iyaAKfo,1499
98
101
  pyinfra/operations/openrc.py,sha256=GXFoCHEEKeyQyRvrZcNYx8og4fmgmtzTVAViBzt84TE,1580
99
102
  pyinfra/operations/pacman.py,sha256=QMjmsBiiw362nhZY0rEDVQL5A32MG3u7GcmX4q4PzfI,1702
100
103
  pyinfra/operations/pip.py,sha256=7PpQvZHnwBGZ60V5b0XKNR4tHLW0MXJo6_6UX7HBtGY,5856
101
104
  pyinfra/operations/pkg.py,sha256=rORQBbKeb-6gS0LYu0a0VdiWcDZoovcUONCaf6KMdeQ,2298
102
105
  pyinfra/operations/pkgin.py,sha256=zhUyGzKjnUfGoyHbMoYMbeeMzcsiOUpBz1zIzppigJ0,1992
103
- pyinfra/operations/postgres.py,sha256=LRoedDevQqiM5eX5Lmzb5mr_E9Od0ROVC0j18ZqaR0w,9661
106
+ pyinfra/operations/postgres.py,sha256=eh3wjX-l4ri-q3mgfV2bdmVs3m87s3C1_EbJUFss9u4,9700
104
107
  pyinfra/operations/postgresql.py,sha256=agZjL2W4yxigk9ThIC0V_3wvmcWVdX308aJO24WkN6g,833
105
108
  pyinfra/operations/puppet.py,sha256=eDe8D9jQbHYQ4_r4-dmEZfMASKQvj36BR8z_h8aDfw8,861
106
109
  pyinfra/operations/python.py,sha256=u569cdPrPesrmzU09nwIPA3bk6TZ-Qv2QP0lJLcO_bw,2021
107
110
  pyinfra/operations/runit.py,sha256=jRR5kt1OUCLbYktnu7yl3YvSiTW51VvEvOuB0yfd7Ww,5126
108
111
  pyinfra/operations/selinux.py,sha256=imZ4dbY4tl0GpBSkUgV983jbDDihWNs_OQkOBulT7FQ,5948
109
- pyinfra/operations/server.py,sha256=cjQRZMYkSNKDwlKpTSj3iuJO3mNre0jR4R3MTIS_xIk,36462
112
+ pyinfra/operations/server.py,sha256=0xDKY9tM7ZvGYxj5zWppmP1fhLUBeGxSsnAxciGifnI,36572
110
113
  pyinfra/operations/snap.py,sha256=a-QtNE4Dlsavqq425TUIwpEJu4oGw8UlLRkdTFyT1F8,3049
111
114
  pyinfra/operations/ssh.py,sha256=wocoaYDlOhhItItAVQCEfnVowTtkg3AP0hQ3mnpUnl0,5634
112
115
  pyinfra/operations/systemd.py,sha256=hPHTjASj6N_fRAzLr3DNHnxxIbiiTIIT9UStSxKDkTk,3984
@@ -115,6 +118,7 @@ pyinfra/operations/upstart.py,sha256=pHb9RGnVhT14A_y6OezfOH-lmniKpiyJqpeoOJl0beE
115
118
  pyinfra/operations/vzctl.py,sha256=2u2CDkuDjzHBRQ54HfyfLpLrsbT8U7_05EEjbbhKUiU,3110
116
119
  pyinfra/operations/xbps.py,sha256=ru3_srMBUyUXGzAsPo7WwoomfM0AeDglFv8CDqB33B0,1508
117
120
  pyinfra/operations/yum.py,sha256=Ig7AzQy1C7I8XM37lWbw0nI5lzFGMoX30P8FV8-V5uA,5600
121
+ pyinfra/operations/zfs.py,sha256=CYaKf_Yealluo0suk9vuhxt-vEUlLfTVbAO35oalvT8,5271
118
122
  pyinfra/operations/zypper.py,sha256=z1CWv2uwWBlCLIhHna7U5DojVoKZYoUYpezJ_FM_xK8,5555
119
123
  pyinfra/operations/util/__init__.py,sha256=ZAHjeCXtLo0TIOSfZ9h0Sh5IXXRCspfHs3RR1l8tQCE,366
120
124
  pyinfra/operations/util/docker.py,sha256=6CvQgeFAXH_lDqKb7RxWpMvlCDwEAXlBaDZoJ8LxrYg,4596
@@ -125,11 +129,11 @@ pyinfra_cli/__init__.py,sha256=G0X7tNdqT45uWuK3aHIKxMdDeCgJ7zHo6vbxoG6zy_8,284
125
129
  pyinfra_cli/__main__.py,sha256=WlW7eP0rrL06eguuD_q2RAqgUjg3SW-QnmrayAh2mBQ,887
126
130
  pyinfra_cli/commands.py,sha256=J-mCJYvDebJ8M7o3HreB2zToa871-xO6_KjVhPLeHho,1832
127
131
  pyinfra_cli/exceptions.py,sha256=iptx9Zj1od7VgSbOyXs7P8tD4zAZ_fwrQFKPlpPrfS0,4806
128
- pyinfra_cli/inventory.py,sha256=S8aSl8TrF72Ni7eHiB40GjtAJeqyB1VxHobG2k254hQ,10329
132
+ pyinfra_cli/inventory.py,sha256=vuSL7dU31hxazHmJoUI0c6QjdItG78x8O5ifLUWuMeI,11292
129
133
  pyinfra_cli/log.py,sha256=7WEGtmf3ncF1BtXL2icUjyxeRKy-7XrCcQ2Hg4GWX5Y,2201
130
134
  pyinfra_cli/main.py,sha256=5VTniMcbKuIfjPTzaUklad5fM1BW7CUEARoSV9tPf1U,19954
131
- pyinfra_cli/prints.py,sha256=TsRxJRkkF-Trgbm4WgH8Vwr5PcelS5rokcqHDYc2kB0,9169
132
- pyinfra_cli/util.py,sha256=f3iGIPxlUiQJ5LmUGYbEz0QrySQAKmf9xov9WvHXbrk,6364
135
+ pyinfra_cli/prints.py,sha256=heCF-ugz0F8gTSr--rYVtRqN6jpAun5DUA4cy0F8l5A,9696
136
+ pyinfra_cli/util.py,sha256=s3-Y2AnRaLLULneaDw8YX1scWTFvGBPADPpYf3RtBms,6362
133
137
  pyinfra_cli/virtualenv.py,sha256=6j9W54JkQLN02SrZZIVwszp0GxlaaDEUWFZjBDHIWNA,2466
134
138
  tests/test_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
139
  tests/test_api/test_api.py,sha256=Ig2ebkNACYbHcC4_zRkxS9vj5ZEogoPqGx30ErIKChg,2413
@@ -139,15 +143,15 @@ tests/test_api/test_api_config.py,sha256=bf0mDrUie3On6zGC_hJBpv-wvSf3LHBIBzUDvko
139
143
  tests/test_api/test_api_deploys.py,sha256=h_zbI6CK4K8SdzEr3LEAMPxOf9hnQBdi_suqiNPqHHQ,4200
140
144
  tests/test_api/test_api_facts.py,sha256=WnKwgLq7sk2LNO5IgIZbO5HRkDr-2GdUWO_EFfTjhO8,10695
141
145
  tests/test_api/test_api_host.py,sha256=U_VW2vTl35vR8EdyIGMKr4y0ydsDLbvHSjZDa99CyNE,1119
142
- tests/test_api/test_api_inventory.py,sha256=VLbV0MXdRLOPvTXJF156ne6rAx1cBlFfgq_1S79s4tw,2013
146
+ tests/test_api/test_api_inventory.py,sha256=rqXd3e_Wwc-SxCzxgR5eLd7ZOdrF8CcHbcTZndLy5gE,2744
143
147
  tests/test_api/test_api_operations.py,sha256=GUfnuHK2NoTAGdOT4AbytT9R8i3ZZIvGP7KBfoYcYUQ,20134
144
148
  tests/test_api/test_api_util.py,sha256=uHv4oLpoy1_tzOoqFA1zpdvC74SvjitZbxQwp0dmjTs,1716
145
149
  tests/test_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
146
150
  tests/test_cli/test_cli.py,sha256=IeWuhkhLzIkRbOEx5-yaW6xV5l4Y8fxaGaDGlMcOyYE,6016
147
151
  tests/test_cli/test_cli_deploy.py,sha256=3tlXpN_ntCvZDeymfQKrZm0kgADkiLUIAGQg5V8-KrU,5079
148
- tests/test_cli/test_cli_exceptions.py,sha256=02sjC6rMptuqchgcdjdsVNQbSQYW6HwGutSy6Q6sMs4,3088
152
+ tests/test_cli/test_cli_exceptions.py,sha256=QaUv40q6Tp0HdcVEvPggFF4dsP2qdy57y9VAcGcR1So,3060
149
153
  tests/test_cli/test_cli_inventory.py,sha256=xlo-p3HdfVPNqxi7SknEZ2mWrKsdDaK3PoVN-tl95Z0,2394
150
- tests/test_cli/test_cli_util.py,sha256=-Ehnj0cO-EkF-6KLxcPPcFeuAUMTz-fKITrxhuiYhV4,2562
154
+ tests/test_cli/test_cli_util.py,sha256=Fqn_9fnG6xNfw8OMHSZs9hQoW260aSt409S1bUAJs-Q,2500
151
155
  tests/test_cli/test_context_objects.py,sha256=JiUTwQP7yvcqA47Kq9jtdsB_Z8nxGMZN46d9pR--FYA,2130
152
156
  tests/test_cli/util.py,sha256=kp_-XsGnTyDgG6IHWorYzl5VD_WLe77dKOH007TDOUE,338
153
157
  tests/test_connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -157,12 +161,12 @@ tests/test_connectors/test_dockerssh.py,sha256=MaC9IK1OZDiqoIsuLOZBJnPDglsMoPDoL
157
161
  tests/test_connectors/test_local.py,sha256=N_FkejDZKu7XLnKeApqfBARYMyxf-hRXCQJrXLHvwRg,7442
158
162
  tests/test_connectors/test_ssh.py,sha256=zYL0FbRXzqkYJslhmVeUgSkcHtozhmvZfRcaqDrYKvI,40386
159
163
  tests/test_connectors/test_sshuserclient.py,sha256=2PQNLPhNL6lBACc6tQuXmPoog-9L6AdDQNrA-rEw1_8,5734
160
- tests/test_connectors/test_terraform.py,sha256=Z5MhgDeRDFumu-GlbjMD0ZRkecwBIPP8C8ZVg-mq7C8,3743
164
+ tests/test_connectors/test_terraform.py,sha256=RZInSjes394eR5CrGGEjzZEFY-UpQj47n4MZH0_ExyY,3779
161
165
  tests/test_connectors/test_util.py,sha256=hQir0WyjH0LEF6xvIyHNyqdI5pkJX6qUR9287MgO2bY,4647
162
166
  tests/test_connectors/test_vagrant.py,sha256=27qRB7ftjEPaj4ejBNZ-rR4Ou1AD1VyVcf2XjwZPG3M,3640
163
- pyinfra-3.0.2.dist-info/LICENSE.md,sha256=gwC95tUll0gwB32tHNkTAasN7Sb6vjWzXa305NwClbI,1076
164
- pyinfra-3.0.2.dist-info/METADATA,sha256=CeBR5TFvTUFHJfa9l6NgUHm0SKfUXaZLWdAxi921QaU,8041
165
- pyinfra-3.0.2.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
166
- pyinfra-3.0.2.dist-info/entry_points.txt,sha256=BraEFyquy05M8ch33HZXOHoH_m2BTqejL3xX3NrpzOM,471
167
- pyinfra-3.0.2.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
168
- pyinfra-3.0.2.dist-info/RECORD,,
167
+ pyinfra-3.1.1.dist-info/LICENSE.md,sha256=gwC95tUll0gwB32tHNkTAasN7Sb6vjWzXa305NwClbI,1076
168
+ pyinfra-3.1.1.dist-info/METADATA,sha256=j3fopNCs_edZ2bzLHKAjoCv7_1CFph0D5q56yBJa-sg,8041
169
+ pyinfra-3.1.1.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
170
+ pyinfra-3.1.1.dist-info/entry_points.txt,sha256=BraEFyquy05M8ch33HZXOHoH_m2BTqejL3xX3NrpzOM,471
171
+ pyinfra-3.1.1.dist-info/top_level.txt,sha256=2K6D1mK35JTSEBgOfEPV-N-uA2SDErxGiE0J-HUMMVI,26
172
+ pyinfra-3.1.1.dist-info/RECORD,,
pyinfra_cli/inventory.py CHANGED
@@ -6,6 +6,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union
6
6
 
7
7
  from pyinfra import logger
8
8
  from pyinfra.api.inventory import Inventory
9
+ from pyinfra.connectors.sshuserclient.client import get_ssh_config
9
10
  from pyinfra.context import ctx_inventory
10
11
 
11
12
  from .exceptions import CliError
@@ -88,7 +89,34 @@ def _resolves_to_host(maybe_host: str) -> bool:
88
89
  socket.getaddrinfo(maybe_host, port=None)
89
90
  return True
90
91
  except socket.gaierror:
91
- return False
92
+ alias = _get_ssh_alias(maybe_host)
93
+ if not alias:
94
+ return False
95
+
96
+ try:
97
+ socket.getaddrinfo(alias, port=None)
98
+ return True
99
+ except socket.gaierror:
100
+ return False
101
+
102
+
103
+ def _get_ssh_alias(maybe_host: str) -> Optional[str]:
104
+ logger.debug('Checking if "%s" is an SSH alias', maybe_host)
105
+
106
+ # Note this does not cover the case where `host.data.ssh_config_file` is used
107
+ ssh_config = get_ssh_config()
108
+
109
+ if ssh_config is None:
110
+ logger.debug("Could not load SSH config")
111
+ return None
112
+
113
+ options = ssh_config.lookup(maybe_host)
114
+ alias = options.get("hostname")
115
+
116
+ if alias is None or maybe_host == alias:
117
+ return None
118
+
119
+ return alias
92
120
 
93
121
 
94
122
  def make_inventory(
@@ -105,7 +133,11 @@ def make_inventory(
105
133
  # (1) an inventory file is a common use case and (2) no other option can have a comma or an @
106
134
  # symbol in them.
107
135
  is_path_or_host_list_or_connector = (
108
- path.exists(inventory) or "," in inventory or "@" in inventory
136
+ path.exists(inventory)
137
+ or "," in inventory
138
+ or "@" in inventory
139
+ # Special case: passing an arbitrary name and specifying --data ssh_hostname=a.b.c
140
+ or (override_data is not None and "ssh_hostname" in override_data)
109
141
  )
110
142
  if not is_path_or_host_list_or_connector:
111
143
  # Next, try loading the inventory from a python function. This happens before checking for a
pyinfra_cli/prints.py CHANGED
@@ -131,6 +131,10 @@ def print_facts(facts):
131
131
 
132
132
 
133
133
  def print_support_info():
134
+ from importlib.metadata import PackageNotFoundError, requires, version
135
+
136
+ from packaging.requirements import Requirement
137
+
134
138
  click.echo(
135
139
  """
136
140
  If you are having issues with pyinfra or wish to make feature requests, please
@@ -144,6 +148,18 @@ def print_support_info():
144
148
  click.echo(" Release: {0}".format(platform.uname()[2]), err=True)
145
149
  click.echo(" Machine: {0}".format(platform.uname()[4]), err=True)
146
150
  click.echo(" pyinfra: v{0}".format(__version__), err=True)
151
+
152
+ for requirement_string in sorted(requires("pyinfra") or []):
153
+ requirement = Requirement(requirement_string)
154
+ try:
155
+ click.echo(
156
+ " {0}: v{1}".format(requirement.name, version(requirement.name)),
157
+ err=True,
158
+ )
159
+ except PackageNotFoundError:
160
+ # package not installed in this environment
161
+ continue
162
+
147
163
  click.echo(" Executable: {0}".format(sys.argv[0]), err=True)
148
164
  click.echo(
149
165
  " Python: {0} ({1}, {2})".format(
pyinfra_cli/util.py CHANGED
@@ -180,13 +180,13 @@ def try_import_module_attribute(path, prefix=None, raise_for_none=True):
180
180
 
181
181
  if module is None:
182
182
  if raise_for_none:
183
- raise CliError(f"No such module: {possible_modules[-1]}")
183
+ raise CliError(f"No such module: {possible_modules[0]}")
184
184
  return
185
185
 
186
186
  attr = getattr(module, attr_name, None)
187
187
  if attr is None:
188
188
  if raise_for_none:
189
- raise CliError(f"No such attribute in module {possible_modules[-1]}: {attr_name}")
189
+ raise CliError(f"No such attribute in module {possible_modules[0]}: {attr_name}")
190
190
  return
191
191
 
192
192
  return attr