Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions src/sap_cloud_sdk/core/auditlog/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
1 change: 1 addition & 0 deletions src/sap_cloud_sdk/core/secret_resolver/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
"""

BASE_MOUNT_PATH = "/etc/secrets/appfnd"
CLOUD_SDK_ENV_PREFIX = "CLOUD_SDK_CFG"
6 changes: 3 additions & 3 deletions src/sap_cloud_sdk/core/secret_resolver/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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`.
Expand Down
2 changes: 0 additions & 2 deletions src/sap_cloud_sdk/destination/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 0 additions & 2 deletions src/sap_cloud_sdk/dms/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 0 additions & 2 deletions src/sap_cloud_sdk/objectstore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 0 additions & 2 deletions tests/core/unit/auditlog/unit/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
36 changes: 18 additions & 18 deletions tests/core/unit/secret_resolver/unit/test_secret_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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"
Expand All @@ -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",
Expand All @@ -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"
Expand All @@ -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')
Expand All @@ -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"

Expand All @@ -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')
Expand All @@ -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"

Expand All @@ -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"

Expand All @@ -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"

Expand All @@ -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"
Expand All @@ -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")

Expand All @@ -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")
2 changes: 0 additions & 2 deletions tests/destination/unit/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading