Skip to content

Commit 0b7ba83

Browse files
committed
[Deps-Test] Deep-merge injected ExtraChefAttributes with the cluster config's existing ones
A test feature (e.g. gb200) may already set DevSettings.Cookbook.ExtraChefAttributes on the cluster config. The previous 'inject only if absent' guard then skipped our attributes entirely( ExtraChefAttributes (python-version,nvidia, etc.) via --extra-chef-attributes), so cluster init used the cookbook default python and failed with 'Errno::ENOENT: .../pyenv/versions/3.14.2/.../python' (the AMI carries the upgraded python). Deep-merge instead: parse the existing JSON string, recursively merge our injected attributes under it, and re-serialize. The two dicts share branch keys (cluster, cluster.nvidia) but disjoint children, so a shallow merge would drop one subtree; recursion preserves both. Test-specific attributes win on leaf conflicts (existing passed as the override arg).
1 parent 37f52f2 commit 0b7ba83

1 file changed

Lines changed: 32 additions & 5 deletions

File tree

tests/integration-tests/conftest.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,22 @@ def inject_additional_image_configs_settings(image_config, request):
739739
yaml.dump(config_content, conf_file)
740740

741741

742+
def _deep_merge_dicts(base, override):
743+
"""Recursively merge override into base.
744+
745+
Branch keys present in both are merged (so disjoint children on a shared
746+
parent are preserved); on a leaf-value conflict the value from `override`
747+
wins. Callers choose the winner by argument order.
748+
"""
749+
result = copy.deepcopy(base)
750+
for key, value in override.items():
751+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
752+
result[key] = _deep_merge_dicts(result[key], value)
753+
else:
754+
result[key] = copy.deepcopy(value)
755+
return result
756+
757+
742758
def _inject_additional_iam_policies(node_config, additional_iam_policies):
743759
if dict_has_nested_key(node_config, ("Iam", "AdditionalIamPolicies")):
744760
for policy in additional_iam_policies:
@@ -800,11 +816,22 @@ def inject_additional_config_settings( # noqa C901
800816
("DevSettings", "Cookbook", "ChefCookbook"),
801817
)
802818

803-
if request.config.getoption("extra_chef_attributes") and not dict_has_nested_key(
804-
config_content, ("DevSettings", "Cookbook", "ExtraChefAttributes")
805-
):
806-
extra_chef = base64.b64decode(request.config.getoption("extra_chef_attributes")).decode("utf-8")
807-
dict_add_nested_key(config_content, extra_chef, ("DevSettings", "Cookbook", "ExtraChefAttributes"))
819+
if request.config.getoption("extra_chef_attributes"):
820+
# Deep-merge our ExtraChefAttributes into any already set on the cluster
821+
# config (e.g. a test feature like gb200 sets its own). Without merging,
822+
# the existing value would shadow the dependency-upgrade attributes
823+
# (python-version, etc.) and cluster init would use the cookbook default
824+
# python, failing with ENOENT on the venv path baked into the AMI.
825+
injected = json.loads(base64.b64decode(request.config.getoption("extra_chef_attributes")).decode("utf-8"))
826+
if dict_has_nested_key(config_content, ("DevSettings", "Cookbook", "ExtraChefAttributes")):
827+
existing = config_content["DevSettings"]["Cookbook"]["ExtraChefAttributes"]
828+
existing = json.loads(existing) if isinstance(existing, str) else existing
829+
# Test-specific attributes win on conflict; the injected dependency
830+
# attributes (e.g. python-version) fill in the rest.
831+
merged = _deep_merge_dicts(injected, existing)
832+
else:
833+
merged = injected
834+
dict_add_nested_key(config_content, json.dumps(merged), ("DevSettings", "Cookbook", "ExtraChefAttributes"))
808835

809836
if request.config.getoption("custom_ami") and not dict_has_nested_key(config_content, ("Image", "CustomAmi")):
810837
dict_add_nested_key(config_content, request.config.getoption("custom_ami"), ("Image", "CustomAmi"))

0 commit comments

Comments
 (0)