diff --git a/salt/states/file.py b/salt/states/file.py index 8e992b746e21..3173bafb38c7 100644 --- a/salt/states/file.py +++ b/salt/states/file.py @@ -2570,8 +2570,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 @@ -2894,8 +2896,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 c1a0b1793585..ea4f2bf21f49 100644 --- a/tests/integration/states/test_file.py +++ b/tests/integration/states/test_file.py @@ -573,6 +573,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')