mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-27 23:28:03 +09:00
194 lines
7.9 KiB
Python
194 lines
7.9 KiB
Python
"""Effective deploy env: shared variable groups merged under local env vars,
|
|
and proof that the docker deploy path injects the merged result."""
|
|
import types
|
|
|
|
|
|
def _make_app(workspace_id=None, project_id=None, environment_id=None, port=8080):
|
|
from app import db
|
|
from app.models.application import Application
|
|
a = Application(
|
|
name='svc', app_type='docker', status='running', user_id=1, port=port,
|
|
workspace_id=workspace_id, project_id=project_id, environment_id=environment_id,
|
|
)
|
|
db.session.add(a)
|
|
db.session.commit()
|
|
return a
|
|
|
|
|
|
def _set_local(app_id, key, value, is_secret=False):
|
|
from app import db
|
|
from app.models import EnvironmentVariable
|
|
ev = EnvironmentVariable(application_id=app_id, key=key, is_secret=is_secret)
|
|
ev.value = value # encrypted via the model's value setter
|
|
db.session.add(ev)
|
|
db.session.commit()
|
|
return ev
|
|
|
|
|
|
def _make_scoped_group(scope_type, scope_id, key, value):
|
|
"""A group scoped to a workspace/project/environment auto-applies to apps in
|
|
that scope (no attachment needed)."""
|
|
from app import db
|
|
from app.models.shared_resource import SharedVariableGroup, SharedVariable
|
|
g = SharedVariableGroup(scope_type=scope_type, scope_id=str(scope_id), name=f'{scope_type}-grp')
|
|
db.session.add(g)
|
|
db.session.commit()
|
|
v = SharedVariable(group_id=g.id, key=key, is_secret=False)
|
|
v.value = value
|
|
db.session.add(v)
|
|
db.session.commit()
|
|
return g
|
|
|
|
|
|
def test_local_only_when_no_shared(app):
|
|
from app.services.env_service import EnvService
|
|
with app.app_context():
|
|
a = _make_app(workspace_id=1)
|
|
_set_local(a.id, 'FOO', 'foo')
|
|
_set_local(a.id, 'BAR', 'bar')
|
|
env = EnvService.get_effective_env(a.id)
|
|
assert env == {'FOO': 'foo', 'BAR': 'bar'}
|
|
|
|
|
|
def test_shared_fills_in_missing_keys(app):
|
|
from app.services.env_service import EnvService
|
|
with app.app_context():
|
|
a = _make_app(workspace_id=7)
|
|
_make_scoped_group('workspace', 7, 'SHARED_KEY', 'shared_val')
|
|
_set_local(a.id, 'LOCAL_KEY', 'local_val')
|
|
env = EnvService.get_effective_env(a.id)
|
|
assert env['SHARED_KEY'] == 'shared_val' # came from the shared group
|
|
assert env['LOCAL_KEY'] == 'local_val'
|
|
|
|
|
|
def test_local_overrides_shared_same_key(app):
|
|
from app.services.env_service import EnvService
|
|
with app.app_context():
|
|
a = _make_app(workspace_id=11)
|
|
_make_scoped_group('workspace', 11, 'DATABASE_URL', 'shared://db')
|
|
_set_local(a.id, 'DATABASE_URL', 'local://db')
|
|
env = EnvService.get_effective_env(a.id)
|
|
# The app's own value wins — matches the "local value applies" UI hint.
|
|
assert env['DATABASE_URL'] == 'local://db'
|
|
|
|
|
|
def test_shared_hierarchy_under_local(app):
|
|
from app.services.env_service import EnvService
|
|
with app.app_context():
|
|
a = _make_app(workspace_id=20, environment_id=30)
|
|
_make_scoped_group('workspace', 20, 'TIER', 'ws')
|
|
_make_scoped_group('environment', 30, 'TIER', 'env')
|
|
# environment scope outranks workspace scope (both below local)
|
|
env = EnvService.get_effective_env(a.id)
|
|
assert env['TIER'] == 'env'
|
|
_set_local(a.id, 'TIER', 'local')
|
|
assert EnvService.get_effective_env(a.id)['TIER'] == 'local'
|
|
|
|
|
|
def test_shared_resolution_failure_is_best_effort(app, monkeypatch):
|
|
from app.services.env_service import EnvService
|
|
from app.services.shared_resource_service import SharedResourceService
|
|
with app.app_context():
|
|
a = _make_app(workspace_id=1)
|
|
_set_local(a.id, 'KEEP', 'me')
|
|
|
|
def _boom(*args, **kwargs):
|
|
raise RuntimeError('shared store down')
|
|
monkeypatch.setattr(SharedResourceService, 'resolve_hierarchical', _boom)
|
|
|
|
env = EnvService.get_effective_env(a.id)
|
|
assert env == {'KEEP': 'me'} # deploy never blocked by shared failure
|
|
|
|
|
|
def test_deploy_docker_injects_effective_env(app, monkeypatch):
|
|
"""The docker deploy path must hand run_container the merged env (shared
|
|
underneath local), not just local env vars."""
|
|
from app.services import deployment_service as ds_mod
|
|
from app.services.deployment_service import DeploymentService
|
|
|
|
with app.app_context():
|
|
a = _make_app(workspace_id=42)
|
|
_make_scoped_group('workspace', 42, 'SHARED_ONLY', 'from_group')
|
|
_make_scoped_group('workspace', 42, 'OVERRIDE_ME', 'group_value')
|
|
_set_local(a.id, 'OVERRIDE_ME', 'local_value')
|
|
_set_local(a.id, 'LOCAL_ONLY', 'local_only')
|
|
|
|
captured = {}
|
|
|
|
class FakeDocker:
|
|
@staticmethod
|
|
def get_container(name):
|
|
return None
|
|
|
|
@staticmethod
|
|
def run_container(**kwargs):
|
|
captured.update(kwargs)
|
|
return {'success': True, 'container_id': 'deadbeef'}
|
|
|
|
monkeypatch.setattr(ds_mod, 'DockerService', FakeDocker)
|
|
|
|
deployment = types.SimpleNamespace(image_tag='myimg:1.0')
|
|
result = DeploymentService._deploy_docker(a, deployment)
|
|
assert result['success'] is True
|
|
|
|
env = captured.get('env') or {}
|
|
assert env['SHARED_ONLY'] == 'from_group' # shared var reached the container
|
|
assert env['OVERRIDE_ME'] == 'local_value' # local won the collision
|
|
assert env['LOCAL_ONLY'] == 'local_only'
|
|
|
|
|
|
def test_effective_view_matches_winners_without_values(app):
|
|
from app import db
|
|
from app.services.env_service import EnvService
|
|
with app.app_context():
|
|
a = _make_app(workspace_id=7)
|
|
_make_scoped_group('workspace', 7, 'SHARED', 'inherited-secret')
|
|
_make_scoped_group('workspace', 7, 'OVERRIDE', 'old-secret')
|
|
_set_local(a.id, 'OVERRIDE', 'new-secret', is_secret=True)
|
|
local = _set_local(a.id, 'TARGET', 'target-secret')
|
|
local.target_service = 'web'
|
|
db.session.commit()
|
|
for service in ('web', 'worker'):
|
|
view = EnvService.get_effective_env_view(a.id, service)
|
|
deployed = EnvService.get_effective_env_for_services(a.id, [service])[service]
|
|
assert view['complete'] and view['values_hidden']
|
|
assert {row['key'] for row in view['env_vars']} == set(deployed)
|
|
rows = {row['key']: row for row in view['env_vars']}
|
|
assert rows['SHARED']['source_scope'] == 'workspace'
|
|
assert rows['OVERRIDE']['source_scope'] == 'local'
|
|
assert rows['OVERRIDE']['overrides']['group_name'] == 'workspace-grp'
|
|
assert 'secret' not in str(view).replace('is_secret', '')
|
|
assert all('value' not in row for row in rows.values())
|
|
|
|
|
|
def test_effective_view_reports_incomplete_shared_resolution(app, monkeypatch):
|
|
from app.services.env_service import EnvService
|
|
from app.services.shared_resource_service import SharedResourceService
|
|
with app.app_context():
|
|
a = _make_app()
|
|
_set_local(a.id, 'LOCAL', 'private')
|
|
def fail(*args, **kwargs):
|
|
raise RuntimeError('sensitive connection details')
|
|
monkeypatch.setattr(SharedResourceService, 'resolve_hierarchical', fail)
|
|
view = EnvService.get_effective_env_view(a.id)
|
|
assert view['complete'] is False
|
|
assert [row['key'] for row in view['env_vars']] == ['LOCAL']
|
|
assert 'sensitive' not in str(view)
|
|
assert EnvService.get_effective_env(a.id) == {'LOCAL': 'private'}
|
|
|
|
|
|
def test_effective_view_never_resolves_local_references(app, monkeypatch):
|
|
from app import db
|
|
from app.services.env_service import EnvService
|
|
with app.app_context():
|
|
a = _make_app()
|
|
ev = _set_local(a.id, 'REF', '')
|
|
ev.set_reference({'kind': 'secret', 'secret': 'credential'})
|
|
db.session.commit()
|
|
def fail(*args):
|
|
raise AssertionError('metadata must not decrypt or resolve local values')
|
|
monkeypatch.setattr(EnvService, '_resolve_var_value', fail)
|
|
view = EnvService.get_effective_env_view(a.id)
|
|
assert view['env_vars'][0]['is_reference'] is True
|
|
assert 'credential' not in str(view)
|