| Index: scripts/tools/migrate_psql_to_sqlite.py
|
| diff --git a/scripts/tools/migrate_psql_to_sqlite.py b/scripts/tools/migrate_psql_to_sqlite.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..ff0a3fdd6f3c7425e271f28abf41bccbb1fa31df
|
| --- /dev/null
|
| +++ b/scripts/tools/migrate_psql_to_sqlite.py
|
| @@ -0,0 +1,148 @@
|
| +#!/usr/bin/env python
|
| +# Copyright 2016 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""This script will change a master from using postgresql to sqlite.
|
| +
|
| +It:
|
| +1) Creates .stop_master_lifecycle to stop master manager.
|
| +2) Stops the master.
|
| +3) Dumps the postgres database and imports it into sqlite.
|
| +4) Removes the .dbconfig file indicating buildbot should now use sqlite.
|
| +5) Starts the master.
|
| +6) Removes .stop_master_lifecycle.
|
| +
|
| +If this script fails in the middle you will have to restore it to a known state
|
| +manually.
|
| +
|
| +Run this script in the master's directory. Some third_party libraries need to
|
| +be on the PYTHONPATH, so use runit.py:
|
| + export TOOLS_DIR=~/buildbot/build/scripts/tools
|
| + ${TOOLS_DIR}/runit.py ${TOOLS_DIR}/migrate_psql_to_sqlite.py
|
| +
|
| +Without any arguments this will run in a dry-run mode and create a
|
| +'dry-run-psql-conversion.sqlite' file for you to inspect manually. Use
|
| +--no-dry-run to actually stop and restart the master and update the config.
|
| +"""
|
| +
|
| +import argparse
|
| +import logging
|
| +import os
|
| +import sqlite3
|
| +import subprocess
|
| +import sys
|
| +
|
| +from buildbot import cache
|
| +from buildbot.db import connector
|
| +from twisted.internet import defer, reactor
|
| +
|
| +
|
| +class FakeBuildMaster(object):
|
| + def __init__(self):
|
| + self.caches = cache.CacheManager()
|
| +
|
| +
|
| +@defer.inlineCallbacks
|
| +def Run(args):
|
| + if args.no_dry_run:
|
| + sqlite_filename = 'state.sqlite'
|
| + else:
|
| + sqlite_filename = 'dry-run-psql-conversion.sqlite'
|
| +
|
| + # Read the dbconfig. This will fail if the config doesn't exist and the
|
| + # master doesn't use postgresql.
|
| + dbconfig = {}
|
| + execfile('.dbconfig', dbconfig)
|
| +
|
| + if args.no_dry_run:
|
| + # Stop master manager from touching this master while we play with it.
|
| + if os.path.exists('.stop_master_lifecycle'):
|
| + raise Exception('A .stop_master_lifecycle file already exists')
|
| + logging.info('Creating .stop_master_lifecycle file')
|
| + with open('.stop_master_lifecycle', 'w') as fh:
|
| + fh.write('migrate_psql_to_sqlite.py')
|
| +
|
| + # Stop the master.
|
| + logging.info('Stopping master')
|
| + subprocess.check_call(['make', 'stop'])
|
| + subprocess.check_call(['make', 'wait'])
|
| +
|
| + # Dump the postgres database.
|
| + logging.info('Dumping postgres database %s', dbconfig['dbname'])
|
| + env = os.environ.copy()
|
| + env['PGPASSWORD'] = dbconfig['password']
|
| + sql = subprocess.check_output(['pg_dump',
|
| + '-d', dbconfig['dbname'],
|
| + '-U', dbconfig['username'],
|
| + '-h', 'localhost',
|
| + '--data-only',
|
| + '--inserts'], env=env)
|
| +
|
| + # Strip out postgres-specific things.
|
| + sql = '\n'.join(
|
| + line for line in sql.splitlines()
|
| + if not line.startswith('SET') and
|
| + not line.startswith('INSERT INTO migrate_version') and
|
| + not 'pg_catalog.setval' in line)
|
| +
|
| + # Delete any existing sqlite database.
|
| + if os.path.exists(sqlite_filename):
|
| + os.unlink(sqlite_filename)
|
| +
|
| + # Create the new sqlite database.
|
| + logging.info('Creating empty sqlite database in %s', sqlite_filename)
|
| + db = connector.DBConnector(
|
| + FakeBuildMaster(), 'sqlite:///%s' % sqlite_filename, '.')
|
| + yield db.model.upgrade()
|
| +
|
| + # Import the data into the sqlite database.
|
| + logging.info('Filling sqlite database %s', sqlite_filename)
|
| + conn = sqlite3.connect(sqlite_filename)
|
| + cursor = conn.cursor()
|
| + cursor.execute('pragma synchronous = off')
|
| + cursor.execute('pragma journal_mode = memory')
|
| + cursor.executescript(sql)
|
| + conn.commit()
|
| + conn.close()
|
| +
|
| + if args.no_dry_run:
|
| + # Remove the .dbconfig to make it use the sqlite database.
|
| + logging.info('Moving .dbconfig file to dbconfig.bak')
|
| + os.rename('.dbconfig', 'dbconfig.bak')
|
| +
|
| + # Start the master.
|
| + logging.info('Starting master')
|
| + subprocess.check_call(['make', 'start'])
|
| +
|
| + # Let master manager take over again.
|
| + logging.info('Removing .stop_master_lifecycle file')
|
| + os.unlink('.stop_master_lifecycle')
|
| +
|
| + logging.info('Done!')
|
| + else:
|
| + logging.info('Dry-run done!')
|
| +
|
| +
|
| +def main():
|
| + parser = argparse.ArgumentParser()
|
| + parser.add_argument('--no-dry-run', action='store_true')
|
| + args = parser.parse_args()
|
| +
|
| + def HandleError(err):
|
| + err.printTraceback()
|
| + reactor.stop()
|
| +
|
| + def Start():
|
| + d = Run(args)
|
| + d.addCallback(lambda _: reactor.stop())
|
| + d.addErrback(HandleError)
|
| +
|
| + logging.basicConfig(level=logging.INFO,
|
| + format='\033[94m%(asctime)s %(message)s\033[0m')
|
| + reactor.callWhenRunning(Start)
|
| + reactor.run()
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + main()
|
|
|