Exemplo n.º 1
0
    def test__find_milestone_revisions_branchless(self, walk_mock):
        revisions = [FakeRevision() for r in range(5)]
        revisions[2].module.neutron_milestone = [migration.LIBERTY]

        walk_mock.return_value = revisions
        m = cli._find_milestone_revisions(self.configs[0], 'liberty')
        self.assertEqual(1, len(m))

        m = cli._find_milestone_revisions(self.configs[0], 'mitaka')
        self.assertEqual(0, len(m))
Exemplo n.º 2
0
    def test__find_milestone_revisions_branchless(self, walk_mock):
        revisions = [FakeRevision() for r in range(5)]
        revisions[2].module.neutron_milestone = [migration.LIBERTY]

        walk_mock.return_value = revisions
        m = cli._find_milestone_revisions(self.configs[0], 'liberty')
        self.assertEqual(1, len(m))

        m = cli._find_milestone_revisions(self.configs[0], 'mitaka')
        self.assertEqual(0, len(m))
Exemplo n.º 3
0
    def test__find_milestone_revisions_one_branch(self, walk_mock):
        c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)]
        c_revs[1].module.neutron_milestone = [migration.LIBERTY]

        walk_mock.return_value = c_revs
        m = cli._find_milestone_revisions(self.configs[0], 'liberty',
                                          cli.CONTRACT_BRANCH)
        self.assertEqual(1, len(m))
        m = cli._find_milestone_revisions(self.configs[0], 'liberty',
                                          cli.EXPAND_BRANCH)
        self.assertEqual(0, len(m))
Exemplo n.º 4
0
    def test__find_milestone_revisions_one_branch(self, walk_mock):
        c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)]
        c_revs[1].module.neutron_milestone = [migration.LIBERTY]

        walk_mock.return_value = c_revs
        m = cli._find_milestone_revisions(self.configs[0], 'liberty',
                                          cli.CONTRACT_BRANCH)
        self.assertEqual(1, len(m))
        m = cli._find_milestone_revisions(self.configs[0], 'liberty',
                                          cli.EXPAND_BRANCH)
        self.assertEqual(0, len(m))
Exemplo n.º 5
0
    def test__find_milestone_revisions_two_branches(self, walk_mock):
        c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)]
        c_revs[1].module.neutron_milestone = [migration.MITAKA]
        e_revs = [FakeRevision(labels={cli.EXPAND_BRANCH}) for r in range(5)]
        e_revs[3].module.neutron_milestone = [migration.MITAKA]

        walk_mock.return_value = c_revs + e_revs
        m = cli._find_milestone_revisions(self.configs[0], 'mitaka')
        self.assertEqual(2, len(m))

        m = cli._find_milestone_revisions(self.configs[0], 'newton')
        self.assertEqual(0, len(m))
Exemplo n.º 6
0
    def _test__find_milestone_revisions_one_branch(self,
                                                   contract_label,
                                                   expand_label,
                                                   walk_mock):
        c_revs = [FakeRevision(labels={contract_label}) for r in range(5)]
        c_revs[1].module.neutron_milestone = [migration.LIBERTY]

        walk_mock.return_value = c_revs
        m = cli._find_milestone_revisions(self.configs[0], 'liberty',
                                          contract_label)
        self.assertEqual(1, len(m))
        m = cli._find_milestone_revisions(self.configs[0], 'liberty',
                                          expand_label)
        self.assertEqual(0, len(m))
