Estimated time to implement: Depends on choice selected
Motivation
Currently, we have several modules which deal specifically with file creation and others which create files as a byproduct of their operation. We should have consistent behaviour among them. This proposal is for consistent behaviour when a directory already exists at the location where the file is to be written.
Problems
We currently deal with a directory existing where we are told to create a file in several ways:
Overwrite the directory when empty; error if nonempty (file state=link|hard with force=True)
Error (file state=link|hard with force=False; file state=file)
Place the new file into the directory (copy, template, assemble)
There are numerous other modules which create files as a byproduct of their actions. (See any modules which have set the add_file_common_args=True parameter). These may error if the file is a directory or do something else.
There are pros and cons for each option:
Placing files into a new file within the directory is the way unix tools (cp, mv, ln) typically work so it's easy for people to understand how our modules should behave. (One of rsync's major drawbacks is that it does not work like standard unix tools. Can we learn not to make the same mistakes as rsync?)
Overwriting the directory always would allow declarative syntax for files (Example: stating that a file of type link should exist at /var/tmp) within a single command. Anything else requires "programming" in the playbook to achieve the same thing every single time someone wants to protect against a pre-existing directory. Something like:
- stat path=filename
- file state=absent path=filename
when: stat_results.isdir is True
- file: state=link path=filename
Overwriting only when the directory is empty was seen as a compromise that preserved the contents of non-empty directories (which might be hard to recreate) and yet sometimes allowing idempotent commands. I personally think this compromise satisfies no one (it is unintuitive for people to use and those who really want to overwrite directories still have to write the multiple tasks to protect against the case of non-empty directories)
Erroring is the least user friendly. Probably shouldn't be an option we adopt.
Solution proposal
All modules creating files should, by default, place a file inside of a directory if the dest is a directory. This makes the default intuitive to the users of standard user tools
Modules should have a new option: overwrite_directories(True|False) which allows overwriting both empty and non-empty directories to allow declarative syntax for creating files.
Note: jimi-c writes that we should not use force for this: https://github.com/ansible/ansible/issues/7254#issuecomment-42052103 I'm not sure that I agree with force being used to repoint a link. That seems like repointing should be the default. OTOH, overwriting directories does seem potentially destructive enough that a specific option with a descriptive name may be warranted? (Especially for non-file specific modules which may use force to mean something else).
For the release this becomes a feature, we will specifically do the following:
add overwrite_directories to the file module and making touch and file use that.
Change the behaviour of link and hard to not overwrite empty directories by default but place the link inside instead. If we want, we could instead make this continue with the old behaviour and return a deprecation warning for a deprecation cycle. Deprecation warning should tell people to use overwite_directories=True if it encounters a directory.
Update documentation and error messages to mention overwrite_directories
copy, template, and assemble will be updated to also have an overwrite_directories flag to allow replacing a directory instead of the present behaviour of placing the file inside of it.
Dependencies (optional)
Explain any dependencies. This section is optional but could be helpful.
For completion of the feature, the file, copy, template, assemble modules and their relevant integration tests will be updated
On an ongoing basis, other modules that are discovered may be updated to comply to this standard as well.
Modules may error if a dest is a directory or place the file within the directory with no over_write directories switch to change that until someone can fix them. Modules which overwrite a directory in some or all cases must be fixed ASAP.
We should move the detection of directories and choice to overwrite the directory or not into module_utils.
Testing (optional)
integration tests for file, copy, and assemble should be updated to test this case
pytest tests should be written for the functions added to module_utils
Documentation (optional)
Documentation of the new parameter is necessary.
Documentation of the change in file's behaviour must be added to the porting guide and to the file module documentation.
Anything else?
A few existing issues about the behaviour of file:
What should we do about file=touch? It currently operates on whatever it finds, applying permissions to it. If nothing is found, it creates a new file with those permissions. So in the case of a directory, it aplies permissions to it.
Proposal: Handle overwriting of directories in a consistent manner
Author: Toshio Kuratomi <@abadger> IRC: abadger1999
Date: 2017/04/28
Motivation
Currently, we have several modules which deal specifically with file creation and others which create files as a byproduct of their operation. We should have consistent behaviour among them. This proposal is for consistent behaviour when a directory already exists at the location where the file is to be written.
Problems
We currently deal with a directory existing where we are told to create a file in several ways:
There are numerous other modules which create files as a byproduct of their actions. (See any modules which have set the add_file_common_args=True parameter). These may error if the file is a directory or do something else.
There are pros and cons for each option:
Solution proposal
Dependencies (optional)
Explain any dependencies. This section is optional but could be helpful.
Testing (optional)
Documentation (optional)
Anything else?
A few existing issues about the behaviour of file:
https://github.com/ansible/ansible/issues/7254
https://github.com/ansible/ansible/pull/8329
What should we do about file=touch? It currently operates on whatever it finds, applying permissions to it. If nothing is found, it creates a new file with those permissions. So in the case of a directory, it aplies permissions to it.