Load MINIO_SECURE from env and replace adapter asserts with explicit errors.
Default TLS to enabled for production MinIO setups while keeping local and integration tests on plain HTTP via explicit secure=false configuration. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
co-authored by
Cursor
parent
7991dabdbc
commit
3aa91280ec
@@ -6,3 +6,4 @@ MINIO_ENDPOINT=localhost:9000
|
|||||||
MINIO_ACCESS_KEY=minioadmin
|
MINIO_ACCESS_KEY=minioadmin
|
||||||
MINIO_SECRET_KEY=minioadmin
|
MINIO_SECRET_KEY=minioadmin
|
||||||
MINIO_BUCKET=my-bucket
|
MINIO_BUCKET=my-bucket
|
||||||
|
MINIO_SECURE=false
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ Requires Redis with the RedisJSON module (e.g. redis-stack).
|
|||||||
| `MINIO_ACCESS_KEY` | Access key |
|
| `MINIO_ACCESS_KEY` | Access key |
|
||||||
| `MINIO_SECRET_KEY` | Secret key |
|
| `MINIO_SECRET_KEY` | Secret key |
|
||||||
| `MINIO_BUCKET` | Bucket name (created on connect if missing) |
|
| `MINIO_BUCKET` | Bucket name (created on connect if missing) |
|
||||||
|
| `MINIO_SECURE` | Use HTTPS (`true`/`false`; default: `true`) |
|
||||||
|
|
||||||
Copy [`.env.example`](.env.example) to `.env` for local development. `RedisConfig.from_env()` and `MinioConfig.from_env()` load `.env` automatically when resolving configuration from the environment.
|
Copy [`.env.example`](.env.example) to `.env` for local development. `RedisConfig.from_env()` and `MinioConfig.from_env()` load `.env` automatically when resolving configuration from the environment.
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@ minio = MinioAdapter(
|
|||||||
access_key="minioadmin",
|
access_key="minioadmin",
|
||||||
secret_key="minioadmin",
|
secret_key="minioadmin",
|
||||||
bucket="my-bucket",
|
bucket="my-bucket",
|
||||||
|
secure=False,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class MinioAdapter(ObjectRepositoryInterface, ConnectionAwareAdapter):
|
|||||||
access_key_env_var_name: str = "MINIO_ACCESS_KEY"
|
access_key_env_var_name: str = "MINIO_ACCESS_KEY"
|
||||||
secret_key_env_var_name: str = "MINIO_SECRET_KEY"
|
secret_key_env_var_name: str = "MINIO_SECRET_KEY"
|
||||||
bucket_env_var_name: str = "MINIO_BUCKET"
|
bucket_env_var_name: str = "MINIO_BUCKET"
|
||||||
|
secure_env_var_name: str = "MINIO_SECURE"
|
||||||
chunk_size: int = 5 * 2**20 # 5 MiB
|
chunk_size: int = 5 * 2**20 # 5 MiB
|
||||||
connection_name: str = "Minio"
|
connection_name: str = "Minio"
|
||||||
|
|
||||||
@@ -35,16 +36,16 @@ class MinioAdapter(ObjectRepositoryInterface, ConnectionAwareAdapter):
|
|||||||
client: minio.Minio | None = None,
|
client: minio.Minio | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
if client is not None and config is None:
|
if config is None:
|
||||||
|
if client is not None:
|
||||||
raise ValueError("config is required when client is provided")
|
raise ValueError("config is required when client is provided")
|
||||||
if config is None and client is None:
|
|
||||||
config = MinioConfig.from_env(
|
config = MinioConfig.from_env(
|
||||||
self.endpoint_env_var_name,
|
self.endpoint_env_var_name,
|
||||||
self.access_key_env_var_name,
|
self.access_key_env_var_name,
|
||||||
self.secret_key_env_var_name,
|
self.secret_key_env_var_name,
|
||||||
self.bucket_env_var_name,
|
self.bucket_env_var_name,
|
||||||
|
self.secure_env_var_name,
|
||||||
)
|
)
|
||||||
assert config is not None
|
|
||||||
self._config = config
|
self._config = config
|
||||||
self._client_injected = client is not None
|
self._client_injected = client is not None
|
||||||
self._client: minio.Minio | None = client
|
self._client: minio.Minio | None = client
|
||||||
|
|||||||
@@ -34,11 +34,10 @@ class RedisAdapter(JsonRepositoryInterface, ConnectionAwareAdapter):
|
|||||||
client: redis.Redis | None = None,
|
client: redis.Redis | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
if client is not None and config is None:
|
if config is None:
|
||||||
|
if client is not None:
|
||||||
raise ValueError("config is required when client is provided")
|
raise ValueError("config is required when client is provided")
|
||||||
if config is None and client is None:
|
|
||||||
config = RedisConfig.from_env(self.uri_env_var_name)
|
config = RedisConfig.from_env(self.uri_env_var_name)
|
||||||
assert config is not None
|
|
||||||
self._config = config
|
self._config = config
|
||||||
self._client_injected = client is not None
|
self._client_injected = client is not None
|
||||||
self._client: redis.Redis | None = client
|
self._client: redis.Redis | None = client
|
||||||
|
|||||||
@@ -9,6 +9,21 @@ from python_utils import check_env
|
|||||||
|
|
||||||
from python_repositories.config.dotenv_loader import load_dotenv
|
from python_repositories.config.dotenv_loader import load_dotenv
|
||||||
|
|
||||||
|
_TRUTHY = frozenset({"1", "true", "yes", "on"})
|
||||||
|
_FALSY = frozenset({"0", "false", "no", "off"})
|
||||||
|
|
||||||
|
|
||||||
|
def _env_bool(name: str, default: bool) -> bool:
|
||||||
|
raw = os.getenv(name)
|
||||||
|
if raw is None:
|
||||||
|
return default
|
||||||
|
normalized = raw.strip().lower()
|
||||||
|
if normalized in _TRUTHY:
|
||||||
|
return True
|
||||||
|
if normalized in _FALSY:
|
||||||
|
return False
|
||||||
|
raise ValueError(f"Invalid boolean value for {name}: {raw!r}")
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class MinioConfig:
|
class MinioConfig:
|
||||||
@@ -18,7 +33,7 @@ class MinioConfig:
|
|||||||
access_key: str
|
access_key: str
|
||||||
secret_key: str
|
secret_key: str
|
||||||
bucket: str
|
bucket: str
|
||||||
secure: bool = False
|
secure: bool = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_env(
|
def from_env(
|
||||||
@@ -27,6 +42,7 @@ class MinioConfig:
|
|||||||
access_key_env_var_name: str = "MINIO_ACCESS_KEY",
|
access_key_env_var_name: str = "MINIO_ACCESS_KEY",
|
||||||
secret_key_env_var_name: str = "MINIO_SECRET_KEY",
|
secret_key_env_var_name: str = "MINIO_SECRET_KEY",
|
||||||
bucket_env_var_name: str = "MINIO_BUCKET",
|
bucket_env_var_name: str = "MINIO_BUCKET",
|
||||||
|
secure_env_var_name: str = "MINIO_SECURE",
|
||||||
*,
|
*,
|
||||||
use_dotenv: bool = True,
|
use_dotenv: bool = True,
|
||||||
) -> MinioConfig:
|
) -> MinioConfig:
|
||||||
@@ -45,4 +61,5 @@ class MinioConfig:
|
|||||||
access_key=str(os.getenv(access_key_env_var_name)),
|
access_key=str(os.getenv(access_key_env_var_name)),
|
||||||
secret_key=str(os.getenv(secret_key_env_var_name)),
|
secret_key=str(os.getenv(secret_key_env_var_name)),
|
||||||
bucket=str(os.getenv(bucket_env_var_name)),
|
bucket=str(os.getenv(bucket_env_var_name)),
|
||||||
|
secure=_env_bool(secure_env_var_name, default=True),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ TEST_MINIO_CONFIG = MinioConfig(
|
|||||||
access_key="minioadmin",
|
access_key="minioadmin",
|
||||||
secret_key="minioadmin",
|
secret_key="minioadmin",
|
||||||
bucket="test-bucket",
|
bucket="test-bucket",
|
||||||
|
secure=False,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ def minio_container() -> Generator[dict[str, str], None, None]:
|
|||||||
"MINIO_ACCESS_KEY": MINIO_ACCESS_KEY,
|
"MINIO_ACCESS_KEY": MINIO_ACCESS_KEY,
|
||||||
"MINIO_SECRET_KEY": MINIO_SECRET_KEY,
|
"MINIO_SECRET_KEY": MINIO_SECRET_KEY,
|
||||||
"MINIO_BUCKET": MINIO_BUCKET,
|
"MINIO_BUCKET": MINIO_BUCKET,
|
||||||
|
"MINIO_SECURE": "false",
|
||||||
}
|
}
|
||||||
|
|
||||||
yield env_vars
|
yield env_vars
|
||||||
@@ -91,6 +92,7 @@ def minio_config(minio_container: dict[str, str]) -> MinioConfig:
|
|||||||
access_key=minio_container["MINIO_ACCESS_KEY"],
|
access_key=minio_container["MINIO_ACCESS_KEY"],
|
||||||
secret_key=minio_container["MINIO_SECRET_KEY"],
|
secret_key=minio_container["MINIO_SECRET_KEY"],
|
||||||
bucket=minio_container["MINIO_BUCKET"],
|
bucket=minio_container["MINIO_BUCKET"],
|
||||||
|
secure=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,17 +7,59 @@ import pytest
|
|||||||
from python_repositories.config import MinioConfig
|
from python_repositories.config import MinioConfig
|
||||||
|
|
||||||
|
|
||||||
def test_from_env_loads_all_fields(monkeypatch: pytest.MonkeyPatch) -> None:
|
def _set_required_minio_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
monkeypatch.setenv("MINIO_ENDPOINT", "localhost:9000")
|
monkeypatch.setenv("MINIO_ENDPOINT", "localhost:9000")
|
||||||
monkeypatch.setenv("MINIO_ACCESS_KEY", "access")
|
monkeypatch.setenv("MINIO_ACCESS_KEY", "access")
|
||||||
monkeypatch.setenv("MINIO_SECRET_KEY", "secret")
|
monkeypatch.setenv("MINIO_SECRET_KEY", "secret")
|
||||||
monkeypatch.setenv("MINIO_BUCKET", "my-bucket")
|
monkeypatch.setenv("MINIO_BUCKET", "my-bucket")
|
||||||
|
|
||||||
|
|
||||||
|
def test_from_env_loads_all_fields(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
_set_required_minio_env(monkeypatch)
|
||||||
config = MinioConfig.from_env(use_dotenv=False)
|
config = MinioConfig.from_env(use_dotenv=False)
|
||||||
assert config.endpoint == "localhost:9000"
|
assert config.endpoint == "localhost:9000"
|
||||||
assert config.access_key == "access"
|
assert config.access_key == "access"
|
||||||
assert config.secret_key == "secret"
|
assert config.secret_key == "secret"
|
||||||
assert config.bucket == "my-bucket"
|
assert config.bucket == "my-bucket"
|
||||||
assert config.secure is False
|
assert config.secure is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_from_env_defaults_secure_to_true_when_unset(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
_set_required_minio_env(monkeypatch)
|
||||||
|
monkeypatch.delenv("MINIO_SECURE", raising=False)
|
||||||
|
config = MinioConfig.from_env(use_dotenv=False)
|
||||||
|
assert config.secure is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("value", "expected"),
|
||||||
|
[
|
||||||
|
("true", True),
|
||||||
|
("1", True),
|
||||||
|
("yes", True),
|
||||||
|
("false", False),
|
||||||
|
("0", False),
|
||||||
|
("no", False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_from_env_parses_secure(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
value: str,
|
||||||
|
expected: bool,
|
||||||
|
) -> None:
|
||||||
|
_set_required_minio_env(monkeypatch)
|
||||||
|
monkeypatch.setenv("MINIO_SECURE", value)
|
||||||
|
config = MinioConfig.from_env(use_dotenv=False)
|
||||||
|
assert config.secure is expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_from_env_raises_for_invalid_secure(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
_set_required_minio_env(monkeypatch)
|
||||||
|
monkeypatch.setenv("MINIO_SECURE", "not-a-bool")
|
||||||
|
with pytest.raises(ValueError, match="MINIO_SECURE"):
|
||||||
|
MinioConfig.from_env(use_dotenv=False)
|
||||||
|
|
||||||
|
|
||||||
def test_from_env_raises_when_endpoint_missing(
|
def test_from_env_raises_when_endpoint_missing(
|
||||||
|
|||||||
Reference in New Issue
Block a user