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)
Example #2
0
def _wait_and_check_exit_code(cmd, child):
    """Wait for child and check it's exit code

    :param cmd: command
    :param child: object which returned by subprocess.Popen
    :raises: ExecutedErrorNonZeroExitCode
    """
    child.wait()
    exit_code = child.returncode

    if exit_code != 0:
        raise errors.ExecutedErrorNonZeroExitCode(
            'Shell command executed with "{0}" '
            'exit code: {1} '.format(exit_code, cmd))

    logger.debug('Command "%s" successfully executed', cmd)
Example #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))
Example #4
0
class TestUtils(BaseTestCase):
    def make_process_mock(self, return_code=0):
        process_mock = mock.Mock()
        process_mock.stdout = ['Stdout line 1', 'Stdout line 2']
        process_mock.returncode = return_code

        return process_mock

    def test_exec_cmd_executes_sucessfuly(self):
        cmd = 'some command'

        process_mock = self.make_process_mock()
        with patch.object(subprocess, 'Popen',
                          return_value=process_mock) as popen_mock:
            exec_cmd(cmd)

        popen_mock.assert_called_once_with(cmd,
                                           stdout=subprocess.PIPE,
                                           stderr=subprocess.STDOUT,
                                           shell=True)

    @mock.patch('fuel_upgrade.utils.exec_cmd',
                side_effect=errors.ExecutedErrorNonZeroExitCode())
    def test_safe_exec_cmd(self, exec_mock):
        cmd = 'some command'
        utils.safe_exec_cmd(cmd)
        exec_mock.assert_called_once_with(cmd)

    def test_exec_cmd_raises_error_in_case_of_non_zero_exit_code(self):
        cmd = 'some command'
        return_code = 1

        process_mock = self.make_process_mock(return_code=return_code)
        with patch.object(subprocess, 'Popen', return_value=process_mock):
            self.assertRaisesRegexp(
                errors.ExecutedErrorNonZeroExitCode,
                'Shell command executed with "{0}" '
                'exit code: {1} '.format(return_code, cmd), exec_cmd, cmd)

    def test_exec_cmd_iterator_executes_sucessfuly(self):
        cmd = 'some command'

        process_mock = self.make_process_mock()
        with patch.object(subprocess, 'Popen',
                          return_value=process_mock) as popen_mock:
            for line in exec_cmd_iterator(cmd):
                self.assertTrue(line.startswith('Stdout line '))

        popen_mock.assert_called_once_with(cmd,
                                           stdout=subprocess.PIPE,
                                           stderr=subprocess.PIPE,
                                           shell=True)

    def test_exec_cmd_iterator_raises_error_in_case_of_non_zero_exit_code(
            self):
        cmd = 'some command'
        return_code = 1

        process_mock = self.make_process_mock(return_code=return_code)
        with patch.object(subprocess, 'Popen', return_value=process_mock):
            with self.assertRaisesRegexp(
                    errors.ExecutedErrorNonZeroExitCode,
                    'Shell command executed with "{0}" '
                    'exit code: {1} '.format(return_code, cmd)):
                for line in exec_cmd_iterator(cmd):
                    self.assertTrue(line.startswith('Stdout line '))

    def test_get_request(self):
        url = 'http://some-url.com/path'
        response = mock.MagicMock()
        response.read.return_value = '{"key": "value"}'
        response.getcode.return_value = 200

        with patch.object(urllib2, 'urlopen',
                          return_value=response) as urlopen:

            resp = get_request(url)
            self.assertEqual(({'key': 'value'}, 200), resp)

        urlopen.assert_called_once_with(url)

    def test_topological_sorting(self):
        graph = {
            'D': ['C', 'G'],
            'E': ['A', 'D'],
            'A': [],
            'B': ['A'],
            'C': ['A'],
            'G': []
        }

        order = topological_sorting(graph)
        self.assertEqual(order, ['A', 'B', 'C', 'G', 'D', 'E'])

    def test_topological_sorting_raises_cycle_dependencies_error(self):
        graph = {'A': ['C', 'D'], 'B': ['A'], 'C': ['B'], 'D': []}

        self.assertRaisesRegexp(errors.CyclicDependenciesError,
                                "Cyclic dependencies error ",
                                topological_sorting, graph)

    @mock.patch('fuel_upgrade.utils.os.makedirs')
    def test_create_dir_if_not_exists_does_not_create_dir(self, mock_makedirs):
        path = 'some_path'

        with mock.patch('fuel_upgrade.utils.os.path.isdir',
                        return_value=True) as mock_isdir:

            create_dir_if_not_exists(path)
            mock_isdir.assert_called_once_with(path)
            self.method_was_not_called(mock_makedirs)

    @mock.patch('fuel_upgrade.utils.os.makedirs')
    def test_create_dir_if_not_exists(self, mock_makedirs):
        path = 'some_path'
        with mock.patch('fuel_upgrade.utils.os.path.isdir',
                        return_value=False) as mock_isdir:

            create_dir_if_not_exists(path)
            mock_isdir.assert_called_once_with(path)
            mock_makedirs.assert_called_once_with(path)

    def test_wait_for_true_does_not_raise_errors(self):
        self.assertEqual(wait_for_true(lambda: True, timeout=0), True)

    def test_wait_for_true_raises_timeout_error(self):
        self.assertRaisesRegexp(errors.TimeoutError,
                                'Failed to execute command with timeout 0',
                                wait_for_true,
                                lambda: False,
                                timeout=0)

    @mock.patch('fuel_upgrade.utils.os.path.isdir', return_value=True)
    @mock.patch('fuel_upgrade.utils.copy_dir')
    def test_copy_with_dir(self, copy_mock, _):
        from_path = '/from_path'
        to_path = '/to_path'

        utils.copy(from_path, to_path)
        copy_mock.assert_called_once_with(from_path, to_path, True, True)

    @mock.patch('fuel_upgrade.utils.os.path.isdir', return_value=False)
    @mock.patch('fuel_upgrade.utils.copy_file')
    def test_copy_with_file(self, copy_mock, _):
        from_path = '/from_path'
        to_path = '/to_path'

        utils.copy(from_path, to_path)
        copy_mock.assert_called_once_with(from_path, to_path, True)

    @mock.patch('fuel_upgrade.utils.os.path.isdir', return_value=False)
    @mock.patch('fuel_upgrade.utils.shutil.copy')
    def test_copy_file(self, copy_mock, _):
        from_path = '/from_path.txt'
        to_path = '/to_path.txt'

        utils.copy_file(from_path, to_path)
        copy_mock.assert_called_once_with(from_path, to_path)

    @mock.patch('fuel_upgrade.utils.os.path.isdir', return_value=True)
    @mock.patch('fuel_upgrade.utils.shutil.copy')
    def test_copy_file_to_dir(self, copy_mock, _):
        from_path = '/from_path.txt'
        to_path = '/to_path'

        utils.copy_file(from_path, to_path)
        copy_mock.assert_called_once_with(from_path, '/to_path/from_path.txt')

    @mock.patch('fuel_upgrade.utils.os.path.isdir', return_value=False)
    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=True)
    @mock.patch('fuel_upgrade.utils.shutil.copy')
    def test_copy_file_do_not_overwrite(self, copy_mock, _, __):
        from_path = '/from_path.txt'
        to_path = '/to_path.txt'

        utils.copy_file(from_path, to_path, overwrite=False)
        self.method_was_not_called(copy_mock)

    @mock.patch('fuel_upgrade.utils.shutil.copytree')
    def test_copy_dir(self, copy_mock):
        from_path = '/from_path'
        to_path = '/to_path'

        utils.copy_dir(from_path, to_path)
        copy_mock.assert_called_once_with(from_path, to_path, symlinks=True)

    @mock.patch('fuel_upgrade.utils.os.path.lexists', return_value=True)
    @mock.patch('fuel_upgrade.utils.shutil.copytree')
    @mock.patch('fuel_upgrade.utils.remove')
    def test_copy_dir_overwrite(self, rm_mock, copy_mock, _):
        from_path = '/from_path'
        to_path = '/to_path'

        utils.copy_dir(from_path, to_path)
        rm_mock.assert_called_once_with(to_path, ignore_errors=True)
        copy_mock.assert_called_once_with(from_path, to_path, symlinks=True)

    def test_file_contains_lines_returns_true(self):
        with mock.patch('__builtin__.open',
                        self.mock_open("line 1\n line2\n line3")):

            self.assertTrue(
                utils.file_contains_lines('/some/path', ['line 1', 'line3']))

    def test_file_contains_lines_returns_false(self):
        with mock.patch('__builtin__.open',
                        self.mock_open("line 1\n line2\n line3")):

            self.assertFalse(
                utils.file_contains_lines('/some/path', ['line 4', 'line3']))

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=True)
    @mock.patch('fuel_upgrade.utils.os.symlink')
    @mock.patch('fuel_upgrade.utils.remove')
    def test_symlink(self, remove_mock, symlink_mock, _):
        from_path = '/tmp/from/path'
        to_path = '/tmp/to/path'
        utils.symlink(from_path, to_path)

        symlink_mock.assert_called_once_with(from_path, to_path)
        remove_mock.assert_called_once_with(to_path)

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=False)
    @mock.patch('fuel_upgrade.utils.os.symlink')
    @mock.patch('fuel_upgrade.utils.remove')
    def test_symlink_no_exist(self, remove_mock, symlink_mock, _):
        from_path = '/tmp/from/path'
        to_path = '/tmp/to/path'
        utils.symlink(from_path, to_path)

        symlink_mock.assert_called_once_with(from_path, to_path)
        self.called_once(remove_mock)

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=True)
    @mock.patch('fuel_upgrade.utils.os.remove')
    def test_remove_if_exists(self, remove_mock, exists_mock):
        path = '/tmp/some/path'
        utils.remove_if_exists(path)
        remove_mock.assert_called_once_with(path)
        exists_mock.assert_called_once_with(path)

    def test_load_fixture(self):
        fixture = StringIO.StringIO('''
        - &base
          fields:
            a: 1
            b: 2
            c: 3

        - pk: 1
          extend: *base
          fields:
            a: 13

        - pk: 2
          extend: *base
          fields:
            d: 42
        ''')
        setattr(fixture, 'name', 'some.yaml')

        result = utils.load_fixture(fixture)
        self.assertEqual(len(result), 2)
        self.assertEqual(result[0], {
            'a': 13,
            'b': 2,
            'c': 3,
        })
        self.assertEqual(result[1], {
            'a': 1,
            'b': 2,
            'c': 3,
            'd': 42,
        })

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=True)
    @mock.patch('fuel_upgrade.utils.shutil.rmtree')
    def test_rmtree(self, rm_mock, exists_mock):
        path = '/some/file/path'
        utils.rmtree(path)
        rm_mock.assert_called_once_with(path, ignore_errors=True)
        exists_mock.assert_called_once_with(path)

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=False)
    @mock.patch('fuel_upgrade.utils.shutil.rmtree')
    def test_rmtree_no_errors_if_file_does_not_exist(self, rm_mock,
                                                     exists_mock):

        path = '/some/file/path'
        utils.rmtree(path)

        self.method_was_not_called(rm_mock)
        exists_mock.assert_called_once_with(path)

    def test_check_file_is_valid_json(self):
        path = '/path/to/file.json'
        with mock.patch('__builtin__.open',
                        self.mock_open('{"valid": "json"}')):
            self.assertTrue(utils.check_file_is_valid_json(path))

    def test_check_file_is_valid_json_returns_false(self):
        path = '/path/to/file.json'
        with mock.patch('__builtin__.open',
                        self.mock_open('{"invalid: "json"}')):
            self.assertFalse(utils.check_file_is_valid_json(path))

    def test_check_file_is_valid_json_false_if_problems_with_access(self):
        path = '/path/to/file.json'
        with mock.patch('__builtin__.open', side_effect=IOError()):
            self.assertFalse(utils.check_file_is_valid_json(path))

    def test_byte_to_megabyte(self):
        self.assertEqual(utils.byte_to_megabyte(0), 0)
        self.assertEqual(utils.byte_to_megabyte(1048576), 1)

    def test_calculate_free_space(self):
        dev_info = mock.Mock()
        dev_info.f_bsize = 1048576
        dev_info.f_bavail = 2
        with mock.patch('fuel_upgrade.utils.os.statvfs',
                        return_value=dev_info) as st_mock:
            self.assertEqual(utils.calculate_free_space('/tmp/dir'), 2)

        st_mock.assert_called_once_with('/tmp/dir/')

    @mock.patch('fuel_upgrade.utils.os.path.ismount',
                side_effect=[False, False, True])
    def test_find_mount_point(self, mock_ismount):
        path = '/dir1/dir2/dir3/dir4'
        self.assertEqual(utils.find_mount_point(path), '/dir1/dir2')
        self.called_times(mock_ismount, 3)

    @mock.patch('fuel_upgrade.utils.os.path.getsize', return_value=1048576)
    @mock.patch('fuel_upgrade.utils.os.walk',
                return_value=[('', '', ['file1', 'file2'])])
    @mock.patch('fuel_upgrade.utils.os.path.isfile', return_value=True)
    def test_dir_size(self, _, __, ___):
        path = '/path/dir'
        self.assertEqual(utils.dir_size(path), 2)

    @mock.patch('fuel_upgrade.utils.os.path.getsize', return_value=1048576)
    @mock.patch('fuel_upgrade.utils.os.path.isfile', return_value=True)
    def test_files_size(self, _, __):
        path = ['/path/file1', '/path/file2']
        self.assertEqual(utils.files_size(path), 2)

    def test_compare_version(self):
        self.assertEqual(utils.compare_version('0.1', '0.2'), 1)
        self.assertEqual(utils.compare_version('0.1', '0.1.5'), 1)
        self.assertEqual(utils.compare_version('0.2', '0.1'), -1)
        self.assertEqual(utils.compare_version('0.2', '0.2'), 0)

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=True)
    @mock.patch('fuel_upgrade.utils.copy')
    def test_copy_if_does_not_exist_file_exists(self, copy_mock, exists_mock):
        utils.copy_if_does_not_exist('from', 'to')
        exists_mock.assert_called_once_with('to')
        self.method_was_not_called(copy_mock)

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=False)
    @mock.patch('fuel_upgrade.utils.copy')
    def test_copy_if_does_not_exist_file_does_not_exist(
            self, copy_mock, exists_mock):
        utils.copy_if_does_not_exist('from', 'to')
        exists_mock.assert_called_once_with('to')
        copy_mock.assert_called_once_with('from', 'to')

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=False)
    @mock.patch('fuel_upgrade.utils.copy')
    def test_copy_if_exists_file_does_not_exist(self, copy_mock, exists_mock):
        utils.copy_if_exists('from', 'to')
        exists_mock.assert_called_once_with('from')
        self.method_was_not_called(copy_mock)

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=True)
    @mock.patch('fuel_upgrade.utils.copy')
    def test_copy_if_exists_file_exists(self, copy_mock, exists_mock):
        utils.copy_if_exists('from', 'to')
        exists_mock.assert_called_once_with('from')
        copy_mock.assert_called_once_with('from', 'to')

    @mock.patch('fuel_upgrade.utils.os.rename')
    def test_rename(self, rename_mock):
        utils.rename('source', 'destination')
        rename_mock.assert_called_once_with('source', 'destination')

    @mock.patch('fuel_upgrade.utils.os.path.lexists', return_value=True)
    @mock.patch('fuel_upgrade.utils.os.path.isdir', return_value=False)
    @mock.patch('fuel_upgrade.utils.os.remove')
    def test_remove_file(self, remove_mock, _, __):
        utils.remove('path')
        remove_mock.assert_called_once_with('path')

    @mock.patch('fuel_upgrade.utils.os.path.lexists', return_value=True)
    @mock.patch('fuel_upgrade.utils.os.path.islink', return_value=True)
    @mock.patch('fuel_upgrade.utils.os.path.isdir', return_value=True)
    @mock.patch('fuel_upgrade.utils.os.remove')
    def test_remove_link_to_dir(self, remove_mock, _, __, ___):
        utils.remove('path')
        remove_mock.assert_called_once_with('path')

    @mock.patch('fuel_upgrade.utils.os.path.lexists', return_value=False)
    @mock.patch('fuel_upgrade.utils.os.path.isdir', return_value=False)
    @mock.patch('fuel_upgrade.utils.os.remove')
    def test_remove_file_does_not_exist(self, remove_mock, _, __):
        utils.remove('path')
        self.method_was_not_called(remove_mock)

    @mock.patch('fuel_upgrade.utils.os.path.lexists', return_value=True)
    @mock.patch('fuel_upgrade.utils.os.path.isdir', return_value=True)
    @mock.patch('fuel_upgrade.utils.shutil.rmtree')
    def test_remove_dir(self, remove_mock, _, __):
        utils.remove('path')
        remove_mock.assert_called_once_with('path', ignore_errors=True)

    @mock.patch('fuel_upgrade.utils.yaml')
    def test_save_as_yaml(self, yaml_mock):
        path = '/tmp/path'
        data = {'a': 'b'}
        mock_open = self.mock_open('')
        with mock.patch('__builtin__.open', mock_open):
            utils.save_as_yaml(path, data)

        yaml_mock.dump.assert_called_once_with(data, default_flow_style=False)

    def test_generate_uuid_string(self):
        random_string = utils.generate_uuid_string()
        self.assertEqual(len(random_string), 36)
        self.assertTrue(isinstance(random_string, str))

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=True)
    @mock.patch('fuel_upgrade.utils.file_contains_lines', returns_value=True)
    def test_verify_postgres_dump(self, file_contains_mock, exists_mock):
        pg_dump_path = '/tmp/some/path'
        utils.verify_postgres_dump(pg_dump_path)

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

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

    def test_file_extension(self):
        cases = [('', ''), ('asdf', ''), ('asdf.', ''), ('asdf.txt', 'txt'),
                 ('asdf.txt.trtr', 'trtr')]

        for case in cases:
            self.assertEqual(utils.file_extension(case[0]), case[1])

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=True)
    def test_file_exists_returns_true(self, exists_mock):
        self.assertTrue(utils.file_exists('path'))
        exists_mock.assert_called_once_with('path')

    @mock.patch('fuel_upgrade.utils.os.path.exists', return_value=False)
    def test_file_exists_returns_false(self, exists_mock):
        self.assertFalse(utils.file_exists('path'))
        exists_mock.assert_called_once_with('path')

    @mock.patch('fuel_upgrade.utils.os.walk')
    def test_iterfiles(self, walk):
        for _ in utils.iterfiles('path/to/dir'):
            pass
        walk.assert_called_once_with('path/to/dir', topdown=True)
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')