diff --git a/changelog/66440.fixed.md b/changelog/66440.fixed.md new file mode 100644 index 00000000000..d81a5712420 --- /dev/null +++ b/changelog/66440.fixed.md @@ -0,0 +1 @@ +Fixed ``TypeError: argument of type 'NoneType' is not iterable`` in beacon state functions (``present``, ``absent``, ``enabled``, ``disabled``) when ``beacons.list`` returns ``None`` due to event-system failure. The return value is now defaulted to an empty dict before iteration. diff --git a/salt/states/beacon.py b/salt/states/beacon.py index b83717ae2bd..a867b6e5c61 100644 --- a/salt/states/beacon.py +++ b/salt/states/beacon.py @@ -99,6 +99,8 @@ def present(name, save=False, **kwargs): ret = {"name": name, "result": True, "changes": {}, "comment": []} current_beacons = __salt__["beacons.list"](return_yaml=False, **kwargs) + if current_beacons is None: + current_beacons = {} beacon_data = [{k: v} for k, v in kwargs.items()] if name in current_beacons: @@ -172,6 +174,8 @@ def absent(name, save=False, **kwargs): ret = {"name": name, "result": True, "changes": {}, "comment": []} current_beacons = __salt__["beacons.list"](return_yaml=False, **kwargs) + if current_beacons is None: + current_beacons = {} if name in current_beacons: if __opts__.get("test"): kwargs["test"] = True @@ -219,6 +223,8 @@ def enabled(name, **kwargs): ret = {"name": name, "result": True, "changes": {}, "comment": []} current_beacons = __salt__["beacons.list"](return_yaml=False, **kwargs) + if current_beacons is None: + current_beacons = {} if name in current_beacons: if __opts__.get("test"): kwargs["test"] = True @@ -259,6 +265,8 @@ def disabled(name, **kwargs): ret = {"name": name, "result": True, "changes": {}, "comment": []} current_beacons = __salt__["beacons.list"](return_yaml=False, **kwargs) + if current_beacons is None: + current_beacons = {} if name in current_beacons: if __opts__.get("test"): kwargs["test"] = True diff --git a/tests/pytests/unit/states/test_beacon.py b/tests/pytests/unit/states/test_beacon.py index a393190ec07..5f1f8499dd4 100644 --- a/tests/pytests/unit/states/test_beacon.py +++ b/tests/pytests/unit/states/test_beacon.py @@ -41,6 +41,29 @@ def test_present(): assert beacon.present(beacon_name) == ret +def test_present_none_beacons(): + """ + Test present when beacons.list returns None. + """ + beacon_name = "test_beacon" + + mock_mod = MagicMock( + return_value={"name": beacon_name, "result": True, "changes": {}, "comment": []} + ) + mock_lst = MagicMock(return_value=None) + with patch.dict( + beacon.__salt__, + { + "beacons.list": mock_lst, + "beacons.add": mock_mod, + }, + ): + with patch.dict(beacon.__opts__, {"test": False}): + ret = beacon.present(beacon_name) + assert ret["result"] is True + assert ret["comment"] == f"Adding {beacon_name} to beacons" + + def test_absent(): """ Test to ensure a job is absent from the schedule. @@ -61,3 +84,22 @@ def test_absent(): comt = "ps not configured in beacons" ret.update({"comment": comt, "result": True}) assert beacon.absent(beacon_name) == ret + + +def test_absent_none_beacons(): + """ + Test absent when beacons.list returns None. + """ + beacon_name = "test_beacon" + + mock_lst = MagicMock(return_value=None) + with patch.dict( + beacon.__salt__, + { + "beacons.list": mock_lst, + }, + ): + with patch.dict(beacon.__opts__, {"test": False}): + ret = beacon.absent(beacon_name) + assert ret["result"] is True + assert f"{beacon_name} not configured in beacons" in ret["comment"]