@@ -75,16 +75,14 @@ def _get_field_map(target: Any) -> Dict[str, Tuple[str, type]]:
7575 return mapping
7676
7777
78- def _load_from_mount (
79- base_volume_mount : str , module : str , instance : str , target : Any
80- ) -> None :
78+ def _load_from_path (secret_dir : str , target : Any ) -> None :
8179 """
82- Load secrets from files at:
83- {base_volume_mount}/{module}/{instance}/{field_key}
80+ Load secrets from files directly in secret_dir into target dataclass.
8481
85- Sets string attributes directly on the dataclass instance.
82+ Reads each field key as a file name under secret_dir. Used for both the
83+ servicebinding.io flat layout ($ROOT/<module>/<field>) and the legacy
84+ three-level layout via :func:`_load_from_mount`.
8685 """
87- secret_dir = os .path .join (base_volume_mount , module , instance )
8886 _validate_path (secret_dir )
8987
9088 field_map = _get_field_map (target )
@@ -106,6 +104,17 @@ def _load_from_mount(
106104 setattr (target , attr_name , content )
107105
108106
107+ def _load_from_mount (
108+ base_volume_mount : str , module : str , instance : str , target : Any
109+ ) -> None :
110+ """
111+ Load secrets from files at:
112+ {base_volume_mount}/{module}/{instance}/{field_key}
113+ """
114+ secret_dir = os .path .join (base_volume_mount , module , instance )
115+ _load_from_path (secret_dir , target )
116+
117+
109118def _load_from_env (base_var_name : str , module : str , instance : str , target : Any ) -> None :
110119 """
111120 Load secrets from environment variables with names:
@@ -133,17 +142,21 @@ def read_from_mount_and_fallback_to_env_var(
133142) -> None :
134143 """
135144 Load secrets for a given module and instance into the provided dataclass instance `target`.
136- Fallback order:
137- 1. Mounted volume path: {base_volume_mount}/{module}/{instance}/{field_key}
138- (``SERVICE_BINDING_ROOT`` env var overrides ``base_volume_mount`` — see
139- :func:`resolve_base_mount`)
145+
146+ Fallback order when ``SERVICE_BINDING_ROOT`` is set:
147+ 1. Flat path: {SERVICE_BINDING_ROOT}/{module}/{field_key} (servicebinding.io spec)
148+ 2. Full path: {SERVICE_BINDING_ROOT}/{module}/{instance}/{field_key} (legacy convention)
149+ 3. Environment variables: {base_var_name}_{module}_{instance}_{field_key} (uppercased)
150+
151+ Fallback order when ``SERVICE_BINDING_ROOT`` is **not** set:
152+ 1. Full path: {base_volume_mount}/{module}/{instance}/{field_key}
140153 2. Environment variables: {base_var_name}_{module}_{instance}_{field_key} (uppercased)
141154
142155 Raises:
143156 ValueError: If inputs are invalid or target is not a dataclass instance
144157 FileNotFoundError / NotADirectoryError / OSError: If mount path issues occur
145158 KeyError: If environment variables are missing on fallback
146- RuntimeError: If both mount and env var loading fail (aggregated error)
159+ RuntimeError: If all strategies fail (aggregated error)
147160 """
148161 _validate_inputs (module , instance )
149162
@@ -152,6 +165,15 @@ def read_from_mount_and_fallback_to_env_var(
152165 normalized_module = module .replace ("-" , "_" )
153166 normalized_instance = instance .replace ("-" , "_" )
154167
168+ # servicebinding.io: when SERVICE_BINDING_ROOT is explicitly set, try the flat path
169+ # $ROOT/<module>/<field> before the legacy $ROOT/<module>/<instance>/<field> path.
170+ if os .environ .get ("SERVICE_BINDING_ROOT" ) is not None :
171+ try :
172+ _load_from_path (os .path .join (resolved_base_path , module ), target )
173+ return
174+ except Exception as e :
175+ errors .append (f"mount failed: { e } ;" )
176+
155177 try :
156178 _load_from_mount (resolved_base_path , module , instance , target )
157179 return
0 commit comments