diff --git a/changelog/63673.changed.md b/changelog/63673.changed.md new file mode 100644 index 000000000000..941c9cf0287b --- /dev/null +++ b/changelog/63673.changed.md @@ -0,0 +1 @@ +Improved the "Cannot extend ID" compiler error to identify the duplicate state IDs when an extend or require_in target matches more than one state's ``name`` parameter, instead of returning the generic "not part of the high state" message. diff --git a/salt/state.py b/salt/state.py index 28d7fc06a5f8..7a607f481cb6 100644 --- a/salt/state.py +++ b/salt/state.py @@ -1799,7 +1799,7 @@ def reconcile_extend(self, high: HighData, strict=False): if name not in high or state_type not in high[name]: # Check for a matching 'name' override in high data ids = find_name(name, state_type, high, strict=strict) - if len(ids) != 1: + if len(ids) == 0: errors.append( "Cannot extend ID '{0}' in '{1}:{2}'. It is not " "part of the high state.\n" @@ -1813,6 +1813,22 @@ def reconcile_extend(self, high: HighData, strict=False): ) ) continue + elif len(ids) > 1: + errors.append( + "Cannot extend ID '{0}' in '{1}:{2}'. It is not " + "unique in the running high state.\n" + "This is likely due to more than one state using " + "the same `name` parameter. Ensure that\nthe " + "state with an ID of '{0}' is unique in " + "environment '{1}' and to SLS '{2}'.\nDuplicate " + "states IDs are: '{3}'".format( + name, + body.get("__env__", "base"), + body.get("__sls__", "base"), + ", ".join(f"{state}: {id}" for id, state in ids), + ) + ) + continue else: name = ids[0][0] diff --git a/tests/pytests/functional/modules/state/requisites/test_require.py b/tests/pytests/functional/modules/state/requisites/test_require.py index 9198b8496daa..1582799eea8d 100644 --- a/tests/pytests/functional/modules/state/requisites/test_require.py +++ b/tests/pytests/functional/modules/state/requisites/test_require.py @@ -361,6 +361,44 @@ def test_requisites_require_ordering_and_errors_5(state, state_tree): assert ret.errors == [errmsg] +def test_requisites_require_ordering_duplicate_extend_id(state, state_tree): + """ + Regression test for PR #63673. + + When the target of an extend (or a require_in, which desugars to extend) + cannot be resolved to a single state because multiple states share the + same ``name`` parameter, the compiler should fail with a "not unique" + error that names every duplicate, instead of the older generic + "not part of the high state" message. + """ + sls_contents = """ + state_a: + test.succeed_without_changes: + - name: duplicate-target + + state_b: + test.succeed_without_changes: + - name: duplicate-target + + trigger: + test.succeed_without_changes: + - require_in: + - test: duplicate-target + """ + errmsg = ( + "Cannot extend ID 'duplicate-target' in 'base:requisite'. It is not" + " unique in the running high state.\nThis is likely due to more than" + " one state using the same `name` parameter. Ensure that\nthe state" + " with an ID of 'duplicate-target' is unique in environment 'base'" + " and to SLS 'requisite'.\nDuplicate states IDs are:" + " 'test: state_a, test: state_b'" + ) + with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): + ret = state.sls("requisite") + assert ret.failed + assert ret.errors == [errmsg] + + def test_requisites_require_with_order_first_last(state, state_tree): """ Call sls file containing a state with require_in order first