diff --git a/flux_local/values.py b/flux_local/values.py index a8ee86a3..eb341740 100644 --- a/flux_local/values.py +++ b/flux_local/values.py @@ -174,6 +174,22 @@ def _lookup_value_reference( return found_value +def _deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]: + """Recursively merge two dictionaries, similar to how Helm merges values. Lists are replaced entirely (Helm behavior).""" + result = base.copy() + for key, override_value in override.items(): + base_value = result.get(key) + if ( + base_value is not None + and isinstance(base_value, dict) + and isinstance(override_value, dict) + ): + result[key] = _deep_merge(base_value, override_value) + else: + result[key] = override_value + return result + + def _update_helmrelease_values( ref: ValuesReference, found_value: str, @@ -196,11 +212,14 @@ def _update_helmrelease_values( inner_values[parts[-1]] = found_value else: obj = yaml.load(found_value, Loader=yaml.SafeLoader) - if not obj or not isinstance(obj, dict): + # Handle empty YAML file case + if obj is None: + obj = {} + if not isinstance(obj, dict): raise InputException( f"Expected '{ref.name}' field '{ref.target_path}' values to be valid yaml, found {type(values)}" ) - values.update(obj) + values = _deep_merge(values, obj) return values diff --git a/tests/testdata/cluster8/apps/podinfo-values.yaml b/tests/testdata/cluster8/apps/podinfo-values.yaml index 1c6a9b8b..b4f3e312 100644 --- a/tests/testdata/cluster8/apps/podinfo-values.yaml +++ b/tests/testdata/cluster8/apps/podinfo-values.yaml @@ -9,7 +9,7 @@ data: redis: enabled: true repository: public.ecr.aws/docker/library/redis - tag: 7.0.6 + tag: 7.0.5 ingress: enabled: true className: nginx @@ -18,3 +18,7 @@ data: paths: - path: / pathType: ImplementationSpecific + empty-values.yaml: "" + patch-values.yaml: |- + redis: + tag: 7.0.6 diff --git a/tests/testdata/cluster8/apps/podinfo.yaml b/tests/testdata/cluster8/apps/podinfo.yaml index 20389087..214f6378 100644 --- a/tests/testdata/cluster8/apps/podinfo.yaml +++ b/tests/testdata/cluster8/apps/podinfo.yaml @@ -31,6 +31,12 @@ spec: valuesFrom: - kind: ConfigMap name: podinfo-values + - kind: ConfigMap + name: podinfo-values + valuesKey: empty-values.yaml + - kind: ConfigMap + name: podinfo-values + valuesKey: patch-values.yaml - kind: Secret name: podinfo-tls-values valuesKey: crt diff --git a/tests/tool/__snapshots__/test_build.ambr b/tests/tool/__snapshots__/test_build.ambr index 4d71a5c9..db48e61c 100644 --- a/tests/tool/__snapshots__/test_build.ambr +++ b/tests/tool/__snapshots__/test_build.ambr @@ -948,6 +948,12 @@ valuesFrom: - kind: ConfigMap name: podinfo-values + - kind: ConfigMap + name: podinfo-values + valuesKey: empty-values.yaml + - kind: ConfigMap + name: podinfo-values + valuesKey: patch-values.yaml - kind: Secret name: podinfo-tls-values optional: true @@ -956,11 +962,15 @@ --- apiVersion: v1 data: + empty-values.yaml: "" + patch-values.yaml: |- + redis: + tag: 7.0.6 values.yaml: |- redis: enabled: true repository: public.ecr.aws/docker/library/redis - tag: 7.0.6 + tag: 7.0.5 ingress: enabled: true className: nginx @@ -6333,6 +6343,12 @@ valuesFrom: - kind: ConfigMap name: podinfo-values + - kind: ConfigMap + name: podinfo-values + valuesKey: empty-values.yaml + - kind: ConfigMap + name: podinfo-values + valuesKey: patch-values.yaml - kind: Secret name: podinfo-tls-values optional: true @@ -6341,11 +6357,15 @@ --- apiVersion: v1 data: + empty-values.yaml: "" + patch-values.yaml: |- + redis: + tag: 7.0.6 values.yaml: |- redis: enabled: true repository: public.ecr.aws/docker/library/redis - tag: 7.0.6 + tag: 7.0.5 ingress: enabled: true className: nginx