diff --git a/salt/states/file.py b/salt/states/file.py index a9287dce22b5..e895b361b202 100644 --- a/salt/states/file.py +++ b/salt/states/file.py @@ -2311,8 +2311,10 @@ def managed(name, If ``True``, files managed using ``contents``, ``contents_pillar``, or ``contents_grains`` will have a newline added to the end of the file if - one is not present. Setting this option to ``False`` will omit this - final newline. + one is not present. Setting this option to ``False`` will ensure the + final line, or entry, does not contain a new line. If the last line, or + entry in the file does contain a new line already, this option will not + remove it. contents_delimiter .. versionadded:: 2015.8.4 @@ -2635,8 +2637,11 @@ def managed(name, for part in validated_contents: for line in part.splitlines(): contents += line.rstrip('\n').rstrip('\r') + os.linesep - if contents_newline and not contents.endswith(os.linesep): - contents += os.linesep + if not contents_newline: + # If contents newline is set to False, strip out the newline + # character and carriage return character + contents = contents.rstrip('\n').rstrip('\r') + except UnicodeDecodeError: # Either something terrible happened, or we have binary data. if template: diff --git a/tests/integration/states/test_file.py b/tests/integration/states/test_file.py index 521fd52b176e..fcd53b43b117 100644 --- a/tests/integration/states/test_file.py +++ b/tests/integration/states/test_file.py @@ -575,6 +575,68 @@ def test_managed_contents(self): if os.path.exists(managed_files[typ]): os.remove(managed_files[typ]) + def test_managed_contents_with_contents_newline(self): + ''' + test file.managed with contents by using the default content_newline + flag. + ''' + contents = 'test_managed_contents_with_newline_one' + name = os.path.join(TMP, 'foo') + + # Create a file named foo with contents as above but with a \n at EOF + self.run_state('file.managed', name=name, contents=contents, + contents_newline=True) + with salt.utils.files.fopen(name, 'r') as fp_: + last_line = fp_.read() + self.assertEqual((contents + os.linesep), last_line) + + def test_managed_contents_with_contents_newline_false(self): + ''' + test file.managed with contents by using the non default content_newline + flag. + ''' + contents = 'test_managed_contents_with_newline_one' + name = os.path.join(TMP, 'bar') + + # Create a file named foo with contents as above but with a \n at EOF + self.run_state('file.managed', name=name, contents=contents, + contents_newline=False) + with salt.utils.files.fopen(name, 'r') as fp_: + last_line = fp_.read() + self.assertEqual(contents, last_line) + + def test_managed_multiline_contents_with_contents_newline(self): + ''' + test file.managed with contents by using the non default content_newline + flag. + ''' + contents = ('this is a cookie{}this is another cookie'. + format(os.linesep)) + name = os.path.join(TMP, 'bar') + + # Create a file named foo with contents as above but with a \n at EOF + self.run_state('file.managed', name=name, contents=contents, + contents_newline=True) + with salt.utils.files.fopen(name, 'r') as fp_: + last_line = fp_.read() + self.assertEqual((contents + os.linesep), last_line) + + def test_managed_multiline_contents_with_contents_newline_false(self): + ''' + test file.managed with contents by using the non default content_newline + flag. + ''' + contents = ('this is a cookie{}this is another cookie'. + format(os.linesep)) + name = os.path.join(TMP, 'bar') + + # Create a file named foo with contents as above but with a \n at EOF + self.run_state('file.managed', name=name, contents=contents, + contents_newline=False) + with salt.utils.files.fopen(name, 'r') as fp_: + last_line = fp_.read() + self.assertEqual(contents, last_line) + @skip_if_not_root @skipIf(IS_WINDOWS, 'Windows does not support "mode" kwarg. Skipping.') @skipIf(not salt.utils.path.which('visudo'), 'sudo is missing')