Skip to content

Commit c79db0f

Browse files
authored
Merge pull request #7 from Never-Over/separate-container-env-from-client-env
Separate env for containers and clients
2 parents 32b0cd3 + 3ae1e25 commit c79db0f

File tree

8 files changed

+85
-81
lines changed

8 files changed

+85
-81
lines changed

bridge/framework/base.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from bridge.platform import Platform, detect_platform
99
from bridge.service.postgres import PostgresService
10-
from bridge.service.redis import RedisConfig, RedisService
10+
from bridge.service.redis import RedisService
1111

1212

1313
class Framework(Enum):
@@ -66,8 +66,7 @@ def start_local_postgres(self, client: docker.DockerClient) -> None:
6666
service.start()
6767

6868
def start_local_redis(self, client: docker.DockerClient) -> None:
69-
config = RedisConfig()
70-
service = RedisService(client=client, config=config)
69+
service = RedisService(client=client)
7170
service.start()
7271

7372
@abstractmethod

bridge/platform/postgres.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
1+
import os
2+
3+
from pydantic import BaseModel
4+
5+
from bridge.console import log_warning
16
from bridge.platform.base import Platform
2-
from bridge.platform.render import build_render_postgres_environment
3-
from bridge.service.postgres import PostgresEnvironment
7+
8+
9+
class PostgresEnvironment(BaseModel):
10+
user: str = "postgres"
11+
password: str = "postgres"
12+
db: str = "postgres"
13+
host: str = "localhost"
14+
port: int = 5432
15+
16+
@classmethod
17+
def from_env(cls):
18+
try:
19+
port = int(os.environ.get("POSTGRES_PORT", 5432))
20+
except ValueError:
21+
log_warning("Invalid POSTGRES_PORT; using default value.")
22+
port = 5432
23+
return cls(
24+
user=os.environ.get("POSTGRES_USER", "postgres"),
25+
password=os.environ.get("POSTGRES_PASSWORD", "postgres"),
26+
db=os.environ.get("POSTGRES_DB", "postgres"),
27+
host=os.environ.get("POSTGRES_HOST", "localhost"),
28+
port=port,
29+
)
430

531

632
def build_postgres_environment(platform: Platform) -> PostgresEnvironment:
@@ -10,6 +36,8 @@ def build_postgres_environment(platform: Platform) -> PostgresEnvironment:
1036
# which runs locally.
1137
return PostgresEnvironment()
1238
elif platform == Platform.RENDER:
39+
from bridge.platform.render import build_render_postgres_environment
40+
1341
return build_render_postgres_environment()
1442
elif platform == Platform.UNKNOWN_REMOTE:
1543
# This will pull from environment variables like POSTGRES_USER, etc.

bridge/platform/redis.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1+
from pydantic import BaseModel
2+
13
from bridge.platform.base import Platform
2-
from bridge.platform.render import build_render_redis_environment
3-
from bridge.service.redis import RedisEnvironment
4+
5+
6+
class RedisEnvironment(BaseModel):
7+
host: str = "localhost"
8+
port: int = 6379
9+
db: int = 0
10+
11+
@property
12+
def url(self) -> str:
13+
return f"redis://{self.host}:{self.port}"
414

515

616
def build_redis_environment(platform: Platform) -> RedisEnvironment:
717
if platform == Platform.LOCAL:
818
return RedisEnvironment()
919
elif platform == Platform.RENDER:
20+
from bridge.platform.render import build_render_redis_environment
21+
1022
return build_render_redis_environment()
1123
else:
1224
raise NotImplementedError(f"Unsupported platform for Redis: {platform}")

bridge/platform/render/postgres.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import dj_database_url
44

5-
from bridge.service.postgres import PostgresEnvironment
5+
from bridge.platform.postgres import PostgresEnvironment
66

77

88
def build_render_postgres_environment() -> PostgresEnvironment:

bridge/service/docker.py

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import sys
22
from abc import ABC, abstractmethod
3-
from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast
3+
from typing import TYPE_CHECKING, Generic, TypeVar, Union, cast
44

