Exemplo n.º 1
0
    def setUp(self):
        # NOTE (eli): mocking doesn't work correctly
        # when we try to patch docker client with
        # class decorator, it's the reason why
        # we have to do it explicitly
        self.docker_patcher = mock.patch(
            'fuel_upgrade.engines.docker_engine.docker.Client')
        self.docker_mock_class = self.docker_patcher.start()
        self.docker_mock = mock.MagicMock()
        self.docker_mock_class.return_value = self.docker_mock

        self.supervisor_patcher = mock.patch(
            'fuel_upgrade.engines.docker_engine.SupervisorClient')
        self.supervisor_class = self.supervisor_patcher.start()
        self.supervisor_mock = mock.MagicMock()
        self.supervisor_class.return_value = self.supervisor_mock

        self.version_mock = mock.MagicMock()

        with mock.patch('fuel_upgrade.engines.docker_engine.utils'):
            with mock.patch('fuel_upgrade.engines.docker_engine.VersionFile',
                            return_value=self.version_mock):
                self.upgrader = DockerUpgrader(self.fake_config)
                self.upgrader.upgrade_verifier = mock.MagicMock()

        self.pg_dump_path = '/var/lib/fuel_upgrade/9999/pg_dump_all.sql'
Exemplo n.º 2
0
class TestDockerUpgrader(BaseTestCase):

    def setUp(self):
        # NOTE (eli): mocking doesn't work correctly
        # when we try to patch docker client with
        # class decorator, it's the reason why
        # we have to do it explicitly
        self.docker_patcher = mock.patch(
            'fuel_upgrade.engines.docker_engine.docker.Client')
        self.docker_mock_class = self.docker_patcher.start()
        self.docker_mock = mock.MagicMock()
        self.docker_mock_class.return_value = self.docker_mock

        self.supervisor_patcher = mock.patch(
            'fuel_upgrade.engines.docker_engine.SupervisorClient')
        self.supervisor_class = self.supervisor_patcher.start()
        self.supervisor_mock = mock.MagicMock()
        self.supervisor_class.return_value = self.supervisor_mock

        with mock.patch('fuel_upgrade.engines.docker_engine.utils'):
            with mock.patch('fuel_upgrade.engines.docker_engine.'
                            'from_fuel_version', return_value='0'):
                self.upgrader = DockerUpgrader(self.fake_config)
                self.upgrader.upgrade_verifier = mock.MagicMock()

    def tearDown(self):
        self.docker_patcher.stop()
        self.supervisor_patcher.stop()

    def mock_methods(self, obj, methods):
        for method in methods:
            setattr(obj, method, mock.MagicMock())

    def test_upgrade(self):
        mocked_methods = [
            'stop_fuel_containers',
            'save_db',
            'save_cobbler_configs',
            'upload_images',
            'create_containers',
            'generate_configs',
            'switch_to_new_configs',
            'switch_version_to_new']

        self.mock_methods(self.upgrader, mocked_methods)
        self.upgrader.upgrade()

        # Check that all methods was called once
        # except stop_fuel_containers method
        for method in mocked_methods[1:-1]:
            self.called_once(getattr(self.upgrader, method))

        self.called_times(self.upgrader.stop_fuel_containers, 3)

        self.called_once(self.supervisor_mock.stop_all_services)
        self.called_once(self.supervisor_mock.restart_and_wait)
        self.called_once(self.upgrader.upgrade_verifier.verify)

    def test_rollback(self):
        self.upgrader.stop_fuel_containers = mock.MagicMock()
        self.upgrader.switch_version_file_to_previous_version = \
            mock.MagicMock()
        self.upgrader.rollback()

        self.called_times(self.upgrader.stop_fuel_containers, 1)
        self.called_once(self.supervisor_mock.switch_to_previous_configs)
        self.called_once(self.supervisor_mock.stop_all_services)
        self.called_once(self.supervisor_mock.restart_and_wait)
        self.called_once(self.upgrader.switch_version_file_to_previous_version)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.symlink')
    def test_switch_version_file_to_previous_version(self, symlink_mock):
        self.upgrader.switch_version_file_to_previous_version()
        symlink_mock.assert_called_once_with(
            '/etc/fuel/0/version.yaml',
            '/etc/fuel/version.yaml')

    def test_stop_fuel_containers(self):
        non_fuel_images = [
            'first_image_1.0', 'second_image_2.0', 'third_image_2.0']
        fuel_images = [
            'fuel/image_1.0', 'fuel/image_2.0']

        all_images = [{'Image': v, 'Id': i}
                      for i, v in enumerate(non_fuel_images + fuel_images)]

        ports = [1, 2, 3]
        self.upgrader._get_docker_container_public_ports = mock.MagicMock(
            return_value=ports)
        self.upgrader.clean_docker_iptables_rules = mock.MagicMock()
        self.docker_mock.containers.return_value = all_images
        self.upgrader.stop_fuel_containers()
        self.assertEqual(
            self.docker_mock.stop.call_args_list, [((3, 10),), ((4, 10),)])
        self.upgrader.clean_docker_iptables_rules.assert_called_once_with(
            ports)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd')
    @mock.patch('fuel_upgrade.engines.docker_engine.os.path.exists',
                return_value=True)
    def test_upload_images(self, _, exec_mock):
        self.upgrader.new_release_images = [
            {'docker_image': 'image1'},
            {'docker_image': 'image2'}]

        self.upgrader.upload_images()
        self.assertEqual(
            exec_mock.call_args_list,
            [(('docker load < "image1"',),),
             (('docker load < "image2"',),)])

    def test_create_containers(self):
        self.upgrader.new_release_containers = [
            {'id': 'id1',
             'container_name': 'name1',
             'image_name': 'i_name1',
             'volumes_from': ['id2']},
            {'id': 'id2',
             'image_name': 'i_name2',
             'container_name': 'name2',
             'after_container_creation_command': 'cmd'}]

        def mocked_create_container(*args, **kwargs):
            """Return name of the container
            """
            return kwargs['name']

        self.upgrader.create_container = mock.MagicMock(
            side_effect=mocked_create_container)
        self.upgrader.start_container = mock.MagicMock()
        self.upgrader.run_after_container_creation_command = mock.MagicMock()

        self.upgrader.create_containers()

        create_container_calls = [
            (('i_name2',), {'detach': False, 'ports': None,
                            'volumes': None, 'name': 'name2'}),
            (('i_name1',), {'detach': False, 'ports': None,
                            'volumes': None, 'name': 'name1'})]

        start_container_calls = [
            (('name2',), {'volumes_from': [],
                          'binds': None, 'port_bindings': None,
                          'privileged': False, 'links': []}),
            (('name1',), {'volumes_from': ['name2'],
                          'binds': None, 'port_bindings': None,
                          'privileged': False, 'links': []})]

        self.assertEqual(
            self.upgrader.create_container.call_args_list,
            create_container_calls)
        self.assertEqual(
            self.upgrader.start_container.call_args_list,
            start_container_calls)
        self.called_once(self.upgrader.run_after_container_creation_command)

    def test_run_after_container_creation_command(self):
        self.upgrader.exec_with_retries = mock.MagicMock()
        self.upgrader.run_after_container_creation_command({
            'after_container_creation_command': 'cmd',
            'container_name': 'name'})

        args, kwargs = self.upgrader.exec_with_retries.call_args

        self.assertEqual(args[1], errors.ExecutedErrorNonZeroExitCode)
        self.assertEqual(kwargs, {'retries': 30, 'interval': 4})

    def test_create_container(self):
        self.upgrader.create_container(
            'image_name', param1=1, param2=2, ports=[1234])

        self.docker_mock.create_container.assert_called_once_with(
            'image_name', param2=2, param1=1, ports=[1234])

    def test_start_container(self):
        self.upgrader.start_container(
            {'Id': 'container_id'}, param1=1, param2=2)

        self.docker_mock.start.assert_called_once_with(
            'container_id', param2=2, param1=1)

    def test_build_dependencies_graph(self):
        containers = [
            {'id': '1', 'volumes_from': ['2'], 'links': [{'id': '3'}]},
            {'id': '2', 'volumes_from': [], 'links': []},
            {'id': '3', 'volumes_from': [], 'links': [{'id': '2'}]}]

        actual_graph = self.upgrader.build_dependencies_graph(containers)
        expected_graph = {
            '1': ['2', '3'],
            '2': [],
            '3': ['2']}

        self.assertEqual(actual_graph, expected_graph)

    def test_get_container_links(self):
        fake_containers = [
            {'id': 'id1', 'container_name': 'container_name1',
             'links': [{'id': 'id2', 'alias': 'alias2'}]},
            {'id': 'id2', 'container_name': 'container_name2'}]
        self.upgrader.new_release_containers = fake_containers
        links = self.upgrader.get_container_links(fake_containers[0])
        self.assertEqual(links, [('container_name2', 'alias2')])

    def test_get_port_bindings(self):
        port_bindings = {'port_bindings': {'53/udp': ['0.0.0.0', 53]}}
        bindings = self.upgrader.get_port_bindings(port_bindings)
        self.assertEqual({'53/udp': ('0.0.0.0', 53)}, bindings)

    def test_get_ports(self):
        ports = self.upgrader.get_ports({'ports': [[53, 'udp'], 100]})
        self.assertEqual([(53, 'udp'), 100], ports)

    def test_generate_configs(self):
        fake_containers = [
            {'id': 'id1', 'container_name': 'container_name1',
             'supervisor_config': False},
            {'id': 'id2', 'container_name': 'container_name2',
             'supervisor_config': True},
            {'id': 'cobbler', 'container_name': 'cobbler_container',
             'supervisor_config': False}]
        self.upgrader.new_release_containers = fake_containers
        self.upgrader.generate_configs()
        self.supervisor_mock.generate_configs.assert_called_once_with(
            [{'service_name': 'id2',
              'command': 'docker start -a container_name2'}])
        self.supervisor_mock.generate_cobbler_config.assert_called_once_with(
            {'service_name': 'cobbler', 'container_name': 'cobbler_container'})

    def test_switch_to_new_configs(self):
        self.upgrader.switch_to_new_configs()
        self.supervisor_mock.switch_to_new_configs.called_once()

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd')
    def test_exec_cmd_in_container(self, exec_cmd_mock):
        name = 'container_name'
        cmd = 'some command'

        self.upgrader.container_docker_id = mock.MagicMock(return_value=name)
        self.upgrader.exec_cmd_in_container(name, cmd)

        self.called_once(self.upgrader.container_docker_id)
        exec_cmd_mock.assert_called_once_with(
            "lxc-attach --name {0} -- {1}".format(name, cmd))

    @mock.patch('fuel_upgrade.engines.docker_engine.'
                'utils.exec_cmd')
    @mock.patch('fuel_upgrade.engines.docker_engine.'
                'DockerUpgrader.verify_cobbler_configs')
    def test_save_cobbler_configs(self, verify_mock, exec_cmd_mock):
        self.upgrader.save_cobbler_configs()

        exec_cmd_mock.assert_called_once_with(
            'docker cp fuel-core-0-cobbler:/var/lib/cobbler/config '
            '/var/lib/fuel_upgrade/9999/cobbler_configs')
        self.called_once(verify_mock)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.rmtree')
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd',
                side_effect=errors.ExecutedErrorNonZeroExitCode())
    def test_save_cobbler_configs_removes_dir_in_case_of_error(
            self, exec_cmd_mock, rm_mock):

        with self.assertRaises(errors.ExecutedErrorNonZeroExitCode):
            self.upgrader.save_cobbler_configs()

        cobbler_config_path = '/var/lib/fuel_upgrade/9999/cobbler_configs'
        exec_cmd_mock.assert_called_once_with(
            'docker cp fuel-core-0-cobbler:/var/lib/cobbler/config '
            '{0}'.format(cobbler_config_path))
        rm_mock.assert_called_once_with(cobbler_config_path)

    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=['1.json'])
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.'
                'check_file_is_valid_json')
    def test_verify_cobbler_configs(self, json_checker_mock, glob_mock):
        self.upgrader.verify_cobbler_configs()
        glob_mock.assert_called_once_with(
            '/var/lib/fuel_upgrade/9999/'
            'cobbler_configs/config/systems.d/*.json')
        json_checker_mock.assert_called_once_with('1.json')

    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=[])
    def test_verify_cobbler_configs_raises_error_if_not_enough_systems(
            self, glob_mock):

        with self.assertRaises(errors.WrongCobblerConfigsError):
            self.upgrader.verify_cobbler_configs()
        self.called_once(glob_mock)

    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=['1.json'])
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.'
                'check_file_is_valid_json', return_value=False)
    def test_verify_cobbler_configs_raises_error_if_invalid_file(
            self, json_checker_mock, glob_mock):

        with self.assertRaises(errors.WrongCobblerConfigsError):
            self.upgrader.verify_cobbler_configs()

        self.called_once(glob_mock)
        self.called_once(json_checker_mock)

    def test_save_db(self):
        self.upgrader.verify_postgres_dump = mock.MagicMock(return_value=True)
        self.upgrader.exec_cmd_in_container = mock.MagicMock()
        self.upgrader.save_db()

    def test_save_db_failed_verification(self):
        self.upgrader.verify_postgres_dump = mock.MagicMock(return_value=False)
        self.upgrader.exec_cmd_in_container = mock.MagicMock()
        self.assertRaises(errors.DatabaseDumpError, self.upgrader.save_db)

    @mock.patch(
        'fuel_upgrade.engines.docker_engine.utils.file_contains_lines',
        returns_value=True)
    @mock.patch(
        'fuel_upgrade.engines.docker_engine.os.path.exists',
        side_effect=[False, True])
    @mock.patch(
        'fuel_upgrade.engines.docker_engine.os.remove')
    def test_save_db_removes_file_in_case_of_error(
            self, remove_mock, _, __):
        self.upgrader.exec_cmd_in_container = mock.MagicMock(
            side_effect=errors.ExecutedErrorNonZeroExitCode())

        self.assertRaises(
            errors.ExecutedErrorNonZeroExitCode,
            self.upgrader.save_db)

        self.called_once(remove_mock)

    @mock.patch(
        'fuel_upgrade.engines.docker_engine.os.path.exists',
        return_value=True)
    @mock.patch(
        'fuel_upgrade.engines.docker_engine.utils.file_contains_lines',
        returns_value=True)
    def test_verify_postgres_dump(self, file_contains_mock, exists_mock):
        self.upgrader.verify_postgres_dump()

        patterns = [
            '-- PostgreSQL database cluster dump',
            '-- PostgreSQL database dump',
            '-- PostgreSQL database dump complete',
            '-- PostgreSQL database cluster dump complete']

        exists_mock.assert_called_once_with(self.upgrader.pg_dump_path)
        file_contains_mock.assert_called_once_with(
            self.upgrader.pg_dump_path,
            patterns)

    def test_get_docker_container_public_ports(self):
        docker_ports_mapping = [
            {'Ports': [
                {'PublicPort': 514},
                {'PublicPort': 515}]},
            {'Ports': [
                {'PublicPort': 516},
                {'PublicPort': 517}]}]

        self.assertEquals(
            [514, 515, 516, 517],
            self.upgrader._get_docker_container_public_ports(
                docker_ports_mapping))

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd')
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd_iterator')
    def test_clean_docker_iptables_rules(
            self, exec_cmd_iterator_mock, exec_cmd_mock):
        iptables_rules = [
            '-A DOCKER -p tcp -m tcp --dport 1 -j DNAT '
            '--to-destination 172.17.0.7:1',
            '-A POSTROUTING -p tcp -m tcp --dport 3 -j DNAT '
            '--to-destination 172.17.0.7:3',
            '-A DOCKER -p tcp -m tcp --dport 2 -j DNAT '
            '--to-destination 172.17.0.3:2']

        exec_cmd_iterator_mock.return_value = iter(iptables_rules)
        self.upgrader.clean_docker_iptables_rules([1, 2, 3])

        expected_calls = [
            (('iptables -t nat -D  DOCKER -p tcp -m tcp --dport 1 -j DNAT '
              '--to-destination 172.17.0.7:1',),),
            (('iptables -t nat -D  DOCKER -p tcp -m tcp --dport 2 -j DNAT '
              '--to-destination 172.17.0.3:2',),)]

        self.assertEquals(exec_cmd_mock.call_args_list, expected_calls)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.files_size',
                return_value=5)
    def test_required_free_space(self, _):
        self.assertEqual(
            self.upgrader.required_free_space,
            {'/var/lib/fuel_upgrade/9999': 50,
             '/var/lib/docker': 5,
             '/etc/fuel/': 10,
             '/etc/supervisord.d/': 10})

    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_save_current_version_file(self, mock_utils):
        self.upgrader.save_current_version_file()
        mock_utils.copy_if_does_not_exist.assert_called_once_with(
            '/etc/fuel/version.yaml',
            '/var/lib/fuel_upgrade/9999/version.yaml')

    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_switch_version_to_new(self, mock_utils):
        self.upgrader.switch_version_to_new()

        mock_utils.create_dir_if_not_exists.assert_called_once_with(
            '/etc/fuel/9999')
        mock_utils.copy.assert_called_once_with(
            '/tmp/upgrade_path/config/version.yaml',
            '/etc/fuel/9999/version.yaml')
        mock_utils.symlink.assert_called_once_with(
            '/etc/fuel/9999/version.yaml',
            '/etc/fuel/version.yaml')
