Skip to content

Commit 63227d2

Browse files
committed
refact: better named module and functions
1 parent bc23078 commit 63227d2

File tree

4 files changed

+136
-103
lines changed

4 files changed

+136
-103
lines changed

packages/hop3-server/src/hop3/deployers/addon_provisioning.py

Lines changed: 3 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@
1919

2020
from hop3.core.credentials import get_credential_encryptor
2121
from hop3.core.plugins import get_addon
22+
from hop3.deployers.env_provisioning import set_env_vars
2223
from hop3.lib import log
2324
from hop3.lib.logging import server_log
24-
from hop3.orm import AddonCredential, EnvVar
25+
from hop3.orm import AddonCredential
2526

2627
if TYPE_CHECKING:
2728
from sqlalchemy.orm import Session
@@ -155,7 +156,7 @@ def _provision_single_addon(
155156
db_session.add(credential)
156157

157158
# Add env vars to app (addons can update existing values, e.g., when password changes)
158-
_inject_env_vars(app, connection_details, db_session)
159+
set_env_vars(app, connection_details, db_session)
159160

160161
log(f" Attached addon {addon_name} to {app.name}", level=1, fg="green")
161162
server_log.info(
@@ -165,80 +166,3 @@ def _provision_single_addon(
165166
app_name=app.name,
166167
env_vars=list(connection_details.keys()),
167168
)
168-
169-
170-
def inject_config_env_vars(
171-
app: App,
172-
env_config: dict[str, str],
173-
db_session: Session,
174-
) -> None:
175-
"""Inject environment variables from hop3.toml [env] section.
176-
177-
These are treated as defaults: they will only be set if the variable
178-
doesn't exist or was previously set from hop3.toml. User-set values
179-
(via config:set) and addon values are preserved.
180-
181-
Args:
182-
app: The application model
183-
env_config: Dict of env var name -> value from hop3.toml
184-
db_session: Database session for persistence
185-
"""
186-
if not env_config:
187-
return
188-
189-
server_log.info(
190-
"Injecting env vars from config",
191-
app_name=app.name,
192-
env_var_count=len(env_config),
193-
)
194-
195-
injected_count = _inject_env_vars(app, env_config, db_session, defaults_only=True)
196-
197-
log(
198-
f" Injected {injected_count} env var(s) from hop3.toml",
199-
level=1,
200-
fg="green",
201-
)
202-
203-
204-
def _inject_env_vars(
205-
app: App,
206-
env_vars: dict[str, str],
207-
db_session: Session,
208-
*,
209-
defaults_only: bool = False,
210-
) -> int:
211-
"""Inject environment variables into an app.
212-
213-
Args:
214-
app: The application model
215-
env_vars: Dict of env var name -> value
216-
db_session: Database session for persistence
217-
defaults_only: If True, only create new vars, never overwrite existing ones
218-
219-
Returns:
220-
Number of env vars actually injected/updated
221-
"""
222-
injected = 0
223-
224-
for key, value in env_vars.items():
225-
# Check if variable already exists
226-
existing = None
227-
for env_var in app.env_vars:
228-
if env_var.name == key:
229-
existing = env_var
230-
break
231-
232-
if existing:
233-
if defaults_only:
234-
# hop3.toml provides defaults only - don't overwrite
235-
continue
236-
existing.value = str(value)
237-
injected += 1
238-
else:
239-
new_var = EnvVar(app_id=app.id, name=key, value=str(value))
240-
db_session.add(new_var)
241-
app.env_vars.append(new_var)
242-
injected += 1
243-
244-
return injected

packages/hop3-server/src/hop3/deployers/deployer.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
from hop3.core.manifest import RuntimeManifestBuilder
99
from hop3.core.plugins import get_builder, get_deployment_strategy
1010
from hop3.core.protocols import DeploymentContext
11-
from hop3.deployers.addon_provisioning import inject_config_env_vars, provision_addons
11+
from hop3.deployers.addon_provisioning import provision_addons
12+
from hop3.deployers.env_provisioning import set_default_env_vars
1213
from hop3.lib import Abort, log, shell
1314
from hop3.lib.logging import server_log
1415
from hop3.orm.app import AppStateEnum
@@ -416,7 +417,7 @@ def _process_config_dependencies(
416417
level=1,
417418
fg="blue",
418419
)
419-
inject_config_env_vars(app, env_config, db_session)
420+
set_default_env_vars(app, env_config, db_session)
420421

421422
# Commit changes before continuing with build
422423
if addon_configs or env_config:
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Copyright (c) 2025, Abilian SAS
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
"""Environment variable provisioning during deployment.
6+
7+
This module handles injection of environment variables from hop3.toml [env] section.
8+
Values from hop3.toml are treated as defaults - they only create new variables
9+
and never overwrite existing ones (set via config:set or addon provisioning).
10+
"""
11+
12+
from __future__ import annotations
13+
14+
from typing import TYPE_CHECKING
15+
16+
from hop3.lib import log
17+
from hop3.lib.logging import server_log
18+
from hop3.orm import EnvVar
19+
20+
if TYPE_CHECKING:
21+
from sqlalchemy.orm import Session
22+
23+
from hop3.orm.app import App
24+
25+
26+
def set_default_env_vars(
27+
app: App,
28+
env_config: dict[str, str],
29+
db_session: Session,
30+
) -> None:
31+
"""Set default environment variables from hop3.toml [env] section.
32+
33+
These are defaults only: they create new variables but never overwrite
34+
existing ones. User-set values (via config:set) and addon values are preserved.
35+
36+
Args:
37+
app: The application model
38+
env_config: Dict of env var name -> value from hop3.toml
39+
db_session: Database session for persistence
40+
"""
41+
if not env_config:
42+
return
43+
44+
server_log.info(
45+
"Setting default env vars from config",
46+
app_name=app.name,
47+
env_var_count=len(env_config),
48+
)
49+
50+
injected_count = set_env_vars(app, env_config, db_session, defaults_only=True)
51+
52+
log(
53+
f" Set {injected_count} env var(s) from hop3.toml",
54+
level=1,
55+
fg="green",
56+
)
57+
58+
59+
def set_env_vars(
60+
app: App,
61+
env_vars: dict[str, str],
62+
db_session: Session,
63+
*,
64+
defaults_only: bool = False,
65+
) -> int:
66+
"""Set environment variables on an app.
67+
68+
Args:
69+
app: The application model
70+
env_vars: Dict of env var name -> value
71+
db_session: Database session for persistence
72+
defaults_only: If True, only create new vars, never overwrite existing ones
73+
74+
Returns:
75+
Number of env vars actually set/updated
76+
"""
77+
count = 0
78+
79+
for key, value in env_vars.items():
80+
existing = None
81+
for env_var in app.env_vars:
82+
if env_var.name == key:
83+
existing = env_var
84+
break
85+
86+
if existing:
87+
if defaults_only:
88+
continue
89+
existing.value = str(value)
90+
count += 1
91+
else:
92+
new_var = EnvVar(app_id=app.id, name=key, value=str(value))
93+
db_session.add(new_var)
94+
app.env_vars.append(new_var)
95+
count += 1
96+
97+
return count

packages/hop3-server/tests/a_unit/test_addon_provisioning.py

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,8 @@
1010

1111
import pytest
1212

13-
from hop3.deployers.addon_provisioning import (
14-
_inject_env_vars,
15-
inject_config_env_vars,
16-
provision_addons,
17-
)
13+
from hop3.deployers.addon_provisioning import provision_addons
14+
from hop3.deployers.env_provisioning import set_default_env_vars, set_env_vars
1815

1916

2017
@pytest.fixture
@@ -35,23 +32,23 @@ def mock_db_session():
3532
return session
3633

3734

38-
class TestInjectEnvVars:
39-
"""Tests for _inject_env_vars function."""
35+
class TestSetEnvVars:
36+
"""Tests for set_env_vars function."""
4037

41-
def test_inject_new_env_vars(self, mock_app, mock_db_session):
42-
"""Test injecting new environment variables."""
38+
def test_set_new_env_vars(self, mock_app, mock_db_session):
39+
"""Test setting new environment variables."""
4340
env_vars = {
4441
"DATABASE_URL": "postgres://localhost/db",
4542
"REDIS_URL": "redis://localhost:6379",
4643
}
4744

48-
_inject_env_vars(mock_app, env_vars, mock_db_session)
45+
set_env_vars(mock_app, env_vars, mock_db_session)
4946

5047
# Should have added 2 new env vars
5148
assert len(mock_app.env_vars) == 2
5249
assert mock_db_session.add.call_count == 2
5350

54-
def test_inject_updates_existing_env_var(self, mock_app, mock_db_session):
51+
def test_set_updates_existing_env_var(self, mock_app, mock_db_session):
5552
"""Test updating existing environment variable."""
5653
# Simulate existing env var
5754
existing_var = MagicMock()
@@ -61,38 +58,52 @@ def test_inject_updates_existing_env_var(self, mock_app, mock_db_session):
6158

6259
env_vars = {"DATABASE_URL": "new_value"}
6360

64-
_inject_env_vars(mock_app, env_vars, mock_db_session)
61+
set_env_vars(mock_app, env_vars, mock_db_session)
6562

6663
# Should have updated the existing var
6764
assert existing_var.value == "new_value"
6865
# Should not have added new env vars
6966
assert mock_db_session.add.call_count == 0
7067

71-
def test_inject_empty_env_vars(self, mock_app, mock_db_session):
72-
"""Test injecting empty env vars dict does nothing."""
73-
_inject_env_vars(mock_app, {}, mock_db_session)
68+
def test_set_empty_env_vars(self, mock_app, mock_db_session):
69+
"""Test setting empty env vars dict does nothing."""
70+
set_env_vars(mock_app, {}, mock_db_session)
7471

7572
assert mock_db_session.add.call_count == 0
7673
assert len(mock_app.env_vars) == 0
7774

75+
def test_defaults_only_skips_existing(self, mock_app, mock_db_session):
76+
"""Test that defaults_only=True skips existing variables."""
77+
existing_var = MagicMock()
78+
existing_var.name = "SECRET_KEY"
79+
existing_var.value = "user-set-value"
80+
mock_app.env_vars = [existing_var]
81+
82+
env_vars = {"SECRET_KEY": "default-value"}
83+
84+
set_env_vars(mock_app, env_vars, mock_db_session, defaults_only=True)
85+
86+
# Should NOT have updated the existing var
87+
assert existing_var.value == "user-set-value"
88+
7889

79-
class TestInjectConfigEnvVars:
80-
"""Tests for inject_config_env_vars function."""
90+
class TestSetDefaultEnvVars:
91+
"""Tests for set_default_env_vars function."""
8192

82-
def test_inject_config_env_vars(self, mock_app, mock_db_session):
83-
"""Test injecting env vars from config."""
93+
def test_set_default_env_vars(self, mock_app, mock_db_session):
94+
"""Test setting default env vars from config."""
8495
env_config = {
8596
"DEBUG": "false",
8697
"SECRET_KEY": "mysecret",
8798
}
8899

89-
inject_config_env_vars(mock_app, env_config, mock_db_session)
100+
set_default_env_vars(mock_app, env_config, mock_db_session)
90101

91102
assert len(mock_app.env_vars) == 2
92103

93-
def test_inject_config_env_vars_empty(self, mock_app, mock_db_session):
104+
def test_set_default_env_vars_empty(self, mock_app, mock_db_session):
94105
"""Test that empty config does nothing."""
95-
inject_config_env_vars(mock_app, {}, mock_db_session)
106+
set_default_env_vars(mock_app, {}, mock_db_session)
96107

97108
assert len(mock_app.env_vars) == 0
98109

0 commit comments

Comments
 (0)