55
import docker
66
from docker.models.containers import Container
7-
from pydantic import BaseModel, Extra, Field
7+
from pydantic import BaseModel, Field
88
from rich.console import Console
99

1010
from bridge.console import log_error, log_task
11+
from bridge.utils.pydantic import Empty
1112

1213
if TYPE_CHECKING:
1314
import docker.errors
@@ -23,18 +24,10 @@ def get_docker_client() -> docker.DockerClient:
2324
return client
2425

2526

26-
class BaseEnvironment(BaseModel):
27-
def to_container_run_kwargs(self) -> dict[str, Any]:
28-
return self.model_dump()
27+
T_BaseModel = TypeVar("T_BaseModel", bound=BaseModel)
2928

30-
class Config:
31-
extra = Extra.allow
3229

33-
34-
T_Environment = TypeVar("T_Environment", bound=BaseEnvironment)
35-
36-
37-
class ContainerConfig(BaseModel, Generic[T_Environment]):
30+
class ContainerConfig(BaseModel, Generic[T_BaseModel]):
3831
"""
3932
Container configuration information.
4033
@@ -44,15 +37,9 @@ class ContainerConfig(BaseModel, Generic[T_Environment]):
4437
image: str
4538
name: str
4639
ports: dict[str, int] = Field(default_factory=dict)
47-
volumes: dict[str, str] = Field(default_factory=dict)
40+
volumes: dict[str, Union[list[str], dict[str, str]]] = Field(default_factory=dict)
4841
restart_policy: dict[str, str] = {"Name": "always"}
49-
environment: T_Environment = Field(default_factory=BaseEnvironment)
50-
51-
def to_container_run_kwargs(self) -> dict[str, Any]:
52-
# Right now the above spec matches `docker.container.run`, model_dump is sufficient
53-
dict_rep = self.model_dump()
54-
dict_rep["environment"] = self.environment.to_container_run_kwargs()
55-
return dict_rep
42+
environment: T_BaseModel = Field(default_factory=Empty)
5643

5744

5845
T_ContainerConfig = TypeVar("T_ContainerConfig", bound=ContainerConfig)
@@ -101,7 +88,7 @@ def start_container(self):
10188
container.restart()
10289
else:
10390
self.client.containers.run(
104-
**self.config.to_container_run_kwargs(),
91+
**self.config.model_dump(),
10592
detach=True,
10693
)
10794

bridge/service/postgres.py

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,36 @@
1-
import os
21
from time import sleep
32
from typing import Optional, Union
43

54
import docker
65
import psycopg
7-
from pydantic import Field
6+
from pydantic import BaseModel, Field
87

98
from bridge.console import log_task
10-
from bridge.service.docker import BaseEnvironment, ContainerConfig, DockerService
9+
from bridge.service.docker import ContainerConfig, DockerService
1110
from bridge.utils.filesystem import resolve_dot_bridge
1211

1312

14-
class PostgresEnvironment(BaseEnvironment):
15-
user: str = "postgres"
16-
password: str = "postgres"
17-
db: str = "postgres"
18-
host: str = "localhost"
19-
port: Union[int, str] = "5432"
20-
21-
@classmethod
22-
def from_env(cls):
23-
return cls(
24-
user=os.environ.get("POSTGRES_USER", "postgres"),
25-
password=os.environ.get("POSTGRES_PASSWORD", "postgres"),
26-
db=os.environ.get("POSTGRES_DB", "postgres"),
27-
host=os.environ.get("POSTGRES_HOST", "localhost"),
28-
port=os.environ.get("POSTGRES_PORT", "5432"),
29-
)
30-
31-
def to_container_run_kwargs(self) -> dict[str, Union[int, str]]:
32-
return {
33-
"POSTGRES_USER": self.user,
34-
"POSTGRES_PASSWORD": self.password,
35-
"POSTGRES_DB": self.db,
36-
"POSTGRES_HOST": self.host,
37-
"POSTGRES_PORT": self.port,
38-
}
13+
class PostgresEnvironment(BaseModel):
14+
POSTGRES_USER: str = "postgres"
15+
POSTGRES_PASSWORD: str = "postgres"
16+
POSTGRES_DB: str = "postgres"
17+
POSTGRES_HOST: str = "localhost"
18+
POSTGRES_PORT: str = "5432"
3919

4020

4121
class PostgresConfig(ContainerConfig[PostgresEnvironment]):
4222
image: str = "postgres:12"
4323
name: str = "bridge_postgres"
4424
ports: dict[str, int] = {"5432/tcp": 5432}
45-
volumes: dict[str, str] = Field(
25+
volumes: dict[str, Union[list[str], dict[str, str]]] = Field(
4626
default_factory=lambda: {
4727
f"{resolve_dot_bridge()}/pgdata": {
4828
"bind": "/var/lib/postgresql/data",
4929
"mode": "rw",
5030
}
5131
}
5232
)
53-
environment: PostgresEnvironment = PostgresEnvironment()
33+
environment: PostgresEnvironment = Field(default_factory=PostgresEnvironment)
5434

5535

5636
class PostgresService(DockerService[PostgresConfig]):
@@ -61,11 +41,11 @@ def __init__(
6141

6242
def ensure_ready(self):
6343
dsn = (
64-
f"dbname={self.config.environment.db} "
65-
f"user={self.config.environment.user} "
66-
f"password={self.config.environment.password} "
67-
f"host={self.config.environment.host} "
68-
f"port={self.config.environment.port}"
44+
f"dbname={self.config.environment.POSTGRES_DB} "
45+
f"user={self.config.environment.POSTGRES_USER} "
46+
f"password={self.config.environment.POSTGRES_PASSWORD} "
47+
f"host={self.config.environment.POSTGRES_HOST} "
48+
f"port={self.config.environment.POSTGRES_PORT}"
6949
)
7050
with log_task(
7151
start_message=f"Waiting for [white]{self.config.name}[/white] to be ready",

bridge/service/redis.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,26 @@
11
from time import sleep
2+
from typing import Optional
23

34
import docker
45
import redis
56

67
from bridge.console import log_task
7-
from bridge.service.docker import BaseEnvironment, ContainerConfig, DockerService
8+
from bridge.platform.redis import RedisEnvironment
9+
from bridge.service.docker import ContainerConfig, DockerService
810

911

10-
class RedisEnvironment(BaseEnvironment):
11-
host: str = "localhost"
12-
port: int = 6379
13-
db: int = 0
14-
15-
@property
16-
def url(self) -> str:
17-
return f"redis://{self.host}:{self.port}"
18-
19-
20-
class RedisConfig(ContainerConfig[RedisEnvironment]):
12+
class RedisConfig(ContainerConfig):
2113
image: str = "redis:7.2.4"
2214
name: str = "bridge_redis"
2315
ports: dict[str, int] = {"6379/tcp": 6379}
24-
environment: RedisEnvironment = RedisEnvironment()
2516

2617

2718
class RedisService(DockerService[RedisConfig]):
28-
def __init__(self, client: docker.DockerClient, config: RedisConfig) -> None:
29-
super().__init__(client, config)
19+
def __init__(
20+
self, client: docker.DockerClient, config: Optional[RedisConfig] = None
21+
) -> None:
22+
super().__init__(client, config or RedisConfig())
23+
self.redis_client_environment = RedisEnvironment()
3024

3125
def ensure_ready(self):
3226
with log_task(
@@ -37,8 +31,8 @@ def ensure_ready(self):
3731
try:
3832
# Attempt to create a connection to Redis
3933
r = redis.Redis(
40-
host=self.config.environment.host,
41-
port=self.config.environment.port,
34+
host=self.redis_client_environment.host,
35+
port=self.redis_client_environment.port,
4236
)
4337
if r.ping():
4438
return # Redis is ready and responding

bridge/utils/pydantic.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from pydantic import BaseModel
2+
3+
4+
class Empty(BaseModel): ...

0 commit comments

Comments
 (0)