Exemplo n.º 3
0
class TestDockerUpgrader(BaseTestCase):

    def setUp(self):
        # NOTE (eli): mocking doesn't work correctly
        # when we try to patch docker client with
        # class decorator, it's the reason why
        # we have to do it explicitly
        self.docker_patcher = mock.patch(
            'fuel_upgrade.engines.docker_engine.docker.Client')
        self.docker_mock_class = self.docker_patcher.start()
        self.docker_mock = mock.MagicMock()
        self.docker_mock_class.return_value = self.docker_mock

        self.supervisor_patcher = mock.patch(
            'fuel_upgrade.engines.docker_engine.SupervisorClient')
        self.supervisor_class = self.supervisor_patcher.start()
        self.supervisor_mock = mock.MagicMock()
        self.supervisor_class.return_value = self.supervisor_mock

        self.version_mock = mock.MagicMock()

        with mock.patch('fuel_upgrade.engines.docker_engine.utils'):
            with mock.patch('fuel_upgrade.engines.docker_engine.VersionFile',
                            return_value=self.version_mock):
                self.upgrader = DockerUpgrader(self.fake_config)
                self.upgrader.upgrade_verifier = mock.MagicMock()

        self.pg_dump_path = '/var/lib/fuel_upgrade/9999/pg_dump_all.sql'

    def tearDown(self):
        self.docker_patcher.stop()
        self.supervisor_patcher.stop()

    def mock_methods(self, obj, methods):
        for method in methods:
            setattr(obj, method, mock.MagicMock())

    def test_upgrade(self):
        mocked_methods = [
            'stop_fuel_containers',
            'save_db',
            'save_cobbler_configs',
            'save_astute_keys',
            'upload_images',
            'create_and_start_new_containers',
            'generate_configs',
            'switch_to_new_configs']

        self.mock_methods(self.upgrader, mocked_methods)
        self.upgrader.upgrade()

        self.assertEqual(
            self.upgrader.generate_configs.call_args_list,
            [mock.call(autostart=False),
             mock.call(autostart=True)])

        self.called_once(self.upgrader.stop_fuel_containers)
        self.called_once(self.supervisor_mock.stop_all_services)
        self.called_once(self.supervisor_mock.restart_and_wait)
        self.called_once(self.upgrader.upgrade_verifier.verify)
        self.called_once(self.version_mock.save_current)
        self.called_once(self.version_mock.switch_to_new)

    def test_rollback(self):
        self.upgrader.stop_fuel_containers = mock.MagicMock()
        self.upgrader.switch_version_file_to_previous_version = \
            mock.MagicMock()
        self.upgrader.rollback()

        self.called_times(self.upgrader.stop_fuel_containers, 1)
        self.called_once(self.supervisor_mock.switch_to_previous_configs)
        self.called_once(self.supervisor_mock.stop_all_services)
        self.called_once(self.supervisor_mock.restart_and_wait)
        self.called_once(self.version_mock.save_current)
        self.called_once(self.version_mock.switch_to_previous)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=['file1', 'file2'])
    def test_on_success(self, glob_mock, utils_mock):
        self.upgrader.on_success()
        glob_mock.assert_called_once_with(self.fake_config.version_files_mask)
        self.assertEqual(
            utils_mock.remove.call_args_list,
            [mock.call('file1'), mock.call('file2')])

    def test_stop_fuel_containers(self):
        non_fuel_images = [
            'first_image_1.0', 'second_image_2.0', 'third_image_2.0']
        fuel_images = [
            'fuel/image_1.0', 'fuel/image_2.0']

        all_images = [{'Image': v, 'Id': i}
                      for i, v in enumerate(non_fuel_images + fuel_images)]

        ports = [1, 2, 3]
        self.upgrader._get_docker_container_public_ports = mock.MagicMock(
            return_value=ports)
        self.docker_mock.containers.return_value = all_images
        self.upgrader.stop_fuel_containers()
        self.assertEqual(
            self.docker_mock.stop.call_args_list,
            [mock.call(3, 20), mock.call(4, 20)])

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd')
    @mock.patch('fuel_upgrade.engines.docker_engine.os.path.exists',
                return_value=True)
    def test_upload_images(self, _, exec_mock):
        self.upgrader.new_release_images = [
            {'docker_image': 'image1'},
            {'docker_image': 'image2'}]

        self.upgrader.upload_images()
        self.assertEqual(
            exec_mock.call_args_list,
            [mock.call('docker load < "image1"'),
             mock.call('docker load < "image2"')])

    def test_create_containers(self):
        fake_containers = [
            {'id': 'id1',
             'container_name': 'name1',
             'image_name': 'i_name1',
             'volumes_from': ['id2']},
            {'id': 'id2',
             'image_name': 'i_name2',
             'container_name': 'name2',
             'after_container_creation_command': 'cmd',
             'supervisor_config': True}]

        self.upgrader.new_release_containers = fake_containers

        def mocked_create_container(*args, **kwargs):
            """Return name of the container
            """
            return kwargs['name']

        self.upgrader.create_container = mock.MagicMock(
            side_effect=mocked_create_container)
        self.upgrader.start_container = mock.MagicMock()
        self.upgrader.run_after_container_creation_command = mock.MagicMock()
        self.upgrader.clean_iptables_rules = mock.MagicMock()
        self.upgrader.start_service_under_supervisor = mock.MagicMock()

        self.upgrader.create_and_start_new_containers()

        create_container_calls = [
            mock.call('i_name2', detach=False, ports=None,
                      volumes=None, name='name2'),
            mock.call('i_name1', detach=False, ports=None,
                      volumes=None, name='name1')]

        start_container_calls = [
            mock.call('name2', volumes_from=[],
                      binds=None, port_bindings=None,
                      privileged=False, links=[]),
            mock.call('name1', volumes_from=['name2'],
                      binds=None, port_bindings=None,
                      privileged=False, links=[])]

        self.upgrader.clean_iptables_rules.assert_called_once_with(
            fake_containers[-1])
        self.upgrader.start_service_under_supervisor.assert_called_once_with(
            'docker-id2')
        self.assertEqual(
            self.upgrader.create_container.call_args_list,
            create_container_calls)
        self.assertEqual(
            self.upgrader.start_container.call_args_list,
            start_container_calls)
        self.called_once(self.upgrader.run_after_container_creation_command)

    def test_run_after_container_creation_command(self):
        self.upgrader.exec_with_retries = mock.MagicMock()
        self.upgrader.run_after_container_creation_command({
            'after_container_creation_command': 'cmd',
            'container_name': 'name'})

        args, kwargs = self.upgrader.exec_with_retries.call_args

        self.assertEqual(args[1], errors.ExecutedErrorNonZeroExitCode)
        self.assertEqual(kwargs, {'retries': 30, 'interval': 4})

    def test_create_container(self):
        self.upgrader.create_container(
            'image_name', param1=1, param2=2, ports=[1234])

        self.docker_mock.create_container.assert_called_once_with(
            'image_name', param2=2, param1=1, ports=[1234])

    def test_start_container(self):
        self.upgrader.start_container(
            {'Id': 'container_id'}, param1=1, param2=2)

        self.docker_mock.start.assert_called_once_with(
            'container_id', param2=2, param1=1)

    def test_build_dependencies_graph(self):
        containers = [
            {'id': '1', 'volumes_from': ['2'], 'links': [{'id': '3'}]},
            {'id': '2', 'volumes_from': [], 'links': []},
            {'id': '3', 'volumes_from': [], 'links': [{'id': '2'}]}]

        actual_graph = self.upgrader.build_dependencies_graph(containers)
        expected_graph = {
            '1': ['2', '3'],
            '2': [],
            '3': ['2']}

        self.assertEqual(actual_graph, expected_graph)

    def test_get_container_links(self):
        fake_containers = [
            {'id': 'id1', 'container_name': 'container_name1',
             'links': [{'id': 'id2', 'alias': 'alias2'}]},
            {'id': 'id2', 'container_name': 'container_name2'}]
        self.upgrader.new_release_containers = fake_containers
        links = self.upgrader.get_container_links(fake_containers[0])
        self.assertEqual(links, [('container_name2', 'alias2')])

    def test_get_ports(self):
        ports = self.upgrader.get_ports({'ports': [[53, 'udp'], 100]})
        self.assertEqual([(53, 'udp'), 100], ports)

    def test_generate_configs(self):
        fake_containers = [
            {'id': 'id1', 'container_name': 'container_name1',
             'supervisor_config': False},
            {'id': 'id2', 'container_name': 'container_name2',
             'supervisor_config': True},
            {'id': 'cobbler', 'container_name': 'cobbler_container',
             'supervisor_config': False}]
        self.upgrader.new_release_containers = fake_containers
        self.upgrader.generate_configs()
        self.supervisor_mock.generate_configs.assert_called_once_with(
            [{'config_name': 'id2',
              'service_name': 'docker-id2',
              'command': 'docker start -a container_name2',
              'autostart': True}])
        self.supervisor_mock.generate_cobbler_config.assert_called_once_with(
            'cobbler',
            'docker-cobbler',
            'cobbler_container',
            autostart=True)

    def test_switch_to_new_configs(self):
        self.upgrader.switch_to_new_configs()
        self.supervisor_mock.switch_to_new_configs.assert_called_once_with()

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd')
    def test_exec_cmd_in_container(self, exec_cmd_mock):
        name = 'container_name'
        cmd = 'some command'

        self.upgrader.container_docker_id = mock.MagicMock(return_value=name)
        self.upgrader.exec_cmd_in_container(name, cmd)

        self.called_once(self.upgrader.container_docker_id)
        exec_cmd_mock.assert_called_once_with(
            "lxc-attach --name {0} -- {1}".format(name, cmd))

    @mock.patch('fuel_upgrade.engines.docker_engine.'
                'utils.exec_cmd')
    @mock.patch('fuel_upgrade.engines.docker_engine.'
                'DockerUpgrader.verify_cobbler_configs')
    def test_save_cobbler_configs(self, verify_mock, exec_cmd_mock):
        self.upgrader.save_cobbler_configs()

        exec_cmd_mock.assert_called_once_with(
            'docker cp fuel-core-0-cobbler:/var/lib/cobbler/config '
            '/var/lib/fuel_upgrade/9999/cobbler_configs')
        self.called_once(verify_mock)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.rmtree')
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd',
                side_effect=errors.ExecutedErrorNonZeroExitCode())
    def test_save_cobbler_configs_removes_dir_in_case_of_error(
            self, exec_cmd_mock, rm_mock):

        with self.assertRaises(errors.ExecutedErrorNonZeroExitCode):
            self.upgrader.save_cobbler_configs()

        cobbler_config_path = '/var/lib/fuel_upgrade/9999/cobbler_configs'
        exec_cmd_mock.assert_called_once_with(
            'docker cp fuel-core-0-cobbler:/var/lib/cobbler/config '
            '{0}'.format(cobbler_config_path))
        rm_mock.assert_called_once_with(cobbler_config_path)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_save_astute_keys(self, utils_mock):
        self.upgrader.save_astute_keys()
        utils_mock.exec_cmd.assert_called_once_with(
            'docker cp fuel-core-0-astute:/var/lib/astute '
            '/var/lib/fuel_upgrade/9999')
        utils_mock.remove.assert_called_once_with(
            '/var/lib/fuel_upgrade/9999/astute')

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd',
                side_effect=errors.ExecutedErrorNonZeroExitCode())
    @mock.patch('fuel_upgrade.engines.docker_engine.os')
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.remove')
    def test_save_astute_keys_creates_dir_if_error(
            self, remove_mock, os_mock, exec_cmd_mock):
        self.upgrader.save_astute_keys()
        exec_cmd_mock.assert_called_once_with(
            'docker cp fuel-core-0-astute:/var/lib/astute '
            '/var/lib/fuel_upgrade/9999')
        remove_mock.assert_called_once_with(
            '/var/lib/fuel_upgrade/9999/astute')
        os_mock.mkdir.assert_called_once_with(
            '/var/lib/fuel_upgrade/9999/astute')

    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=['1.json'])
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.'
                'check_file_is_valid_json')
    def test_verify_cobbler_configs(self, json_checker_mock, glob_mock):
        self.upgrader.verify_cobbler_configs()
        glob_mock.assert_called_once_with(
            '/var/lib/fuel_upgrade/9999/'
            'cobbler_configs/config/systems.d/*.json')
        json_checker_mock.assert_called_once_with('1.json')

    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=[])
    def test_verify_cobbler_configs_raises_error_if_not_enough_systems(
            self, glob_mock):

        with self.assertRaises(errors.WrongCobblerConfigsError):
            self.upgrader.verify_cobbler_configs()
        self.called_once(glob_mock)

    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=['1.json'])
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.'
                'check_file_is_valid_json', return_value=False)
    def test_verify_cobbler_configs_raises_error_if_invalid_file(
            self, json_checker_mock, glob_mock):

        with self.assertRaises(errors.WrongCobblerConfigsError):
            self.upgrader.verify_cobbler_configs()

        self.called_once(glob_mock)
        self.called_once(json_checker_mock)

    def test_get_docker_container_public_ports(self):
        docker_ports_mapping = [
            {'Ports': [
                {'PublicPort': 514},
                {'PublicPort': 515}]},
            {'Ports': [
                {'PublicPort': 516},
                {'PublicPort': 517}]}]

        self.assertEquals(
            [514, 515, 516, 517],
            self.upgrader._get_docker_container_public_ports(
                docker_ports_mapping))

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.safe_exec_cmd')
    def test_clean_iptables_rules(self, exec_cmd_mock):
        containers = [
            {'id': 'astute', 'port_bindings': ['some_ports']},
            {'id': 'some_volume_container'},
            {'id': 'ostf', 'port_bindings': ['some_ports']}]

        for container in containers:
            with mock.patch('fuel_upgrade.engines.docker_engine.'
                            'DockerUpgrader._log_iptables') as log_mock:
                self.upgrader.clean_iptables_rules(container)

        self.called_times(log_mock, 2)
        self.assertEqual(
            exec_cmd_mock.call_args_list,
            [mock.call('dockerctl post_start_hooks astute'),
             mock.call('service iptables save'),
             mock.call('dockerctl post_start_hooks ostf'),
             mock.call('service iptables save')])

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.files_size',
                return_value=5)
    def test_required_free_space(self, _):
        self.assertEqual(
            self.upgrader.required_free_space,
            {'/var/lib/fuel_upgrade/9999': 150,
             '/var/lib/docker': 5,
             '/etc/fuel/': 10,
             '/etc/supervisord.d/': 10})

    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_save_db_succeed(self, mock_utils):
        with mock.patch('fuel_upgrade.engines.docker_engine.'
                        'utils.VersionedFile') as version_mock:
            version_mock.return_value.next_file_name.return_value = 'file3'
            version_mock.return_value.sorted_files.return_value = [
                'file3', 'file2', 'file1']

            self.upgrader.save_db()
        self.called_once(mock_utils.wait_for_true)
        mock_utils.hardlink.assert_called_once_with(
            'file3',
            '/var/lib/fuel_upgrade/9999/pg_dump_all.sql',
            overwrite=True)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_save_db_error_first_dump_is_invalid(self, mock_utils):
        with mock.patch('fuel_upgrade.engines.docker_engine.'
                        'utils.VersionedFile') as version_mock:
            version_mock.return_value.filter_files.return_value = []
            self.assertRaises(errors.DatabaseDumpError, self.upgrader.save_db)

        self.method_was_not_called(mock_utils.hardlink)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_save_db_removes_old_dump_files(self, mock_utils):
        mock_utils.file_exists.return_value = True
        with mock.patch('fuel_upgrade.engines.docker_engine.'
                        'utils.VersionedFile') as version_mock:
            version_mock.return_value.sorted_files.return_value = [
                'file1', 'file2', 'file3', 'file4', 'file5']
            self.upgrader.save_db()

        self.assertEqual(
            mock_utils.remove_if_exists.call_args_list,
            [mock.call('file4'), mock.call('file5')])

    @mock.patch('fuel_upgrade.engines.docker_engine.'
                'DockerUpgrader.exec_cmd_in_container')
    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_make_pg_dump_succeed(self, mock_utils, exec_mock):
        self.assertTrue(
            self.upgrader.make_pg_dump('tmp_path', self.pg_dump_path))
        self.method_was_not_called(mock_utils.file_exists)
        self.method_was_not_called(mock_utils.remove_if_exists)

        exec_mock.assert_called_once_with(
            'fuel-core-0-postgres',
            "su postgres -c 'pg_dumpall --clean' > tmp_path")

    @mock.patch('fuel_upgrade.engines.docker_engine.'
                'DockerUpgrader.exec_cmd_in_container',
                side_effect=errors.ExecutedErrorNonZeroExitCode())
    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_make_pg_dump_error_failed_to_execute_dump_command(
            self, mock_utils, _):
        mock_utils.file_exists.return_value = False
        self.assertFalse(
            self.upgrader.make_pg_dump('tmp_path', self.pg_dump_path))
        mock_utils.file_exists.assert_called_once_with(self.pg_dump_path)
        mock_utils.remove_if_exists.assert_called_once_with('tmp_path')

    @mock.patch('fuel_upgrade.engines.docker_engine.'
                'DockerUpgrader.exec_cmd_in_container',
                side_effect=errors.CannotFindContainerError())
    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_make_pg_dump_error_failed_because_of_stopped_container(
            self, mock_utils, exec_cmd_mock):
        mock_utils.file_exists.return_value = False
        self.assertFalse(
            self.upgrader.make_pg_dump('tmp_path', self.pg_dump_path))
        mock_utils.file_exists.assert_called_once_with(self.pg_dump_path)
        mock_utils.remove_if_exists.assert_called_once_with('tmp_path')

    @mock.patch('fuel_upgrade.engines.docker_engine.'
                'DockerUpgrader.exec_cmd_in_container',
                side_effect=errors.ExecutedErrorNonZeroExitCode())
    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_make_pg_dump_second_run_failed_to_execute_dump_command(
            self, mock_utils, exec_cmd_mock):
        mock_utils.file_exists.return_value = True

        with mock.patch('fuel_upgrade.engines.docker_engine.'
                        'utils.VersionedFile') as version_mock:
            version_mock.return_value.sorted_files.return_value = [
                'file1', 'file2']

            self.assertTrue(
                self.upgrader.make_pg_dump('tmp_path', self.pg_dump_path))

        mock_utils.file_exists.assert_called_once_with(self.pg_dump_path)
        self.called_once(mock_utils.remove_if_exists)

    @mock.patch('fuel_upgrade.engines.docker_engine.'
                'DockerUpgrader.exec_cmd_in_container',
                side_effect=errors.CannotFindContainerError())
    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_make_pg_dump_second_run_failed_because_of_stopped_container(
            self, mock_utils, _):
        mock_utils.file_exists.return_value = True
        with mock.patch('fuel_upgrade.engines.docker_engine.'
                        'utils.VersionedFile') as version_mock:
            version_mock.return_value.sorted_files.return_value = ['file1']
            self.assertTrue(
                self.upgrader.make_pg_dump('tmp_path', self.pg_dump_path))
