pyinfra 2.9.2__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.2.dist-info → pyinfra-3.0.dist-info}/METADATA +40 -41
- pyinfra-3.0.dist-info/RECORD +167 -0
- {pyinfra-2.9.2.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.2.dist-info/RECORD +0 -170
- pyinfra-2.9.2.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.2.dist-info → pyinfra-3.0.dist-info}/LICENSE.md +0 -0
- {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,551 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
The windows_files module handles windows filesystem state, file uploads and template generation.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import ntpath
|
|
6
|
-
import os
|
|
7
|
-
from datetime import timedelta
|
|
8
|
-
|
|
9
|
-
from pyinfra import host, state
|
|
10
|
-
from pyinfra.api import FileUploadCommand, OperationError, OperationTypeError, operation
|
|
11
|
-
from pyinfra.api.util import get_file_sha1
|
|
12
|
-
from pyinfra.facts.windows import Date
|
|
13
|
-
from pyinfra.facts.windows_files import Directory, File, Link, Md5File, Sha1File, Sha256File
|
|
14
|
-
|
|
15
|
-
from .util.files import ensure_mode_int
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@operation(
|
|
19
|
-
pipeline_facts={"file": "dest"},
|
|
20
|
-
)
|
|
21
|
-
def download(
|
|
22
|
-
src,
|
|
23
|
-
dest,
|
|
24
|
-
user=None,
|
|
25
|
-
group=None,
|
|
26
|
-
mode=None,
|
|
27
|
-
cache_time=None,
|
|
28
|
-
force=False,
|
|
29
|
-
sha256sum=None,
|
|
30
|
-
sha1sum=None,
|
|
31
|
-
md5sum=None,
|
|
32
|
-
):
|
|
33
|
-
"""
|
|
34
|
-
Download files from remote locations using curl or wget.
|
|
35
|
-
|
|
36
|
-
+ src: source URL of the file
|
|
37
|
-
+ dest: where to save the file
|
|
38
|
-
+ user: user to own the files
|
|
39
|
-
+ group: group to own the files
|
|
40
|
-
+ mode: permissions of the files
|
|
41
|
-
+ cache_time: if the file exists already, re-download after this time (in seconds)
|
|
42
|
-
+ force: always download the file, even if it already exists
|
|
43
|
-
+ sha256sum: sha256 hash to checksum the downloaded file against
|
|
44
|
-
+ sha1sum: sha1 hash to checksum the downloaded file against
|
|
45
|
-
+ md5sum: md5 hash to checksum the downloaded file against
|
|
46
|
-
|
|
47
|
-
**Example:**
|
|
48
|
-
|
|
49
|
-
.. code:: python
|
|
50
|
-
|
|
51
|
-
windows_files.download(
|
|
52
|
-
name="Download the Docker repo file",
|
|
53
|
-
src="https://download.docker.com/linux/centos/docker-ce.repo",
|
|
54
|
-
dest="C:\\docker",
|
|
55
|
-
)
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
|
-
info = host.get_fact(File, path=dest)
|
|
59
|
-
# Destination is a directory?
|
|
60
|
-
if info is False:
|
|
61
|
-
raise OperationError(
|
|
62
|
-
"Destination {0} already exists and is not a file".format(dest),
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
# Do we download the file? Force by default
|
|
66
|
-
download = force
|
|
67
|
-
|
|
68
|
-
# Doesn't exist, lets download it
|
|
69
|
-
if info is None:
|
|
70
|
-
download = True
|
|
71
|
-
|
|
72
|
-
# Destination file exists & cache_time: check when the file was last modified,
|
|
73
|
-
# download if old
|
|
74
|
-
else:
|
|
75
|
-
if cache_time:
|
|
76
|
-
# Time on files is not tz-aware, and will be the same tz as the server's time,
|
|
77
|
-
# so we can safely remove the tzinfo from Date before comparison.
|
|
78
|
-
cache_time = host.get_fact(Date).replace(tzinfo=None) - timedelta(seconds=cache_time)
|
|
79
|
-
if info["mtime"] and info["mtime"] > cache_time:
|
|
80
|
-
download = True
|
|
81
|
-
|
|
82
|
-
if sha1sum:
|
|
83
|
-
if sha1sum != host.get_fact(Sha1File, path=dest):
|
|
84
|
-
download = True
|
|
85
|
-
|
|
86
|
-
if sha256sum:
|
|
87
|
-
if sha256sum != host.get_fact(Sha256File, path=dest):
|
|
88
|
-
download = True
|
|
89
|
-
|
|
90
|
-
if md5sum:
|
|
91
|
-
if md5sum != host.get_fact(Md5File, path=dest):
|
|
92
|
-
download = True
|
|
93
|
-
|
|
94
|
-
# If we download, always do user/group/mode as SSH user may be different
|
|
95
|
-
if download:
|
|
96
|
-
yield (
|
|
97
|
-
'$ProgressPreference = "SilentlyContinue"; ' "Invoke-WebRequest -Uri {0} -OutFile {1}"
|
|
98
|
-
).format(src, dest)
|
|
99
|
-
|
|
100
|
-
# if user or group:
|
|
101
|
-
# yield chown(dest, user, group)
|
|
102
|
-
|
|
103
|
-
# if mode:
|
|
104
|
-
# yield chmod(dest, mode)
|
|
105
|
-
|
|
106
|
-
if sha1sum:
|
|
107
|
-
yield (
|
|
108
|
-
'if ((Get-FileHash -Algorithm SHA1 "{0}").hash -ne {1}) {{ '
|
|
109
|
-
'Write-Error "SHA1 did not match!" '
|
|
110
|
-
"}}"
|
|
111
|
-
).format(dest, sha1sum)
|
|
112
|
-
|
|
113
|
-
if sha256sum:
|
|
114
|
-
yield (
|
|
115
|
-
'if ((Get-FileHash -Algorithm SHA256 "{0}").hash -ne {1}) {{ '
|
|
116
|
-
'Write-Error "SHA256 did not match!" '
|
|
117
|
-
"}}"
|
|
118
|
-
).format(dest, sha256sum)
|
|
119
|
-
|
|
120
|
-
if md5sum:
|
|
121
|
-
yield (
|
|
122
|
-
'if ((Get-FileHash -Algorithm MD5 "{0}").hash -ne {1}) {{ '
|
|
123
|
-
'Write-Error "MD5 did not match!" '
|
|
124
|
-
"}}"
|
|
125
|
-
).format(dest, md5sum)
|
|
126
|
-
|
|
127
|
-
else:
|
|
128
|
-
host.noop("file {0} has already been downloaded".format(dest))
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
@operation(
|
|
132
|
-
pipeline_facts={
|
|
133
|
-
"file": "dest",
|
|
134
|
-
"sha1_file": "dest",
|
|
135
|
-
},
|
|
136
|
-
)
|
|
137
|
-
def put(
|
|
138
|
-
src,
|
|
139
|
-
dest,
|
|
140
|
-
user=None,
|
|
141
|
-
group=None,
|
|
142
|
-
mode=None,
|
|
143
|
-
add_deploy_dir=True,
|
|
144
|
-
create_remote_dir=True,
|
|
145
|
-
force=False,
|
|
146
|
-
assume_exists=False,
|
|
147
|
-
):
|
|
148
|
-
"""
|
|
149
|
-
Upload a local file to the remote system.
|
|
150
|
-
|
|
151
|
-
+ src: local filename to upload
|
|
152
|
-
+ dest: remote filename to upload to
|
|
153
|
-
+ user: user to own the files
|
|
154
|
-
+ group: group to own the files
|
|
155
|
-
+ mode: permissions of the files
|
|
156
|
-
+ add_deploy_dir: src is relative to the deploy directory
|
|
157
|
-
+ create_remote_dir: create the remote directory if it doesn't exist
|
|
158
|
-
+ force: always upload the file, even if the remote copy matches
|
|
159
|
-
+ assume_exists: whether to assume the local file exists
|
|
160
|
-
|
|
161
|
-
``create_remote_dir``:
|
|
162
|
-
If the remote directory does not exist it will be created using the same
|
|
163
|
-
user & group as passed to ``files.put``. The mode will *not* be copied over,
|
|
164
|
-
if this is required call ``files.directory`` separately.
|
|
165
|
-
|
|
166
|
-
Note:
|
|
167
|
-
This operation is not suitable for large files as it may involve copying
|
|
168
|
-
the file before uploading it.
|
|
169
|
-
|
|
170
|
-
**Examples:**
|
|
171
|
-
|
|
172
|
-
.. code:: python
|
|
173
|
-
|
|
174
|
-
# Note: This requires a 'files/motd' file on the local filesystem
|
|
175
|
-
files.put(
|
|
176
|
-
name="Update the message of the day file",
|
|
177
|
-
src="data/content.json",
|
|
178
|
-
dest="C:\\data\\content.json",
|
|
179
|
-
)
|
|
180
|
-
"""
|
|
181
|
-
|
|
182
|
-
# Upload IO objects as-is
|
|
183
|
-
if hasattr(src, "read"):
|
|
184
|
-
local_file = src
|
|
185
|
-
|
|
186
|
-
# Assume string filename
|
|
187
|
-
else:
|
|
188
|
-
# Add deploy directory?
|
|
189
|
-
if add_deploy_dir and state.cwd:
|
|
190
|
-
src = os.path.join(state.cwd, src)
|
|
191
|
-
|
|
192
|
-
local_file = src
|
|
193
|
-
|
|
194
|
-
if not assume_exists and not os.path.isfile(local_file):
|
|
195
|
-
raise IOError("No such file: {0}".format(local_file))
|
|
196
|
-
|
|
197
|
-
mode = ensure_mode_int(mode)
|
|
198
|
-
remote_file = host.get_fact(File, path=dest)
|
|
199
|
-
|
|
200
|
-
if create_remote_dir:
|
|
201
|
-
yield from _create_remote_dir(state, host, dest, user, group)
|
|
202
|
-
|
|
203
|
-
# No remote file, always upload and user/group/mode if supplied
|
|
204
|
-
if not remote_file or force:
|
|
205
|
-
yield FileUploadCommand(
|
|
206
|
-
local_file,
|
|
207
|
-
dest,
|
|
208
|
-
remote_temp_filename=state.get_temp_filename(dest),
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
# if user or group:
|
|
212
|
-
# yield chown(dest, user, group)
|
|
213
|
-
|
|
214
|
-
# if mode:
|
|
215
|
-
# yield chmod(dest, mode)
|
|
216
|
-
|
|
217
|
-
# File exists, check sum and check user/group/mode if supplied
|
|
218
|
-
else:
|
|
219
|
-
local_sum = get_file_sha1(src)
|
|
220
|
-
remote_sum = host.get_fact(Sha1File, path=dest)
|
|
221
|
-
|
|
222
|
-
# Check sha1sum, upload if needed
|
|
223
|
-
if local_sum != remote_sum:
|
|
224
|
-
yield FileUploadCommand(
|
|
225
|
-
local_file,
|
|
226
|
-
dest,
|
|
227
|
-
remote_temp_filename=state.get_temp_filename(dest),
|
|
228
|
-
)
|
|
229
|
-
|
|
230
|
-
# if user or group:
|
|
231
|
-
# yield chown(dest, user, group)
|
|
232
|
-
|
|
233
|
-
# if mode:
|
|
234
|
-
# yield chmod(dest, mode)
|
|
235
|
-
|
|
236
|
-
else:
|
|
237
|
-
changed = False
|
|
238
|
-
|
|
239
|
-
# Check mode
|
|
240
|
-
# if mode and remote_file['mode'] != mode:
|
|
241
|
-
# yield chmod(dest, mode)
|
|
242
|
-
# changed = True
|
|
243
|
-
|
|
244
|
-
# Check user/group
|
|
245
|
-
# if (
|
|
246
|
-
# (user and remote_file['user'] != user)
|
|
247
|
-
# or (group and remote_file['group'] != group)
|
|
248
|
-
# ):
|
|
249
|
-
# yield chown(dest, user, group)
|
|
250
|
-
# changed = True
|
|
251
|
-
|
|
252
|
-
if not changed:
|
|
253
|
-
host.noop("file {0} is already uploaded".format(dest))
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
@operation(
|
|
257
|
-
pipeline_facts={"windows_file": "name"},
|
|
258
|
-
)
|
|
259
|
-
def file(
|
|
260
|
-
path,
|
|
261
|
-
present=True,
|
|
262
|
-
assume_present=False,
|
|
263
|
-
user=None,
|
|
264
|
-
group=None,
|
|
265
|
-
mode=None,
|
|
266
|
-
touch=False,
|
|
267
|
-
create_remote_dir=True,
|
|
268
|
-
):
|
|
269
|
-
"""
|
|
270
|
-
Add/remove/update files.
|
|
271
|
-
|
|
272
|
-
+ path: path of the remote file
|
|
273
|
-
+ present: whether the file should exist
|
|
274
|
-
+ assume_present: whether to assume the file exists
|
|
275
|
-
+ TODO: user: user to own the files
|
|
276
|
-
+ TODO: group: group to own the files
|
|
277
|
-
+ TODO: mode: permissions of the files as an integer, eg: 755
|
|
278
|
-
+ touch: whether to touch the file
|
|
279
|
-
+ create_remote_dir: create the remote directory if it doesn't exist
|
|
280
|
-
|
|
281
|
-
``create_remote_dir``:
|
|
282
|
-
If the remote directory does not exist it will be created using the same
|
|
283
|
-
user & group as passed to ``files.put``. The mode will *not* be copied over,
|
|
284
|
-
if this is required call ``files.directory`` separately.
|
|
285
|
-
|
|
286
|
-
**Example:**
|
|
287
|
-
|
|
288
|
-
.. code:: python
|
|
289
|
-
|
|
290
|
-
files.file(
|
|
291
|
-
name="Create c:\\temp\\hello.txt",
|
|
292
|
-
path="c:\\temp\\hello.txt",
|
|
293
|
-
touch=True,
|
|
294
|
-
)
|
|
295
|
-
"""
|
|
296
|
-
|
|
297
|
-
if not isinstance(path, str):
|
|
298
|
-
raise OperationTypeError("Name must be a string")
|
|
299
|
-
|
|
300
|
-
# mode = ensure_mode_int(mode)
|
|
301
|
-
info = host.get_fact(File, path=path)
|
|
302
|
-
|
|
303
|
-
# Not a file?!
|
|
304
|
-
if info is False:
|
|
305
|
-
raise OperationError("{0} exists and is not a file".format(path))
|
|
306
|
-
|
|
307
|
-
# Doesn't exist & we want it
|
|
308
|
-
if not assume_present and info is None and present:
|
|
309
|
-
if create_remote_dir:
|
|
310
|
-
yield from _create_remote_dir(state, host, path, user, group)
|
|
311
|
-
|
|
312
|
-
yield "New-Item -ItemType file {0}".format(path)
|
|
313
|
-
|
|
314
|
-
# if mode:
|
|
315
|
-
# yield chmod(path, mode)
|
|
316
|
-
# if user or group:
|
|
317
|
-
# yield chown(path, user, group)
|
|
318
|
-
|
|
319
|
-
# It exists and we don't want it
|
|
320
|
-
elif (assume_present or info) and not present:
|
|
321
|
-
yield "Remove-Item {0}".format(path)
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
# # It exists & we want to ensure its state
|
|
325
|
-
# elif (assume_present or info) and present:
|
|
326
|
-
# if touch:
|
|
327
|
-
# yield 'New-Item -ItemType file {0}'.format(path)
|
|
328
|
-
#
|
|
329
|
-
# # Check mode
|
|
330
|
-
# if mode and (not info or info['mode'] != mode):
|
|
331
|
-
# yield chmod(path, mode)
|
|
332
|
-
#
|
|
333
|
-
# # Check user/group
|
|
334
|
-
# if (
|
|
335
|
-
# (not info and (user or group))
|
|
336
|
-
# or (user and info['user'] != user)
|
|
337
|
-
# or (group and info['group'] != group)
|
|
338
|
-
# ):
|
|
339
|
-
# yield chown(path, user, group)
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
def _create_remote_dir(state, host, remote_filename, user, group):
|
|
343
|
-
# Always use POSIX style path as local might be Windows, remote always *nix
|
|
344
|
-
remote_dirname = ntpath.dirname(remote_filename)
|
|
345
|
-
if remote_dirname:
|
|
346
|
-
yield from directory(
|
|
347
|
-
remote_dirname,
|
|
348
|
-
state=state,
|
|
349
|
-
host=host,
|
|
350
|
-
user=user,
|
|
351
|
-
group=group,
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
@operation(
|
|
356
|
-
pipeline_facts={"windows_directory": "name"},
|
|
357
|
-
)
|
|
358
|
-
def directory(
|
|
359
|
-
path,
|
|
360
|
-
present=True,
|
|
361
|
-
assume_present=False,
|
|
362
|
-
user=None,
|
|
363
|
-
group=None,
|
|
364
|
-
mode=None,
|
|
365
|
-
recursive=False,
|
|
366
|
-
):
|
|
367
|
-
"""
|
|
368
|
-
Add/remove/update directories.
|
|
369
|
-
|
|
370
|
-
+ path: path of the remote folder
|
|
371
|
-
+ present: whether the folder should exist
|
|
372
|
-
+ assume_present: whether to assume the directory exists
|
|
373
|
-
+ TODO: user: user to own the folder
|
|
374
|
-
+ TODO: group: group to own the folder
|
|
375
|
-
+ TODO: mode: permissions of the folder
|
|
376
|
-
+ TODO: recursive: recursively apply user/group/mode
|
|
377
|
-
|
|
378
|
-
**Examples:**
|
|
379
|
-
|
|
380
|
-
.. code:: python
|
|
381
|
-
|
|
382
|
-
files.directory(
|
|
383
|
-
name="Ensure the c:\\temp\\dir_that_we_want_removed is removed",
|
|
384
|
-
path="c:\\temp\\dir_that_we_want_removed",
|
|
385
|
-
present=False,
|
|
386
|
-
)
|
|
387
|
-
|
|
388
|
-
files.directory(
|
|
389
|
-
name="Ensure c:\\temp\\foo\\foo_dir exists",
|
|
390
|
-
path="c:\\temp\\foo\\foo_dir",
|
|
391
|
-
recursive=True,
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
# multiple directories
|
|
395
|
-
dirs = ["c:\\temp\\foo_dir1", "c:\\temp\\foo_dir2"]
|
|
396
|
-
for dir in dirs:
|
|
397
|
-
files.directory(
|
|
398
|
-
name="Ensure the directory `{}` exists".format(dir),
|
|
399
|
-
path=dir,
|
|
400
|
-
)
|
|
401
|
-
|
|
402
|
-
"""
|
|
403
|
-
|
|
404
|
-
if not isinstance(path, str):
|
|
405
|
-
raise OperationTypeError("Name must be a string")
|
|
406
|
-
|
|
407
|
-
info = host.get_fact(Directory, path=path)
|
|
408
|
-
|
|
409
|
-
# Not a directory?!
|
|
410
|
-
if info is False:
|
|
411
|
-
raise OperationError("{0} exists and is not a directory".format(path))
|
|
412
|
-
|
|
413
|
-
# Doesn't exist & we want it
|
|
414
|
-
if not assume_present and info is None and present:
|
|
415
|
-
yield "New-Item -Path {0} -ItemType Directory".format(path)
|
|
416
|
-
# if mode:
|
|
417
|
-
# yield chmod(path, mode, recursive=recursive)
|
|
418
|
-
# if user or group:
|
|
419
|
-
# yield chown(path, user, group, recursive=recursive)
|
|
420
|
-
#
|
|
421
|
-
# Somewhat bare fact, should flesh out more
|
|
422
|
-
host.create_fact(
|
|
423
|
-
Date,
|
|
424
|
-
kwargs={"path": path},
|
|
425
|
-
data={"type": "directory"},
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
# It exists and we don't want it
|
|
429
|
-
elif (assume_present or info) and not present:
|
|
430
|
-
# TODO: how to ensure we use 'ps'?
|
|
431
|
-
# remove anything in the directory
|
|
432
|
-
yield "Get-ChildItem {0} -Recurse | Remove-Item".format(path)
|
|
433
|
-
# remove directory
|
|
434
|
-
yield "Remove-Item {0}".format(path)
|
|
435
|
-
|
|
436
|
-
# It exists & we want to ensure its state
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
# elif (assume_present or info) and present:
|
|
440
|
-
# # Check mode
|
|
441
|
-
# if mode and (not info or info['mode'] != mode):
|
|
442
|
-
# yield chmod(path, mode, recursive=recursive)
|
|
443
|
-
#
|
|
444
|
-
# # Check user/group
|
|
445
|
-
# if (
|
|
446
|
-
# (not info and (user or group))
|
|
447
|
-
# or (user and info['user'] != user)
|
|
448
|
-
# or (group and info['group'] != group)
|
|
449
|
-
# ):
|
|
450
|
-
# yield chown(path, user, group, recursive=recursive)
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
def _validate_path(path):
|
|
454
|
-
try:
|
|
455
|
-
path = os.fspath(path)
|
|
456
|
-
except TypeError:
|
|
457
|
-
raise OperationTypeError("`path` must be a string or `os.PathLike` object")
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
@operation(
|
|
461
|
-
pipeline_facts={"link": "path"},
|
|
462
|
-
)
|
|
463
|
-
def link(
|
|
464
|
-
path,
|
|
465
|
-
target=None,
|
|
466
|
-
present=True,
|
|
467
|
-
assume_present=False,
|
|
468
|
-
user=None,
|
|
469
|
-
group=None,
|
|
470
|
-
symbolic=True,
|
|
471
|
-
force=True,
|
|
472
|
-
create_remote_dir=True,
|
|
473
|
-
):
|
|
474
|
-
"""
|
|
475
|
-
Add/remove/update links.
|
|
476
|
-
|
|
477
|
-
+ path: the name of the link
|
|
478
|
-
+ target: the file/directory the link points to
|
|
479
|
-
+ present: whether the link should exist
|
|
480
|
-
+ assume_present: whether to assume the link exists
|
|
481
|
-
+ user: user to own the link
|
|
482
|
-
+ group: group to own the link
|
|
483
|
-
+ symbolic: whether to make a symbolic link (vs hard link)
|
|
484
|
-
+ create_remote_dir: create the remote directory if it doesn't exist
|
|
485
|
-
|
|
486
|
-
``create_remote_dir``:
|
|
487
|
-
If the remote directory does not exist it will be created using the same
|
|
488
|
-
user & group as passed to ``files.put``. The mode will *not* be copied over,
|
|
489
|
-
if this is required call ``files.directory`` separately.
|
|
490
|
-
|
|
491
|
-
Source changes:
|
|
492
|
-
If the link exists and points to a different target, pyinfra will remove it and
|
|
493
|
-
recreate a new one pointing to then new target.
|
|
494
|
-
|
|
495
|
-
**Examples:**
|
|
496
|
-
|
|
497
|
-
.. code:: python
|
|
498
|
-
|
|
499
|
-
# simple example showing how to link to a file
|
|
500
|
-
files.link(
|
|
501
|
-
name=r"Create link C:\\issue2 that points to C:\\issue",
|
|
502
|
-
path=r"C:\\issue2",
|
|
503
|
-
target=r"C\\issue",
|
|
504
|
-
)
|
|
505
|
-
"""
|
|
506
|
-
|
|
507
|
-
_validate_path(path)
|
|
508
|
-
|
|
509
|
-
if present and not target:
|
|
510
|
-
raise OperationError("If present is True target must be provided")
|
|
511
|
-
|
|
512
|
-
info = host.get_fact(Link, path=path)
|
|
513
|
-
|
|
514
|
-
# Not a link?
|
|
515
|
-
if info is not None and not info:
|
|
516
|
-
raise OperationError("{0} exists and is not a link".format(path))
|
|
517
|
-
|
|
518
|
-
add_cmd = "New-Item -ItemType {0} -Path {1} -Target {2} {3}".format(
|
|
519
|
-
"SymbolicLink" if symbolic else "HardLink",
|
|
520
|
-
path,
|
|
521
|
-
target,
|
|
522
|
-
"-Force" if force else "",
|
|
523
|
-
)
|
|
524
|
-
|
|
525
|
-
remove_cmd = "(Get-Item {0}).Delete()".format(path)
|
|
526
|
-
|
|
527
|
-
# We will attempt to link regardless of current existence
|
|
528
|
-
# since we know by now the path is either a link already
|
|
529
|
-
# or does not exist
|
|
530
|
-
if (info is None or force) and present:
|
|
531
|
-
if create_remote_dir:
|
|
532
|
-
yield from _create_remote_dir(state, host, path, user, group)
|
|
533
|
-
|
|
534
|
-
yield add_cmd
|
|
535
|
-
|
|
536
|
-
# if user or group:
|
|
537
|
-
# yield chown(path, user, group, dereference=False)
|
|
538
|
-
|
|
539
|
-
# host.create_fact(
|
|
540
|
-
# WindowsLink,
|
|
541
|
-
# kwargs={'name': path},
|
|
542
|
-
# data={'link_target': target, 'group': group, 'user': user},
|
|
543
|
-
# )
|
|
544
|
-
|
|
545
|
-
# It exists and we don't want it
|
|
546
|
-
elif (assume_present or info) and not present:
|
|
547
|
-
yield remove_cmd
|
|
548
|
-
# host.delete_fact(WindowsLink, kwargs={'name': path})
|
|
549
|
-
|
|
550
|
-
else:
|
|
551
|
-
host.noop("link {0} already exists and force=False".format(path))
|