diff --git a/changelog/55269.fixed b/changelog/55269.fixed new file mode 100644 index 00000000000..42543738580 --- /dev/null +++ b/changelog/55269.fixed @@ -0,0 +1 @@ +Fixed malformed state return when testing file.managed with unavailable source file diff --git a/changelog/61814.fixed b/changelog/61814.fixed new file mode 100644 index 00000000000..6d06855ebb8 --- /dev/null +++ b/changelog/61814.fixed @@ -0,0 +1 @@ +Fixed malformed state return when merge-serializing to an improperly formatted file diff --git a/salt/states/file.py b/salt/states/file.py index e8eb4575add..41a550ff35f 100644 --- a/salt/states/file.py +++ b/salt/states/file.py @@ -3139,6 +3139,7 @@ def managed( if isinstance(ret["changes"], tuple): ret["result"], ret["comment"] = ret["changes"] + ret["changes"] = {} elif ret["changes"]: ret["result"] = None ret["comment"] = "The file {} is set to be changed".format(name) @@ -8004,7 +8005,7 @@ def serialize( ret["comment"] = "Failed to deserialize existing data: {}".format( exc ) - return False + return ret if existing_data is not None: merged_data = salt.utils.dictupdate.merge_recurse( diff --git a/tests/integration/states/test_file.py b/tests/integration/states/test_file.py index c5c9bdf21a5..0cb26b7951d 100644 --- a/tests/integration/states/test_file.py +++ b/tests/integration/states/test_file.py @@ -845,6 +845,26 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): # Check that we identified a hash mismatch self.assertIn("does not exist", ret["comment"]) + def test_test_managed_issue_55269(self): + """ + Make sure that we exit gracefully in case a local source does not exist + when file.managed is run with test=1. + """ + name = self.tmp_dir / "local_source_does_not_exist_testing" + self.addCleanup(salt.utils.files.safe_rm, str(name)) + local_path = os.path.join(RUNTIME_VARS.BASE_FILES, "grail", "scene99") + + for proto in ("file://", ""): + source = proto + local_path + log.debug("Trying source %s", source) + ret = self.run_state( + "file.managed", name=str(name), source=source, test=True + ) + self.assertSaltFalseReturn(ret) + ret = ret[next(iter(ret))] + self.assertFalse(ret["changes"]) + self.assertIn("does not exist".format(), ret["comment"]) + def test_managed_unicode_jinja_with_tojson_filter(self): """ Using {{ varname }} with a list or dictionary which contains unicode diff --git a/tests/pytests/unit/states/file/test_filestate.py b/tests/pytests/unit/states/file/test_filestate.py index ecbca012f59..6fb50a817bf 100644 --- a/tests/pytests/unit/states/file/test_filestate.py +++ b/tests/pytests/unit/states/file/test_filestate.py @@ -18,7 +18,7 @@ import salt.utils.platform import salt.utils.win_functions import salt.utils.yaml from salt.exceptions import CommandExecutionError -from tests.support.mock import MagicMock, Mock, patch +from tests.support.mock import MagicMock, Mock, mock_open, patch log = logging.getLogger(__name__) @@ -536,6 +536,26 @@ def test_serialize_into_managed_file(): ret.update({"comment": comt, "result": None}) assert filestate.serialize(name, dataset=True, formatter="python") == ret + # merge_if_exists deserialization error + mock_exception = MagicMock(side_effect=TypeError("test")) + with patch.object(os.path, "isfile", mock_t): + with patch.dict( + filestate.__serializers__, + { + "exception.serialize": mock_exception, + "exception.deserialize": mock_exception, + }, + ): + with patch.object(salt.utils.files, "fopen", mock_open(read_data="foo")): + comt = "Failed to deserialize existing data: test" + ret.update({"comment": comt, "result": False, "changes": {}}) + assert ( + filestate.serialize( + name, dataset=True, merge_if_exists=True, serializer="exception" + ) + == ret + ) + # 'mknod' function tests: 1 def test_mknod():