diff --git a/src/sap_cloud_sdk/core/auditlog/config.py b/src/sap_cloud_sdk/core/auditlog/config.py index 745a497..cd0524f 100644 --- a/src/sap_cloud_sdk/core/auditlog/config.py +++ b/src/sap_cloud_sdk/core/auditlog/config.py @@ -109,11 +109,7 @@ def _load_config_from_env() -> AuditLogConfig: binding_data: BindingData = BindingData("", "") read_from_mount_and_fallback_to_env_var( - base_volume_mount="/etc/secrets/appfnd", - base_var_name="CLOUD_SDK_CFG", - module="auditlog", - instance="default", - target=binding_data, + module="auditlog", instance="default", target=binding_data ) binding_data.validate() diff --git a/src/sap_cloud_sdk/core/secret_resolver/constants.py b/src/sap_cloud_sdk/core/secret_resolver/constants.py index 7d66cf4..a338b18 100644 --- a/src/sap_cloud_sdk/core/secret_resolver/constants.py +++ b/src/sap_cloud_sdk/core/secret_resolver/constants.py @@ -3,3 +3,4 @@ """ BASE_MOUNT_PATH = "/etc/secrets/appfnd" +CLOUD_SDK_ENV_PREFIX = "CLOUD_SDK_CFG" diff --git a/src/sap_cloud_sdk/core/secret_resolver/resolver.py b/src/sap_cloud_sdk/core/secret_resolver/resolver.py index fa16322..1693ebc 100644 --- a/src/sap_cloud_sdk/core/secret_resolver/resolver.py +++ b/src/sap_cloud_sdk/core/secret_resolver/resolver.py @@ -5,7 +5,7 @@ import os from dataclasses import fields, is_dataclass from typing import Any, Dict, Tuple -from .constants import BASE_MOUNT_PATH +from .constants import BASE_MOUNT_PATH, CLOUD_SDK_ENV_PREFIX def resolve_base_mount(base_volume_mount: str = BASE_MOUNT_PATH) -> str: @@ -125,11 +125,11 @@ def _load_from_env(base_var_name: str, module: str, instance: str, target: Any) def read_from_mount_and_fallback_to_env_var( - base_volume_mount: str, - base_var_name: str, module: str, instance: str, target: Any, + base_volume_mount: str = BASE_MOUNT_PATH, + base_var_name: str = CLOUD_SDK_ENV_PREFIX, ) -> None: """ Load secrets for a given module and instance into the provided dataclass instance `target`. diff --git a/src/sap_cloud_sdk/destination/config.py b/src/sap_cloud_sdk/destination/config.py index e0fce1c..b48a65a 100644 --- a/src/sap_cloud_sdk/destination/config.py +++ b/src/sap_cloud_sdk/destination/config.py @@ -132,8 +132,6 @@ def load_from_env_or_mount(instance: Optional[str] = None) -> DestinationConfig: # 1) Try mount at /etc/secrets/appfnd/destination/{instance}/... # 2) Fallback to env: CLOUD_SDK_CFG_DESTINATION_{INSTANCE}_{FIELD_KEY} read_from_mount_and_fallback_to_env_var( - base_volume_mount="/etc/secrets/appfnd", - base_var_name="CLOUD_SDK_CFG", module="destination", instance=inst, target=binding, diff --git a/src/sap_cloud_sdk/dms/config.py b/src/sap_cloud_sdk/dms/config.py index f7ad14c..ed647ae 100644 --- a/src/sap_cloud_sdk/dms/config.py +++ b/src/sap_cloud_sdk/dms/config.py @@ -121,8 +121,6 @@ def load_sdm_config_from_env_or_mount(instance: Optional[str] = None) -> DMSCred # 1) Try mount at /etc/secrets/appfnd/destination/{instance}/... # 2) Fallback to env: CLOUD_SDK_CFG_SDM_{INSTANCE}_{FIELD_KEY} read_from_mount_and_fallback_to_env_var( - base_volume_mount="/etc/secrets/appfnd", - base_var_name="CLOUD_SDK_CFG", module="sdm", instance=inst, target=binding, diff --git a/src/sap_cloud_sdk/objectstore/__init__.py b/src/sap_cloud_sdk/objectstore/__init__.py index 770c1ad..42ac6df 100644 --- a/src/sap_cloud_sdk/objectstore/__init__.py +++ b/src/sap_cloud_sdk/objectstore/__init__.py @@ -54,8 +54,6 @@ def create_client( # Cloud mode: use secret resolver to load configuration config = ObjectStoreBindingData() read_from_mount_and_fallback_to_env_var( - base_volume_mount="/etc/secrets/appfnd", - base_var_name="CLOUD_SDK_CFG", module="objectstore", instance=instance, target=config, diff --git a/tests/core/unit/auditlog/unit/test_config.py b/tests/core/unit/auditlog/unit/test_config.py index 63cc797..2019c44 100644 --- a/tests/core/unit/auditlog/unit/test_config.py +++ b/tests/core/unit/auditlog/unit/test_config.py @@ -216,8 +216,6 @@ def mock_read_side_effect(*args, **kwargs): assert config.service_url == "https://service.example.com" mock_read.assert_called_once_with( - base_volume_mount="/etc/secrets/appfnd", - base_var_name="CLOUD_SDK_CFG", module="auditlog", instance="default", target=mock_read.call_args.kwargs["target"] diff --git a/tests/core/unit/secret_resolver/unit/test_secret_resolver.py b/tests/core/unit/secret_resolver/unit/test_secret_resolver.py index 8afe074..819db80 100644 --- a/tests/core/unit/secret_resolver/unit/test_secret_resolver.py +++ b/tests/core/unit/secret_resolver/unit/test_secret_resolver.py @@ -25,21 +25,21 @@ class TestSecretResolver: def test_validate_inputs_empty_module(self): config = SampleConfig() with pytest.raises(ValueError, match="module name cannot be empty"): - read_from_mount_and_fallback_to_env_var("/path", "VAR", "", "instance", config) + read_from_mount_and_fallback_to_env_var("", "instance", config, base_volume_mount="/path", base_var_name="VAR") def test_validate_inputs_empty_instance(self): config = SampleConfig() with pytest.raises(ValueError, match="instance name cannot be empty"): - read_from_mount_and_fallback_to_env_var("/path", "VAR", "module", "", config) + read_from_mount_and_fallback_to_env_var("module", "", config, base_volume_mount="/path", base_var_name="VAR") def test_non_dataclass_target(self): with pytest.raises(RuntimeError, match="failed to read secrets.*target must be a dataclass instance"): - read_from_mount_and_fallback_to_env_var("/path", "VAR", "module", "instance", "not_dataclass") + read_from_mount_and_fallback_to_env_var("module", "instance", "not_dataclass", base_volume_mount="/path", base_var_name="VAR") def test_non_string_field_error(self): config = NonStringConfig() with pytest.raises(RuntimeError, match="failed to read secrets.*is not a string"): - read_from_mount_and_fallback_to_env_var("/path", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/path", base_var_name="VAR") @patch('os.path.isdir', return_value=True) @patch('os.stat') @@ -52,7 +52,7 @@ def test_load_from_mount_success(self, mock_file, mock_stat, mock_isdir): ] config = SampleConfig() - read_from_mount_and_fallback_to_env_var("/secrets", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/secrets", base_var_name="VAR") assert config.username == "test_user" assert config.password == "test_pass" @@ -64,20 +64,20 @@ def test_load_from_mount_success(self, mock_file, mock_stat, mock_isdir): def test_load_from_mount_file_not_found(self, mock_file, mock_stat, mock_isdir): config = SampleConfig() with pytest.raises(RuntimeError, match="failed to read secrets.*failed to read secret file"): - read_from_mount_and_fallback_to_env_var("/secrets", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/secrets", base_var_name="VAR") @patch('os.stat', side_effect=FileNotFoundError("Path not found")) def test_validate_path_not_exists(self, mock_stat): config = SampleConfig() with pytest.raises(RuntimeError, match="mount failed"): - read_from_mount_and_fallback_to_env_var("/nonexistent", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/nonexistent", base_var_name="VAR") @patch('os.path.isdir', return_value=False) @patch('os.stat') def test_validate_path_not_directory(self, mock_stat, mock_isdir): config = SampleConfig() with pytest.raises(RuntimeError, match="mount failed"): - read_from_mount_and_fallback_to_env_var("/file", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/file", base_var_name="VAR") @patch.dict(os.environ, { "VAR_MODULE_INSTANCE_USER": "env_user", @@ -88,7 +88,7 @@ def test_load_from_env_success(self): config = SampleConfig() with patch('os.path.isdir', return_value=False), \ patch('os.stat', side_effect=FileNotFoundError()): - read_from_mount_and_fallback_to_env_var("/nonexistent", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/nonexistent", base_var_name="VAR") assert config.username == "env_user" assert config.password == "env_pass" @@ -100,7 +100,7 @@ def test_load_from_env_missing_var(self): with patch('os.path.isdir', return_value=False), \ patch('os.stat', side_effect=FileNotFoundError()): with pytest.raises(RuntimeError, match="env var failed"): - read_from_mount_and_fallback_to_env_var("/nonexistent", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/nonexistent", base_var_name="VAR") @patch('os.path.isdir', return_value=True) @patch('os.stat') @@ -113,7 +113,7 @@ def test_mount_success_no_env_fallback(self, mock_file, mock_stat, mock_isdir): ] config = SampleConfig() - read_from_mount_and_fallback_to_env_var("/secrets", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/secrets", base_var_name="VAR") assert config.username == "mount_user" @@ -123,7 +123,7 @@ def test_both_fail_aggregated_error(self): with patch('os.path.isdir', return_value=False), \ patch('os.stat', side_effect=FileNotFoundError()): with pytest.raises(RuntimeError, match="mount failed.*env var failed"): - read_from_mount_and_fallback_to_env_var("/nonexistent", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/nonexistent", base_var_name="VAR") @patch('os.path.isdir', return_value=True) @patch('os.stat') @@ -136,7 +136,7 @@ def test_preserves_newlines(self, mock_file, mock_stat, mock_isdir): ] config = SampleConfig() - read_from_mount_and_fallback_to_env_var("/secrets", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/secrets", base_var_name="VAR") assert config.username == "user\nwith\nnewlines" @@ -149,7 +149,7 @@ class CaseConfig: config = CaseConfig() with patch('os.path.isdir', return_value=False), \ patch('os.stat', side_effect=FileNotFoundError()): - read_from_mount_and_fallback_to_env_var("/nonexistent", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/nonexistent", base_var_name="VAR") assert config.testfield == "test_value" @@ -164,7 +164,7 @@ def test_metadata_secret_priority(self, mock_file, mock_stat, mock_isdir): ] config = SampleConfig() - read_from_mount_and_fallback_to_env_var("/secrets", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/secrets", base_var_name="VAR") assert config.username == "metadata_user" @@ -179,7 +179,7 @@ def test_env_instance_name_hyphen_normalization(self): with patch('os.path.isdir', return_value=False), \ patch('os.stat', side_effect=FileNotFoundError()): read_from_mount_and_fallback_to_env_var( - "/nonexistent", "VAR", "module", "my-instance", config + "module", "my-instance", config, base_volume_mount="/nonexistent", base_var_name="VAR" ) assert config.username == "env_user_hyphen" @@ -197,7 +197,7 @@ def test_service_binding_root_overrides_base_mount(self, mock_file, mock_stat, m mock_open(read_data="e").return_value, ] config = SampleConfig() - read_from_mount_and_fallback_to_env_var("/etc/secrets/appfnd", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/etc/secrets/appfnd", base_var_name="VAR") first_call_path = mock_file.call_args_list[0][0][0] assert first_call_path.startswith("/custom/root") @@ -212,6 +212,6 @@ def test_default_base_mount_used_when_no_service_binding_root(self, mock_file, m mock_open(read_data="e").return_value, ] config = SampleConfig() - read_from_mount_and_fallback_to_env_var("/etc/secrets/appfnd", "VAR", "module", "instance", config) + read_from_mount_and_fallback_to_env_var("module", "instance", config, base_volume_mount="/etc/secrets/appfnd", base_var_name="VAR") first_call_path = mock_file.call_args_list[0][0][0] assert first_call_path.startswith("/etc/secrets/appfnd") diff --git a/tests/destination/unit/test_config.py b/tests/destination/unit/test_config.py index 1f4c740..79fa53d 100644 --- a/tests/destination/unit/test_config.py +++ b/tests/destination/unit/test_config.py @@ -126,8 +126,6 @@ def fake_read_side_effect(*args, **kwargs): # Verify resolver called with expected parameters assert mock_read.call_count == 1 _, kwargs = mock_read.call_args - assert kwargs["base_volume_mount"] == "/etc/secrets/appfnd" - assert kwargs["base_var_name"] == "CLOUD_SDK_CFG" assert kwargs["module"] == "destination" assert kwargs["instance"] == "default" assert isinstance(kwargs["target"], BindingData)