Example #1
0
    def test_repo_dir_load(self):
        patch_dir = os.path.join(self.tmp_dir_path, 'patch1')
        os.mkdir(patch_dir)
        with open(os.path.join(patch_dir, 'depends_on'), 'wb') as fp:
            fp.write('patch2\n')
            fp.write('patch7\n')
        with open(os.path.join(patch_dir, 'upgrade.sql'), 'wb') as fp:
            fp.write('SELECT 1\n')
        with open(os.path.join(patch_dir, 'downgrade.sql'), 'wb') as fp:
            fp.write('SELECT 2\n')

        patch_dir = os.path.join(self.tmp_dir_path, 'patch2')
        os.mkdir(patch_dir)
        with open(os.path.join(patch_dir, 'upgrade.sql'), 'wb') as fp:
            fp.write('SELECT 3\n')
        with open(os.path.join(patch_dir, 'downgrade.sql'), 'wb') as fp:
            fp.write('SELECT 4\n')

        patch_dir = os.path.join(self.tmp_dir_path, 'patch7')
        os.mkdir(patch_dir)
        with open(os.path.join(patch_dir, 'depends_on'), 'wb') as fp:
            fp.write('patch2\n')
        with open(os.path.join(patch_dir, 'upgrade.sql'), 'wb') as fp:
            fp.write('SELECT 5\n')

        open(os.path.join(self.tmp_dir_path, 'unrelated_file'), 'wb').close()

        repo_loader = DirPatchRepositoryLoader(patch_loader=DirPatchLoader())
        repo = repo_loader.load_repo(self.tmp_dir_path)
        eq_(set(repo.patches.keys()), set(['patch1', 'patch2', 'patch7']))
        patch = repo.patches['patch7']
        eq_(patch.name, 'patch7')
        eq_(set(patch.depends_on_names), set([('patch2', False)]))
        eq_(patch.upgrade_sql, 'SELECT 5\n')
        eq_(patch.downgrade_sql, None)

        repo.resolve_dependencies()
        eq_(set(patch.depends_on), set([repo.patches['patch2']]))
Example #2
0
def main():
    parser = argparse.ArgumentParser(
            description='Migrate SQL schemas (and data) from one set of SQL '
            'patches to another set.')
    cmd_parser = parser.add_subparsers(title='migration commands',
            description='the list of migration operations that can be '
            'performed')

    init_parser = cmd_parser.add_parser('init', help='initialise the '
            'repository with the book-keeping tables needed for migration')
    patch_group = init_parser.add_mutually_exclusive_group()
    patch_group.add_argument('--patches', metavar='PATCH', nargs='+',
            help='list of patches that are assumed to be applied already',
            default=[])
    patch_group.add_argument('--all-patches', help='assume that all currently '
            'known patches were applied already', action='store_true',
            default=False)
    init_parser.set_defaults(cmd_func=cmd_init)

    convert_init_parser = cmd_parser.add_parser('convert-init',
            help='convert a repository from the pre-0.15 format to the ' \
            'current repository format, detecting all applied patches')
    convert_init_parser.set_defaults(cmd_func=cmd_convert_init)

    uninit_parser = cmd_parser.add_parser('uninit', help='uninitialise the '
            'repository by removing the book-keeping tables')
    uninit_parser.set_defaults(cmd_func=cmd_uninit)

    status_parser = cmd_parser.add_parser('status', help='list which patches '\
            'are currently applied to the repository')
    status_parser.set_defaults(cmd_func=cmd_status)

    upgrade_parser = cmd_parser.add_parser('upgrade', help='upgrade the '
            'repository by applying additional SQL patches')
    upgrade_parser.add_argument('patches', metavar='PATCH', nargs='*',
            help='list of patches that will be applied (defaults to all '
            'missing patches)', default=[])
    upgrade_parser.add_argument('--skip-sql', help='only modify the metadata '
            'but do not execute the SQL of the patches', action='store_true',
            default=False)
    upgrade_parser.set_defaults(cmd_func=cmd_upgrade)

    test_parser = cmd_parser.add_parser('test', help='test the '
            'specified SQL patch')
    test_parser.add_argument('patches', metavar='PATCH', nargs='*',
            help='list of patches that will be tested (defaults to all '
            'missing patches)', default=[])
    test_parser.set_defaults(cmd_func=cmd_test)

    downgrade_parser = cmd_parser.add_parser('downgrade', help='downgrade the '
            'repository by reverting SQL patches')
    downgrade_parser.add_argument('patches', metavar='PATCH', nargs='*',
            help='list of patches to remove (defaults to all applied patches)',
            default=[])
    downgrade_parser.add_argument('--skip-sql', help='only modify the metadata '
            'but do not execute the SQL of the patches', action='store_true',
            default=False)
    downgrade_parser.set_defaults(cmd_func=cmd_downgrade)

    renew_parser = cmd_parser.add_parser('renew', help='renew the '
            'repository by reverting and then reapplying the specified SQL '
            'patches. Additional patches that needed to be reverted will be '
            're-applied too.')
    renew_parser.add_argument('patches', metavar='PATCH', nargs='+',
            help='list of patches that will be reverted and then reapplied',
            default=[])
    renew_parser.set_defaults(cmd_func=cmd_renew)

    calc_minimal_parser = cmd_parser.add_parser('calc-minimal',
            help='calculcate the minimal set of patches necessary to cause the '
            'listed set of patches to be applied')
    calc_minimal_parser.add_argument('patches', metavar='PATCH', nargs='+',
            help='list of patches for which the minimal set will be determined')
    calc_minimal_parser.set_defaults(cmd_func=cmd_calc_minimal)

    parser.add_argument('--add-repo', help='additional repository of patches '
            'to query', metavar='REPO', dest='repo_paths',
            action='append', default=[])
    parser.add_argument('--simulate', help='rollback all changes afterwards',
            action='store_true', default=False)
    parser.add_argument('url', help='SQL database connection URL')

    options = parser.parse_args()

    engine = open_engine(options.url)
    Session = sessionmaker(bind=engine, autocommit=False)
    sess = Session()

    repo_loader = DirPatchRepositoryLoader(patch_loader=DirPatchLoader())
    repo = repo_loader.load_repo(PATCH_REPO_PATH)
    for repo_path in options.repo_paths:
        override_repo = repo_loader.load_repo(repo_path)
        repo.patches.update(override_repo.patches)
    repo.resolve_dependencies()

    try:
        driver = Driver(sess, repo)
        options.cmd_func(options=options, repo=repo, driver=driver)
        if options.simulate:
            print >>sys.stderr, "notice: simulation option set, rolling back "\
                    "any changes."
            sess.rollback()
        else:
            sess.commit()
    except PatchFailedException, ex:
        print >>sys.stderr, "error: %s" % (ex.args[0])
        if ex.details is not None:
            for detail in ex.details:
                print >>sys.stderr, "error: details: %s" % (detail)
        print >>sys.stderr, "notice: rolling back any changes to the database."
        sess.rollback()
        sys.exit(1)