dbupdate.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. # GNU MediaGoblin -- federated, autonomous media hosting
  2. # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU Affero General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU Affero General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Affero General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. import logging
  17. import six
  18. from sqlalchemy.orm import sessionmaker
  19. from mediagoblin.db.open import setup_connection_and_db_from_config
  20. from mediagoblin.db.migration_tools import MigrationManager, AlembicMigrationManager
  21. from mediagoblin.init import setup_global_and_app_config
  22. from mediagoblin.tools.common import import_component
  23. _log = logging.getLogger(__name__)
  24. logging.basicConfig()
  25. ## Let's not set the level as debug by default to avoid confusing users :)
  26. # _log.setLevel(logging.DEBUG)
  27. def dbupdate_parse_setup(subparser):
  28. pass
  29. class DatabaseData(object):
  30. def __init__(self, name, models, foundations, migrations):
  31. self.name = name
  32. self.models = models
  33. self.foundations = foundations
  34. self.migrations = migrations
  35. def make_migration_manager(self, session):
  36. return MigrationManager(
  37. self.name, self.models, self.foundations, self.migrations, session)
  38. def gather_database_data(plugins):
  39. """
  40. Gather all database data relevant to the extensions we have
  41. installed so we can do migrations and table initialization.
  42. Returns a list of DatabaseData objects.
  43. """
  44. managed_dbdata = []
  45. # Add main first
  46. from mediagoblin.db.models import MODELS as MAIN_MODELS
  47. from mediagoblin.db.migrations import MIGRATIONS as MAIN_MIGRATIONS
  48. from mediagoblin.db.models import FOUNDATIONS as MAIN_FOUNDATIONS
  49. managed_dbdata.append(
  50. DatabaseData(
  51. u'__main__', MAIN_MODELS, MAIN_FOUNDATIONS, MAIN_MIGRATIONS))
  52. for plugin in plugins:
  53. try:
  54. models = import_component('{0}.models:MODELS'.format(plugin))
  55. except ImportError as exc:
  56. _log.debug('No models found for {0}: {1}'.format(
  57. plugin,
  58. exc))
  59. models = []
  60. except AttributeError as exc:
  61. _log.warning('Could not find MODELS in {0}.models, have you \
  62. forgotten to add it? ({1})'.format(plugin, exc))
  63. models = []
  64. try:
  65. migrations = import_component('{0}.migrations:MIGRATIONS'.format(
  66. plugin))
  67. except ImportError as exc:
  68. _log.debug('No migrations found for {0}: {1}'.format(
  69. plugin,
  70. exc))
  71. migrations = {}
  72. except AttributeError as exc:
  73. _log.debug('Could not find MIGRATIONS in {0}.migrations, have you \
  74. forgotten to add it? ({1})'.format(plugin, exc))
  75. migrations = {}
  76. try:
  77. foundations = import_component('{0}.models:FOUNDATIONS'.format(plugin))
  78. except ImportError as exc:
  79. foundations = {}
  80. except AttributeError as exc:
  81. foundations = {}
  82. if models:
  83. managed_dbdata.append(
  84. DatabaseData(plugin, models, foundations, migrations))
  85. return managed_dbdata
  86. def run_alembic_migrations(db, app_config, global_config):
  87. """Initializes a database and runs all Alembic migrations."""
  88. Session = sessionmaker(bind=db.engine)
  89. manager = AlembicMigrationManager(Session())
  90. manager.init_or_migrate()
  91. def run_dbupdate(app_config, global_config):
  92. """
  93. Initialize or migrate the database as specified by the config file.
  94. Will also initialize or migrate all extensions (media types, and
  95. in the future, plugins)
  96. """
  97. # Set up the database
  98. db = setup_connection_and_db_from_config(app_config, migrations=True)
  99. # Run the migrations
  100. run_all_migrations(db, app_config, global_config)
  101. # TODO: Make this happen regardless of python 2 or 3 once ensured
  102. # to be "safe"!
  103. if six.PY3:
  104. run_alembic_migrations(db, app_config, global_config)
  105. def run_all_migrations(db, app_config, global_config):
  106. """
  107. Initializes or migrates a database that already has a
  108. connection setup and also initializes or migrates all
  109. extensions based on the config files.
  110. It can be used to initialize an in-memory database for
  111. testing.
  112. """
  113. # Gather information from all media managers / projects
  114. dbdatas = gather_database_data(
  115. list(global_config.get('plugins', {}).keys()))
  116. Session = sessionmaker(bind=db.engine)
  117. # Setup media managers for all dbdata, run init/migrate and print info
  118. # For each component, create/migrate tables
  119. for dbdata in dbdatas:
  120. migration_manager = dbdata.make_migration_manager(Session())
  121. migration_manager.init_or_migrate()
  122. def dbupdate(args):
  123. global_config, app_config = setup_global_and_app_config(args.conf_file)
  124. run_dbupdate(app_config, global_config)