Skip to content

Commit

Permalink
Add force & force_backup args to files.[file|directory|link] op…
Browse files Browse the repository at this point in the history
…erations. (#631)

* Add `force` & `force_backup` args to `files.[file|directory|link]` operations.

This makes it possible to have pyinfra move or remove an existing and
invalid path before creating the desired one.

* Add `force_backup_dir` argument to `files.[file|directory|link]` operations.

* Fix rebase error in config defaults.
  • Loading branch information
Fizzadar authored Jul 31, 2021
1 parent 579f3f6 commit 025e1db
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 3 deletions.
4 changes: 4 additions & 0 deletions pyinfra/api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
'DOAS': False,
'DOAS_USER': None,

# Use doas and optional user
'DOAS': False,
'DOAS_USER': None,

# Only show errors, but don't count as failure
'IGNORE_ERRORS': False,

Expand Down
40 changes: 37 additions & 3 deletions pyinfra/operations/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,19 @@ def _validate_path(path):
raise OperationTypeError('`path` must be a string or `os.PathLike` object')


def _raise_or_remove_invalid_path(fs_type, path, force, force_backup, force_backup_dir):
if force:
if force_backup:
backup_path = '{0}.{1}'.format(path, get_timestamp())
if force_backup_dir:
backup_path = '{0}/{1}'.format(force_backup_dir, backup_path)
yield 'mv {0} {1}'.format(path, backup_path)
else:
yield 'rm -rf {0}'.format(path)
else:
raise OperationError('{0} exists and is not a {1}'.format(path, fs_type))


@operation(pipeline_facts={
'link': 'path',
})
Expand All @@ -943,6 +956,7 @@ def link(
target=None, present=True, assume_present=False,
user=None, group=None, symbolic=True,
create_remote_dir=True,
force=False, force_backup=True, force_backup_dir=None,
state=None, host=None,
):
'''
Expand All @@ -956,6 +970,9 @@ def link(
+ group: group to own the link
+ symbolic: whether to make a symbolic link (vs hard link)
+ create_remote_dir: create the remote directory if it doesn't exist
+ force: if the target exists and is not a file, move or remove it and continue
+ force_backup: set to ``False`` to remove any existing non-file when ``force=True``
+ force_backup_dir: directory to move any backup to when ``force=True``
``create_remote_dir``:
If the remote directory does not exist it will be created using the same
Expand Down Expand Up @@ -1003,7 +1020,10 @@ def link(

# Not a link?
if info is False:
raise OperationError('{0} exists and is not a link'.format(path))
yield _raise_or_remove_invalid_path(
'link', path, force, force_backup, force_backup_dir,
)
info = None

add_args = ['ln']
if symbolic:
Expand Down Expand Up @@ -1086,6 +1106,7 @@ def file(
present=True, assume_present=False,
user=None, group=None, mode=None, touch=False,
create_remote_dir=True,
force=False, force_backup=True, force_backup_dir=None,
state=None, host=None,
):
'''
Expand All @@ -1099,6 +1120,9 @@ def file(
+ mode: permissions of the files as an integer, eg: 755
+ touch: whether to touch the file
+ create_remote_dir: create the remote directory if it doesn't exist
+ force: if the target exists and is not a file, move or remove it and continue
+ force_backup: set to ``False`` to remove any existing non-file when ``force=True``
+ force_backup_dir: directory to move any backup to when ``force=True``
``create_remote_dir``:
If the remote directory does not exist it will be created using the same
Expand Down Expand Up @@ -1128,7 +1152,10 @@ def file(

# Not a file?!
if info is False:
raise OperationError('{0} exists and is not a file'.format(path))
yield _raise_or_remove_invalid_path(
'file', path, force, force_backup, force_backup_dir,
)
info = None

# Doesn't exist & we want it
if not assume_present and info is None and present:
Expand Down Expand Up @@ -1195,6 +1222,7 @@ def directory(
path,
present=True, assume_present=False,
user=None, group=None, mode=None, recursive=False,
force=False, force_backup=True, force_backup_dir=None,
_no_check_owner_mode=False,
_no_fail_on_link=False,
state=None, host=None,
Expand All @@ -1209,6 +1237,9 @@ def directory(
+ group: group to own the folder
+ mode: permissions of the folder
+ recursive: recursively apply user/group/mode
+ force: if the target exists and is not a file, move or remove it and continue
+ force_backup: set to ``False`` to remove any existing non-file when ``force=True``
+ force_backup_dir: directory to move any backup to when ``force=True``
Examples:
Expand Down Expand Up @@ -1246,7 +1277,10 @@ def directory(
if _no_fail_on_link and host.get_fact(Link, path=path):
host.noop('directory {0} already exists (as a link)'.format(path))
return
raise OperationError('{0} exists and is not a directory'.format(path))
yield _raise_or_remove_invalid_path(
'directory', path, force, force_backup, force_backup_dir,
)
info = None

# Doesn't exist & we want it
if not assume_present and info is None and present:
Expand Down
15 changes: 15 additions & 0 deletions tests/operations/files.directory/add_force.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"args": ["testdir"],
"kwargs": {
"force": true
},
"facts": {
"files.Directory": {
"path=testdir": false
}
},
"commands": [
"mv testdir testdir.a-timestamp",
"mkdir -p testdir"
]
}
16 changes: 16 additions & 0 deletions tests/operations/files.directory/add_force_backup_dir.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"args": ["testdir"],
"kwargs": {
"force": true,
"force_backup_dir": "/tmp"
},
"facts": {
"files.Directory": {
"path=testdir": false
}
},
"commands": [
"mv testdir /tmp/testdir.a-timestamp",
"mkdir -p testdir"
]
}
16 changes: 16 additions & 0 deletions tests/operations/files.directory/add_force_no_backup.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"args": ["testdir"],
"kwargs": {
"force": true,
"force_backup": false
},
"facts": {
"files.Directory": {
"path=testdir": false
}
},
"commands": [
"rm -rf testdir",
"mkdir -p testdir"
]
}
15 changes: 15 additions & 0 deletions tests/operations/files.file/add_force.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"args": ["testfile"],
"kwargs": {
"force": true
},
"facts": {
"files.File": {
"path=testfile": false
}
},
"commands": [
"mv testfile testfile.a-timestamp",
"touch testfile"
]
}
16 changes: 16 additions & 0 deletions tests/operations/files.file/add_force_backup_dir.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"args": ["testfile"],
"kwargs": {
"force": true,
"force_backup_dir": "/tmp/somewhere"
},
"facts": {
"files.File": {
"path=testfile": false
}
},
"commands": [
"mv testfile /tmp/somewhere/testfile.a-timestamp",
"touch testfile"
]
}
16 changes: 16 additions & 0 deletions tests/operations/files.file/add_force_no_backup.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"args": ["testfile"],
"kwargs": {
"force": true,
"force_backup": false
},
"facts": {
"files.File": {
"path=testfile": false
}
},
"commands": [
"rm -rf testfile",
"touch testfile"
]
}
16 changes: 16 additions & 0 deletions tests/operations/files.link/add_force.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"args": ["testlink"],
"kwargs": {
"target": "/etc/init.d/nginx",
"force": true
},
"facts": {
"files.Link": {
"path=testlink": false
}
},
"commands": [
"mv testlink testlink.a-timestamp",
"ln -s /etc/init.d/nginx testlink"
]
}
17 changes: 17 additions & 0 deletions tests/operations/files.link/add_force_backup_dir.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"args": ["testlink"],
"kwargs": {
"target": "/etc/init.d/nginx",
"force": true,
"force_backup_dir": "/tmp/somewhere"
},
"facts": {
"files.Link": {
"path=testlink": false
}
},
"commands": [
"mv testlink /tmp/somewhere/testlink.a-timestamp",
"ln -s /etc/init.d/nginx testlink"
]
}
17 changes: 17 additions & 0 deletions tests/operations/files.link/add_force_no_backup.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"args": ["testlink"],
"kwargs": {
"target": "/etc/init.d/nginx",
"force": true,
"force_backup": false
},
"facts": {
"files.Link": {
"path=testlink": false
}
},
"commands": [
"rm -rf testlink",
"ln -s /etc/init.d/nginx testlink"
]
}

0 comments on commit 025e1db

Please sign in to comment.