Coverage for mcpgateway/bootstrap_db.py: 83%

22 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-09 11:03 +0100

1# -*- coding: utf-8 -*- 

2"""Database bootstrap/upgrade entry-point for MCP Gateway. 

3 

4Copyright 2025 

5SPDX-License-Identifier: Apache-2.0 

6Authors: Madhav Kandukuri 

7 

8The script: 

9 

101. Creates a synchronous SQLAlchemy ``Engine`` from ``settings.database_url``. 

112. Looks for an *alembic.ini* two levels up from this file to drive migrations. 

123. If the database is still empty (no ``gateways`` table), it: 

13 - builds the base schema with ``Base.metadata.create_all()`` 

14 - stamps the migration head so Alembic knows it is up-to-date 

154. Otherwise, it applies any outstanding Alembic revisions. 

165. Logs a **"Database ready"** message on success. 

17 

18It is intended to be invoked via ``python -m mcpgateway.bootstrap_db`` or 

19directly with ``python mcpgateway/bootstrap_db.py``. 

20""" 

21 

22# Standard 

23import asyncio 

24from importlib.resources import files 

25import logging 

26 

27# Third-Party 

28from alembic.config import Config 

29from sqlalchemy import create_engine, inspect 

30 

31# First-Party 

32from alembic import command 

33from mcpgateway.config import settings 

34from mcpgateway.db import Base 

35 

36logger = logging.getLogger(__name__) 

37 

38 

39async def main() -> None: 

40 """ 

41 Bootstrap or upgrade the database schema, then log readiness. 

42 

43 Runs `create_all()` + `alembic stamp head` on an empty DB, otherwise just 

44 executes `alembic upgrade head`, leaving application data intact. 

45 

46 Args: 

47 None 

48 """ 

49 engine = create_engine(settings.database_url) 

50 ini_path = files("mcpgateway").joinpath("alembic.ini") 

51 cfg = Config(str(ini_path)) # path in container 

52 cfg.attributes["configure_logger"] = False 

53 

54 command.ensure_version(cfg) 

55 

56 insp = inspect(engine) 

57 if "gateways" not in insp.get_table_names(): 57 ↛ 58line 57 didn't jump to line 58 because the condition on line 57 was never true

58 logger.info("Empty DB detected - creating baseline schema") 

59 Base.metadata.create_all(engine) 

60 command.stamp(cfg, "head") # record baseline 

61 else: 

62 command.upgrade(cfg, "head") # apply any new revisions 

63 logger.info("Database ready") 

64 

65 

66if __name__ == "__main__": 

67 asyncio.run(main())