31827cd09638b74fcc0c8492fb9447e09c08aaa0
[mediagoblin.git] / mediagoblin / gmg_commands / dbupdate.py
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
17 import logging
18
19 import six
20 from sqlalchemy.orm import sessionmaker
21
22 from mediagoblin.db.open import setup_connection_and_db_from_config
23 from mediagoblin.db.migration_tools import MigrationManager, AlembicMigrationManager
24 from mediagoblin.init import setup_global_and_app_config
25 from mediagoblin.tools.common import import_component
26
27 _log = logging.getLogger(__name__)
28 logging.basicConfig()
29 ## Let's not set the level as debug by default to avoid confusing users :)
30 # _log.setLevel(logging.DEBUG)
31
32
33 def dbupdate_parse_setup(subparser):
34 pass
35
36
37 class DatabaseData(object):
38 def __init__(self, name, models, foundations, migrations):
39 self.name = name
40 self.models = models
41 self.foundations = foundations
42 self.migrations = migrations
43
44 def make_migration_manager(self, session):
45 return MigrationManager(
46 self.name, self.models, self.foundations, self.migrations, session)
47
48
49 def gather_database_data(plugins):
50 """
51 Gather all database data relevant to the extensions we have
52 installed so we can do migrations and table initialization.
53
54 Returns a list of DatabaseData objects.
55 """
56 managed_dbdata = []
57
58 # Add main first
59 from mediagoblin.db.models import MODELS as MAIN_MODELS
60 from mediagoblin.db.migrations import MIGRATIONS as MAIN_MIGRATIONS
61 from mediagoblin.db.models import FOUNDATIONS as MAIN_FOUNDATIONS
62
63 managed_dbdata.append(
64 DatabaseData(
65 u'__main__', MAIN_MODELS, MAIN_FOUNDATIONS, MAIN_MIGRATIONS))
66
67 for plugin in plugins:
68 try:
69 models = import_component('{0}.models:MODELS'.format(plugin))
70 except ImportError as exc:
71 _log.debug('No models found for {0}: {1}'.format(
72 plugin,
73 exc))
74
75 models = []
76 except AttributeError as exc:
77 _log.warning('Could not find MODELS in {0}.models, have you \
78 forgotten to add it? ({1})'.format(plugin, exc))
79 models = []
80
81 try:
82 migrations = import_component('{0}.migrations:MIGRATIONS'.format(
83 plugin))
84 except ImportError as exc:
85 _log.debug('No migrations found for {0}: {1}'.format(
86 plugin,
87 exc))
88
89 migrations = {}
90 except AttributeError as exc:
91 _log.debug('Could not find MIGRATIONS in {0}.migrations, have you \
92 forgotten to add it? ({1})'.format(plugin, exc))
93 migrations = {}
94
95 try:
96 foundations = import_component('{0}.models:FOUNDATIONS'.format(plugin))
97 except ImportError as exc:
98 foundations = {}
99 except AttributeError as exc:
100 foundations = {}
101
102 if models:
103 managed_dbdata.append(
104 DatabaseData(plugin, models, foundations, migrations))
105
106
107 return managed_dbdata
108
109
110 def run_alembic_migrations(db, app_config, global_config):
111 """Initializes a database and runs all Alembic migrations."""
112 Session = sessionmaker(bind=db.engine)
113 manager = AlembicMigrationManager(Session())
114 manager.init_or_migrate()
115
116
117 def run_dbupdate(app_config, global_config):
118 """
119 Initialize or migrate the database as specified by the config file.
120
121 Will also initialize or migrate all extensions (media types, and
122 in the future, plugins)
123 """
124
125 # Set up the database
126 db = setup_connection_and_db_from_config(app_config, migrations=True)
127 # Run the migrations
128 run_all_migrations(db, app_config, global_config)
129
130 # TODO: Make this happen regardless of python 2 or 3 once ensured
131 # to be "safe"!
132 if six.PY3:
133 run_alembic_migrations(db, app_config, global_config)
134
135
136 def run_all_migrations(db, app_config, global_config):
137 """
138 Initializes or migrates a database that already has a
139 connection setup and also initializes or migrates all
140 extensions based on the config files.
141
142 It can be used to initialize an in-memory database for
143 testing.
144 """
145 # Gather information from all media managers / projects
146 dbdatas = gather_database_data(
147 list(global_config.get('plugins', {}).keys()))
148
149 Session = sessionmaker(bind=db.engine)
150
151 # Setup media managers for all dbdata, run init/migrate and print info
152 # For each component, create/migrate tables
153 for dbdata in dbdatas:
154 migration_manager = dbdata.make_migration_manager(Session())
155 migration_manager.init_or_migrate()
156
157
158 def dbupdate(args):
159 global_config, app_config = setup_global_and_app_config(args.conf_file)
160 run_dbupdate(app_config, global_config)