Skip to content

Commit edd96fc

Browse files
authored
Fix apply_overrides to support nested template overrides in spec.yaml (#23348)
* Fix apply_overrides to support nested template overrides in spec.yaml When a spec.yaml template reference has an overrides: block targeting a field that lives inside a nested template (e.g. tags inside instances/default -> instances/all_integrations -> instances/tags), apply_overrides previously could not find the field because it only searched the raw (unexpanded) template list. The new _expand_and_find helper recursively loads nested template refs in-place, allowing any named field at any nesting depth to be reached from a single overrides: block in spec.yaml. * Fix ruff formatting in template.py and test_template.py
1 parent f7406de commit edd96fc

3 files changed

Lines changed: 71 additions & 10 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix `apply_overrides` to support nested template references in spec.yaml overrides.

datadog_checks_dev/datadog_checks/dev/tooling/configuration/template.py

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,45 @@ def load(self, template):
7575

7676
return data
7777

78-
@staticmethod
79-
def apply_overrides(template, overrides):
78+
def _expand_and_find(self, items: list, name: str, visited: set | None = None) -> dict | None:
79+
"""Find a named item in a list, expanding nested template refs in-place as needed.
80+
81+
When the named item lives inside a nested ``- template:`` reference, that reference
82+
is loaded and spliced into ``items`` in-place so that subsequent spec.py processing
83+
sees the already-expanded data with any overrides applied.
84+
"""
85+
if visited is None:
86+
visited = set()
87+
88+
for item in items:
89+
if isinstance(item, dict) and item.get('name') == name:
90+
return item
91+
92+
for idx, item in enumerate(items):
93+
if not isinstance(item, dict) or 'name' in item or 'template' not in item:
94+
continue
95+
tmpl_name = item['template']
96+
if tmpl_name in visited:
97+
continue
98+
visited.add(tmpl_name)
99+
try:
100+
nested = self.load(tmpl_name)
101+
except Exception:
102+
continue
103+
104+
if isinstance(nested, dict):
105+
if nested.get('name') == name:
106+
items[idx] = nested
107+
return nested
108+
elif isinstance(nested, list):
109+
found = self._expand_and_find(nested, name, visited)
110+
if found is not None:
111+
items[idx : idx + 1] = nested
112+
return found
113+
114+
return None
115+
116+
def apply_overrides(self, template, overrides):
80117
errors = []
81118

82119
for override, value in sorted(overrides.items()):
@@ -100,10 +137,9 @@ def apply_overrides(template, overrides):
100137
)
101138
break
102139
elif isinstance(root, list):
103-
for item in root:
104-
if isinstance(item, dict) and item.get('name') == key:
105-
root = item
106-
break
140+
found = self._expand_and_find(root, key)
141+
if found is not None:
142+
root = found
107143
else:
108144
intermediate_error = (
109145
f"Template override `{'.'.join(override_keys[:i])}` has no named mapping `{key}`"
@@ -123,10 +159,12 @@ def apply_overrides(template, overrides):
123159
if isinstance(root, dict):
124160
root[final_key] = value
125161
elif isinstance(root, list):
126-
for i, item in enumerate(root):
127-
if isinstance(item, dict) and item.get('name') == final_key:
128-
root[i] = value
129-
break
162+
found = self._expand_and_find(root, final_key)
163+
if found is not None:
164+
for i, item in enumerate(root):
165+
if item is found:
166+
root[i] = value
167+
break
130168
else:
131169
intermediate_error = 'Template override has no named mapping `{}`'.format(
132170
'.'.join(override_keys) if override_keys else override

datadog_checks_dev/tests/tooling/configuration/test_template.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,25 @@ def test_primitive_recurse(self):
230230

231231
assert len(errors) == 1
232232
assert errors[0] == 'Template override `proxy.description` does not refer to a mapping'
233+
234+
def test_nested_template_override(self):
235+
"""Overrides should reach fields in nested template refs."""
236+
templates = ConfigTemplates()
237+
238+
# instances/default → instances/all_integrations → instances/tags (tags field)
239+
template = templates.load('instances/default')
240+
errors = templates.apply_overrides(template, {'tags.display_priority': 5})
241+
assert not errors
242+
243+
tags_item = next((item for item in template if isinstance(item, dict) and item.get('name') == 'tags'), None)
244+
assert tags_item is not None
245+
assert tags_item['display_priority'] == 5
246+
247+
def test_nested_template_override_not_found(self):
248+
"""An override for a name absent from nested templates still reports an error."""
249+
templates = ConfigTemplates()
250+
251+
template = templates.load('instances/default')
252+
errors = templates.apply_overrides(template, {'nonexistent_field.display_priority': 5})
253+
254+
assert len(errors) == 1

0 commit comments

Comments
 (0)