Exemplo n.º 7
0
    def test_branches(self):

        drop_exceptions = collections.defaultdict(list)
        creation_exceptions = collections.defaultdict(list)

        def find_migration_exceptions():
            # Due to some misunderstandings and some conscious decisions,
            # there may be some expand migrations which drop elements and
            # some contract migrations which create elements. These excepted
            # elements must be returned by a method in the script itself.
            # The names of the method must be 'contract_creation_exceptions'
            # or 'expand_drop_exceptions'. The methods must have a docstring
            # explaining the reason for the exception.
            #
            # Here we build lists of the excepted elements and verify that
            # they are documented.
            script = alembic_script.ScriptDirectory.from_config(
                self.alembic_config)
            for m in list(script.walk_revisions(base='base', head='heads')):
                branches = m.branch_labels or []
                if migration.CONTRACT_BRANCH in branches:
                    method_name = 'contract_creation_exceptions'
                    exceptions_dict = creation_exceptions
                elif migration.EXPAND_BRANCH in branches:
                    method_name = 'expand_drop_exceptions'
                    exceptions_dict = drop_exceptions
                else:
                    continue
                get_excepted_elements = getattr(m.module, method_name, None)
                if not get_excepted_elements:
                    continue
                explanation = getattr(get_excepted_elements, '__doc__', "")
                if len(explanation) < 1:
                    self.fail(
                        "%s() requires docstring with explanation" % '.'.join([
                            m.module.__name__, get_excepted_elements.__name__
                        ]))
                for sa_type, elements in get_excepted_elements().items():
                    exceptions_dict[sa_type].extend(elements)

        def is_excepted_sqla(clauseelement, exceptions):
            """Identify excepted operations that are allowed for the branch."""
            element = clauseelement.element
            element_name = element.name
            if isinstance(element, sqlalchemy.Index):
                element_name = element.table.name
            for sa_type_, excepted_names in exceptions.items():
                if isinstance(element, sa_type_):
                    if element_name in excepted_names:
                        return True

        def is_excepted_alembic(clauseelement, exceptions):
            """Identify excepted operations that are allowed for the branch."""
            # For alembic the clause is AddColumn or DropColumn
            column = clauseelement.column.name
            table = clauseelement.column.table.name
            element_name = '.'.join([table, column])
            for alembic_type, excepted_names in exceptions.items():
                if alembic_type == sqlalchemy.Column:
                    if element_name in excepted_names:
                        return True

        def is_allowed(clauseelement, exceptions, disallowed_ops):
            if (isinstance(clauseelement, disallowed_ops['sqla'])
                    and hasattr(clauseelement, 'element')):
                return is_excepted_sqla(clauseelement, exceptions)
            if isinstance(clauseelement, disallowed_ops['alembic']):
                return is_excepted_alembic(clauseelement, exceptions)
            return True

        def check_expand_branch(conn, clauseelement, multiparams, params):
            if not is_allowed(clauseelement, drop_exceptions, DROP_OPERATIONS):
                self.fail("Migration in expand branch contains drop command")

        def check_contract_branch(conn, clauseelement, multiparams, params):
            if not is_allowed(clauseelement, creation_exceptions,
                              CREATION_OPERATIONS):
                self.fail("Migration in contract branch contains create "
                          "command")

        find_migration_exceptions()
        engine = self.engine
        cfg.CONF.set_override('connection', engine.url, group='database')

        with engine.begin() as connection:
            self.alembic_config.attributes['connection'] = connection

            # upgrade to latest release first; --expand users are expected to
            # apply all alembic scripts from previous releases before applying
            # the new ones
            for release in migration_root.NEUTRON_MILESTONES:
                release_revisions = migration._find_milestone_revisions(
                    self.alembic_config, release)
                for rev in release_revisions:
                    migration.do_alembic_command(self.alembic_config,
                                                 'upgrade', rev[0])

            with self._listener(engine, check_expand_branch):
                migration.do_alembic_command(
                    self.alembic_config, 'upgrade',
                    '%s@head' % migration.EXPAND_BRANCH)

            with self._listener(engine, check_contract_branch):
                migration.do_alembic_command(
                    self.alembic_config, 'upgrade',
                    '%s@head' % migration.CONTRACT_BRANCH)