Exemplo n.º 4
0
class TestDockerUpgrader(BaseTestCase):
    def setUp(self):
        # NOTE (eli): mocking doesn't work correctly
        # when we try to patch docker client with
        # class decorator, it's the reason why
        # we have to do it explicitly
        self.docker_patcher = mock.patch(
            'fuel_upgrade.engines.docker_engine.docker.Client')
        self.docker_mock_class = self.docker_patcher.start()
        self.docker_mock = mock.MagicMock()
        self.docker_mock_class.return_value = self.docker_mock

        self.supervisor_patcher = mock.patch(
            'fuel_upgrade.engines.docker_engine.SupervisorClient')
        self.supervisor_class = self.supervisor_patcher.start()
        self.supervisor_mock = mock.MagicMock()
        self.supervisor_class.return_value = self.supervisor_mock

        with mock.patch('fuel_upgrade.engines.docker_engine.utils'):
            self.upgrader = DockerUpgrader(self.fake_config)
            self.upgrader.upgrade_verifier = mock.MagicMock()

    def tearDown(self):
        self.docker_patcher.stop()
        self.supervisor_patcher.stop()

    def mock_methods(self, obj, methods):
        for method in methods:
            setattr(obj, method, mock.MagicMock())

    def test_upgrade(self):
        mocked_methods = [
            'stop_fuel_containers', 'save_db', 'save_cobbler_configs',
            'save_astute_keys', 'upload_images', 'create_containers',
            'generate_configs', 'switch_to_new_configs',
            'switch_version_to_new'
        ]

        self.mock_methods(self.upgrader, mocked_methods)
        self.upgrader.upgrade()

        # Check that all methods was called once
        # except stop_fuel_containers method
        for method in mocked_methods[1:-1]:
            self.called_once(getattr(self.upgrader, method))

        self.called_times(self.upgrader.stop_fuel_containers, 3)

        self.called_once(self.supervisor_mock.stop_all_services)
        self.called_once(self.supervisor_mock.restart_and_wait)
        self.called_once(self.upgrader.upgrade_verifier.verify)

    def test_rollback(self):
        self.upgrader.stop_fuel_containers = mock.MagicMock()
        self.upgrader.switch_version_file_to_previous_version = \
            mock.MagicMock()
        self.upgrader.rollback()

        self.called_times(self.upgrader.stop_fuel_containers, 1)
        self.called_once(self.supervisor_mock.switch_to_previous_configs)
        self.called_once(self.supervisor_mock.stop_all_services)
        self.called_once(self.supervisor_mock.restart_and_wait)
        self.called_once(self.upgrader.switch_version_file_to_previous_version)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.symlink')
    def test_switch_version_file_to_previous_version(self, symlink_mock):
        self.upgrader.switch_version_file_to_previous_version()
        symlink_mock.assert_called_once_with('/etc/fuel/0/version.yaml',
                                             '/etc/fuel/version.yaml')

    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=['file1', 'file2'])
    def test_on_success(self, glob_mock, utils_mock):
        self.upgrader.on_success()
        glob_mock.assert_called_once_with(self.fake_config.version_files_mask)
        self.assertEqual(utils_mock.remove.call_args_list, [(('file1', ), ),
                                                            (('file2', ), )])

    def test_stop_fuel_containers(self):
        non_fuel_images = [
            'first_image_1.0', 'second_image_2.0', 'third_image_2.0'
        ]
        fuel_images = ['fuel/image_1.0', 'fuel/image_2.0']

        all_images = [{
            'Image': v,
            'Id': i
        } for i, v in enumerate(non_fuel_images + fuel_images)]

        ports = [1, 2, 3]
        self.upgrader._get_docker_container_public_ports = mock.MagicMock(
            return_value=ports)
        self.upgrader.clean_docker_iptables_rules = mock.MagicMock()
        self.docker_mock.containers.return_value = all_images
        self.upgrader.stop_fuel_containers()
        self.assertEqual(self.docker_mock.stop.call_args_list, [((3, 10), ),
                                                                ((4, 10), )])
        self.upgrader.clean_docker_iptables_rules.assert_called_once_with(
            ports)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd')
    @mock.patch('fuel_upgrade.engines.docker_engine.os.path.exists',
                return_value=True)
    def test_upload_images(self, _, exec_mock):
        self.upgrader.new_release_images = [{
            'docker_image': 'image1'
        }, {
            'docker_image': 'image2'
        }]

        self.upgrader.upload_images()
        self.assertEqual(exec_mock.call_args_list,
                         [(('docker load < "image1"', ), ),
                          (('docker load < "image2"', ), )])

    def test_create_containers(self):
        self.upgrader.new_release_containers = [{
            'id': 'id1',
            'container_name': 'name1',
            'image_name': 'i_name1',
            'volumes_from': ['id2']
        }, {
            'id':
            'id2',
            'image_name':
            'i_name2',
            'container_name':
            'name2',
            'after_container_creation_command':
            'cmd'
        }]

        def mocked_create_container(*args, **kwargs):
            """Return name of the container
            """
            return kwargs['name']

        self.upgrader.create_container = mock.MagicMock(
            side_effect=mocked_create_container)
        self.upgrader.start_container = mock.MagicMock()
        self.upgrader.run_after_container_creation_command = mock.MagicMock()

        self.upgrader.create_containers()

        create_container_calls = [(('i_name2', ), {
            'detach': False,
            'ports': None,
            'volumes': None,
            'name': 'name2'
        }),
                                  (('i_name1', ), {
                                      'detach': False,
                                      'ports': None,
                                      'volumes': None,
                                      'name': 'name1'
                                  })]

        start_container_calls = [(('name2', ), {
            'volumes_from': [],
            'binds': None,
            'port_bindings': None,
            'privileged': False,
            'links': []
        }),
                                 (('name1', ), {
                                     'volumes_from': ['name2'],
                                     'binds': None,
                                     'port_bindings': None,
                                     'privileged': False,
                                     'links': []
                                 })]

        self.assertEqual(self.upgrader.create_container.call_args_list,
                         create_container_calls)
        self.assertEqual(self.upgrader.start_container.call_args_list,
                         start_container_calls)
        self.called_once(self.upgrader.run_after_container_creation_command)

    def test_run_after_container_creation_command(self):
        self.upgrader.exec_with_retries = mock.MagicMock()
        self.upgrader.run_after_container_creation_command({
            'after_container_creation_command':
            'cmd',
            'container_name':
            'name'
        })
        self.called_once(self.upgrader.exec_with_retries)

    def test_create_container(self):
        self.upgrader.create_container('image_name',
                                       param1=1,
                                       param2=2,
                                       ports=[1234])

        self.docker_mock.create_container.assert_called_once_with('image_name',
                                                                  param2=2,
                                                                  param1=1,
                                                                  ports=[1234])

    def test_start_container(self):
        self.upgrader.start_container({'Id': 'container_id'},
                                      param1=1,
                                      param2=2)

        self.docker_mock.start.assert_called_once_with('container_id',
                                                       param2=2,
                                                       param1=1)

    def test_build_dependencies_graph(self):
        containers = [{
            'id': '1',
            'volumes_from': ['2'],
            'links': [{
                'id': '3'
            }]
        }, {
            'id': '2',
            'volumes_from': [],
            'links': []
        }, {
            'id': '3',
            'volumes_from': [],
            'links': [{
                'id': '2'
            }]
        }]

        actual_graph = self.upgrader.build_dependencies_graph(containers)
        expected_graph = {'1': ['2', '3'], '2': [], '3': ['2']}

        self.assertEqual(actual_graph, expected_graph)

    def test_get_container_links(self):
        fake_containers = [{
            'id': 'id1',
            'container_name': 'container_name1',
            'links': [{
                'id': 'id2',
                'alias': 'alias2'
            }]
        }, {
            'id': 'id2',
            'container_name': 'container_name2'
        }]
        self.upgrader.new_release_containers = fake_containers
        links = self.upgrader.get_container_links(fake_containers[0])
        self.assertEqual(links, [('container_name2', 'alias2')])

    def test_get_port_bindings(self):
        port_bindings = {'port_bindings': {'53/udp': ['0.0.0.0', 53]}}
        bindings = self.upgrader.get_port_bindings(port_bindings)
        self.assertEqual({'53/udp': ('0.0.0.0', 53)}, bindings)

    def test_get_ports(self):
        ports = self.upgrader.get_ports({'ports': [[53, 'udp'], 100]})
        self.assertEqual([(53, 'udp'), 100], ports)

    def test_generate_configs(self):
        fake_containers = [{
            'id': 'id1',
            'container_name': 'container_name1',
            'supervisor_config': False
        }, {
            'id': 'id2',
            'container_name': 'container_name2',
            'supervisor_config': True
        }, {
            'id': 'cobbler',
            'container_name': 'cobbler_container',
            'supervisor_config': False
        }]
        self.upgrader.new_release_containers = fake_containers
        self.upgrader.generate_configs()
        self.supervisor_mock.generate_configs.assert_called_once_with([{
            'service_name':
            'id2',
            'command':
            'docker start -a container_name2'
        }])
        self.supervisor_mock.generate_cobbler_config.assert_called_once_with({
            'service_name':
            'cobbler',
            'container_name':
            'cobbler_container'
        })

    def test_switch_to_new_configs(self):
        self.upgrader.switch_to_new_configs()
        self.supervisor_mock.switch_to_new_configs.called_once()

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd')
    def test_exec_cmd_in_container(self, exec_cmd_mock):
        name = 'container_name'
        cmd = 'some command'

        self.upgrader.container_docker_id = mock.MagicMock(return_value=name)
        self.upgrader.exec_cmd_in_container(name, cmd)

        self.called_once(self.upgrader.container_docker_id)
        exec_cmd_mock.assert_called_once_with(
            "lxc-attach --name {0} -- {1}".format(name, cmd))

    @mock.patch('fuel_upgrade.engines.docker_engine.' 'utils.exec_cmd')
    @mock.patch('fuel_upgrade.engines.docker_engine.'
                'DockerUpgrader.verify_cobbler_configs')
    def test_save_cobbler_configs(self, verify_mock, exec_cmd_mock):
        self.upgrader.save_cobbler_configs()

        exec_cmd_mock.assert_called_once_with(
            'docker cp fuel-core-0-cobbler:/var/lib/cobbler/config '
            '/var/lib/fuel_upgrade/9999/cobbler_configs')
        self.called_once(verify_mock)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.rmtree')
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd',
                side_effect=errors.ExecutedErrorNonZeroExitCode())
    def test_save_cobbler_configs_removes_dir_in_case_of_error(
            self, exec_cmd_mock, rm_mock):

        with self.assertRaises(errors.ExecutedErrorNonZeroExitCode):
            self.upgrader.save_cobbler_configs()

        cobbler_config_path = '/var/lib/fuel_upgrade/9999/cobbler_configs'
        exec_cmd_mock.assert_called_once_with(
            'docker cp fuel-core-0-cobbler:/var/lib/cobbler/config '
            '{0}'.format(cobbler_config_path))
        rm_mock.assert_called_once_with(cobbler_config_path)

    @mock.patch('fuel_upgrade.engines.docker_engine.' 'utils.exec_cmd')
    def test_save_astute_keys(self, exec_cmd_mock):
        self.upgrader.save_astute_keys()

        exec_cmd_mock.assert_called_once_with(
            'docker cp fuel-core-0-astute:/var/lib/astute '
            '/var/lib/fuel_upgrade/9999')
        self.called_once(exec_cmd_mock)

    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=['1.json'])
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.'
                'check_file_is_valid_json')
    def test_verify_cobbler_configs(self, json_checker_mock, glob_mock):
        self.upgrader.verify_cobbler_configs()
        glob_mock.assert_called_once_with(
            '/var/lib/fuel_upgrade/9999/'
            'cobbler_configs/config/systems.d/*.json')
        json_checker_mock.assert_called_once_with('1.json')

    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=[])
    def test_verify_cobbler_configs_raises_error_if_not_enough_systems(
            self, glob_mock):

        with self.assertRaises(errors.WrongCobblerConfigsError):
            self.upgrader.verify_cobbler_configs()
        self.called_once(glob_mock)

    @mock.patch('fuel_upgrade.engines.docker_engine.glob.glob',
                return_value=['1.json'])
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.'
                'check_file_is_valid_json',
                return_value=False)
    def test_verify_cobbler_configs_raises_error_if_invalid_file(
            self, json_checker_mock, glob_mock):

        with self.assertRaises(errors.WrongCobblerConfigsError):
            self.upgrader.verify_cobbler_configs()

        self.called_once(glob_mock)
        self.called_once(json_checker_mock)

    def test_save_db(self):
        self.upgrader.verify_postgres_dump = mock.MagicMock(return_value=True)
        self.upgrader.exec_cmd_in_container = mock.MagicMock()
        self.upgrader.save_db()

    def test_save_db_failed_verification(self):
        self.upgrader.verify_postgres_dump = mock.MagicMock(return_value=False)
        self.upgrader.exec_cmd_in_container = mock.MagicMock()
        self.assertRaises(errors.DatabaseDumpError, self.upgrader.save_db)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.file_contains_lines',
                returns_value=True)
    @mock.patch('fuel_upgrade.engines.docker_engine.os.path.exists',
                side_effect=[False, True])
    @mock.patch('fuel_upgrade.engines.docker_engine.os.remove')
    def test_save_db_removes_file_in_case_of_error(self, remove_mock, _, __):
        self.upgrader.exec_cmd_in_container = mock.MagicMock(
            side_effect=errors.ExecutedErrorNonZeroExitCode())

        self.assertRaises(errors.ExecutedErrorNonZeroExitCode,
                          self.upgrader.save_db)

        self.called_once(remove_mock)

    @mock.patch('fuel_upgrade.engines.docker_engine.os.path.exists',
                return_value=True)
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.file_contains_lines',
                returns_value=True)
    def test_verify_postgres_dump(self, file_contains_mock, exists_mock):
        self.upgrader.verify_postgres_dump()

        patterns = [
            '-- PostgreSQL database cluster dump',
            '-- PostgreSQL database dump',
            '-- PostgreSQL database dump complete',
            '-- PostgreSQL database cluster dump complete'
        ]

        exists_mock.assert_called_once_with(self.upgrader.pg_dump_path)
        file_contains_mock.assert_called_once_with(self.upgrader.pg_dump_path,
                                                   patterns)

    def test_get_docker_container_public_ports(self):
        docker_ports_mapping = [{
            'Ports': [{
                'PublicPort': 514
            }, {
                'PublicPort': 515
            }]
        }, {
            'Ports': [{
                'PublicPort': 516
            }, {
                'PublicPort': 517
            }]
        }]

        self.assertEquals([514, 515, 516, 517],
                          self.upgrader._get_docker_container_public_ports(
                              docker_ports_mapping))

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd')
    @mock.patch('fuel_upgrade.engines.docker_engine.utils.exec_cmd_iterator')
    def test_clean_docker_iptables_rules(self, exec_cmd_iterator_mock,
                                         exec_cmd_mock):
        iptables_rules = [
            '-A DOCKER -p tcp -m tcp --dport 1 -j DNAT '
            '--to-destination 172.17.0.7:1',
            '-A POSTROUTING -p tcp -m tcp --dport 3 -j DNAT '
            '--to-destination 172.17.0.7:3',
            '-A DOCKER -p tcp -m tcp --dport 2 -j DNAT '
            '--to-destination 172.17.0.3:2'
        ]

        exec_cmd_iterator_mock.return_value = iter(iptables_rules)
        self.upgrader.clean_docker_iptables_rules([1, 2, 3])

        expected_calls = [
            (('iptables -t nat -D  DOCKER -p tcp -m tcp --dport 1 -j DNAT '
              '--to-destination 172.17.0.7:1', ), ),
            (('iptables -t nat -D  DOCKER -p tcp -m tcp --dport 2 -j DNAT '
              '--to-destination 172.17.0.3:2', ), )
        ]

        self.assertEquals(exec_cmd_mock.call_args_list, expected_calls)

    @mock.patch('fuel_upgrade.engines.docker_engine.utils.files_size',
                return_value=5)
    def test_required_free_space(self, _):
        self.assertEqual(
            self.upgrader.required_free_space, {
                '/var/lib/fuel_upgrade/9999': 50,
                '/var/lib/docker': 5,
                '/etc/fuel/': 10,
                '/etc/supervisord.d/': 10
            })

    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_save_current_version_file(self, mock_utils):
        self.upgrader.save_current_version_file()
        mock_utils.copy_if_does_not_exist.assert_called_once_with(
            '/etc/fuel/version.yaml',
            '/var/lib/fuel_upgrade/9999/version.yaml')

    @mock.patch('fuel_upgrade.engines.docker_engine.utils')
    def test_switch_version_to_new(self, mock_utils):
        self.upgrader.switch_version_to_new()

        mock_utils.create_dir_if_not_exists.assert_called_once_with(
            '/etc/fuel/9999')
        mock_utils.copy.assert_called_once_with(
            '/tmp/upgrade_path/config/version.yaml',
            '/etc/fuel/9999/version.yaml')
        mock_utils.symlink.assert_called_once_with(
            '/etc/fuel/9999/version.yaml', '/etc/fuel/version.yaml')