Exemplo n.º 8
0
    def test_branches(self):

        drop_exceptions = collections.defaultdict(list)
        creation_exceptions = collections.defaultdict(list)

        def find_migration_exceptions():
            # Due to some misunderstandings and some conscious decisions,
            # there may be some expand migrations which drop elements and
            # some contract migrations which create elements. These excepted
            # elements must be returned by a method in the script itself.
            # The names of the method must be 'contract_creation_exceptions'
            # or 'expand_drop_exceptions'. The methods must have a docstring
            # explaining the reason for the exception.
            #
            # Here we build lists of the excepted elements and verify that
            # they are documented.
            script = alembic_script.ScriptDirectory.from_config(
                self.alembic_config)
            for m in list(script.walk_revisions(base='base', head='heads')):
                branches = m.branch_labels or []
                if migration.CONTRACT_BRANCH in branches:
                    method_name = 'contract_creation_exceptions'
                    exceptions_dict = creation_exceptions
                elif migration.EXPAND_BRANCH in branches:
                    method_name = 'expand_drop_exceptions'
                    exceptions_dict = drop_exceptions
                else:
                    continue
                get_excepted_elements = getattr(m.module, method_name, None)
                if not get_excepted_elements:
                    continue
                explanation = getattr(get_excepted_elements, '__doc__', "")
                if len(explanation) < 1:
                    self.fail("%s() requires docstring with explanation" %
                              '.'.join([m.module.__name__,
                                        get_excepted_elements.__name__]))
                for sa_type, elements in get_excepted_elements().items():
                    exceptions_dict[sa_type].extend(elements)

        def is_excepted_sqla(clauseelement, exceptions):
            """Identify excepted operations that are allowed for the branch."""
            element = clauseelement.element
            element_name = element.name
            if isinstance(element, sqlalchemy.Index):
                element_name = element.table.name
            for sa_type_, excepted_names in exceptions.items():
                if isinstance(element, sa_type_):
                    if element_name in excepted_names:
                        return True

        def is_excepted_alembic(clauseelement, exceptions):
            """Identify excepted operations that are allowed for the branch."""
            # For alembic the clause is AddColumn or DropColumn
            column = clauseelement.column.name
            table = clauseelement.column.table.name
            element_name = '.'.join([table, column])
            for alembic_type, excepted_names in exceptions.items():
                if alembic_type == sqlalchemy.Column:
                    if element_name in excepted_names:
                        return True

        def is_allowed(clauseelement, exceptions, disallowed_ops):
            if (isinstance(clauseelement, disallowed_ops['sqla']) and
                    hasattr(clauseelement, 'element')):
                return is_excepted_sqla(clauseelement, exceptions)
            if isinstance(clauseelement, disallowed_ops['alembic']):
                return is_excepted_alembic(clauseelement, exceptions)
            return True

        def check_expand_branch(conn, clauseelement, multiparams, params):
            if not is_allowed(clauseelement, drop_exceptions, DROP_OPERATIONS):
                self.fail("Migration in expand branch contains drop command")

        def check_contract_branch(conn, clauseelement, multiparams, params):
            if not is_allowed(clauseelement, creation_exceptions,
                              CREATION_OPERATIONS):
                self.fail("Migration in contract branch contains create "
                          "command")

        find_migration_exceptions()
        engine = self.engine
        cfg.CONF.set_override('connection', engine.url, group='database')

        with engine.begin() as connection:
            self.alembic_config.attributes['connection'] = connection

            # upgrade to latest release first; --expand users are expected to
            # apply all alembic scripts from previous releases before applying
            # the new ones
            for release in migration_root.NEUTRON_MILESTONES:
                release_revisions = migration._find_milestone_revisions(
                    self.alembic_config, release)
                for rev in release_revisions:
                    migration.do_alembic_command(
                        self.alembic_config, 'upgrade', rev[0])

            with self._listener(engine, check_expand_branch):
                migration.do_alembic_command(
                    self.alembic_config, 'upgrade',
                    '%s@head' % migration.EXPAND_BRANCH)

            with self._listener(engine, check_contract_branch):
                migration.do_alembic_command(
                    self.alembic_config, 'upgrade',
                    '%s@head' % migration.CONTRACT_BRANCH)