# -*- coding: utf-8 -*-

import mock

import six

import os

from pungi.phases.image_build import ImageBuildPhase, CreateImageBuildThread
from tests.helpers import DummyCompose, PungiTestCase, boom


class TestImageBuildPhase(PungiTestCase):
    def setUp(self):
        super(TestImageBuildPhase, self).setUp()
        self.maxDiff = None

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Client|Server$": [
                        {
                            "image-build": {
                                "format": [("docker", "tar.xz")],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "failable": ["x86_64"],
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)
        client_args = {
            "image_conf": {
                "image-build": {
                    "install_tree": self.topdir + "/compose/Client/$arch/os",
                    "kickstart": "fedora-docker-base.ks",
                    "format": ["docker"],
                    "repo": self.topdir + "/compose/Client/$arch/os",
                    "variant": compose.variants["Client"],
                    "target": "f24",
                    "disk_size": 3,
                    "name": "Fedora-Docker-Base",
                    "arches": ["amd64"],
                    "version": "Rawhide",
                    "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                    "distro": "Fedora-20",
                    "can_fail": ["x86_64"],
                }
            },
            "conf_file": self.topdir
            + "/work/image-build/Client/docker_Fedora-Docker-Base_amd64.cfg",
            "image_dir": self.topdir + "/compose/Client/%(arch)s/images",
            "relative_image_dir": "Client/%(arch)s/images",
            "link_type": "hardlink-or-copy",
            "scratch": False,
        }
        server_args = {
            "image_conf": {
                "image-build": {
                    "install_tree": self.topdir + "/compose/Server/$arch/os",
                    "kickstart": "fedora-docker-base.ks",
                    "format": ["docker"],
                    "repo": self.topdir + "/compose/Server/$arch/os",
                    "variant": compose.variants["Server"],
                    "target": "f24",
                    "disk_size": 3,
                    "name": "Fedora-Docker-Base",
                    "arches": ["amd64", "x86_64"],
                    "version": "Rawhide",
                    "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                    "distro": "Fedora-20",
                    "can_fail": ["x86_64"],
                }
            },
            "conf_file": self.topdir
            + "/work/image-build/Server/docker_Fedora-Docker-Base_amd64-x86_64.cfg",
            "image_dir": self.topdir + "/compose/Server/%(arch)s/images",
            "relative_image_dir": "Server/%(arch)s/images",
            "link_type": "hardlink-or-copy",
            "scratch": False,
        }
        six.assertCountEqual(
            self,
            phase.pool.queue_put.mock_calls,
            [mock.call((compose, client_args)), mock.call((compose, server_args))],
        )

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_phase_global_options(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build_ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                "image_build_release": "!RELEASE_FROM_LABEL_DATE_TYPE_RESPIN",
                "image_build_target": "f24",
                "image_build_version": "Rawhide",
                "image_build": {
                    "^Server$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)
        server_args = {
            "image_conf": {
                "image-build": {
                    "install_tree": self.topdir + "/compose/Server/$arch/os",
                    "kickstart": "fedora-docker-base.ks",
                    "format": ["docker"],
                    "repo": self.topdir + "/compose/Server/$arch/os",
                    "variant": compose.variants["Server"],
                    "target": "f24",
                    "disk_size": 3,
                    "name": "Fedora-Docker-Base",
                    "arches": ["amd64", "x86_64"],
                    "version": "Rawhide",
                    "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                    "distro": "Fedora-20",
                    "release": "20151203.t.0",
                }
            },
            "conf_file": self.topdir
            + "/work/image-build/Server/docker_Fedora-Docker-Base_amd64-x86_64.cfg",
            "image_dir": self.topdir + "/compose/Server/%(arch)s/images",
            "relative_image_dir": "Server/%(arch)s/images",
            "link_type": "hardlink-or-copy",
            "scratch": False,
        }
        self.assertEqual(
            phase.pool.queue_put.mock_calls, [mock.call((compose, server_args))]
        )

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_phase_missing_version(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build_ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                "image_build_release": "!RELEASE_FROM_LABEL_DATE_TYPE_RESPIN",
                "image_build_target": "f24",
                "image_build": {
                    "^Server$": [
                        {
                            "image-build": {
                                "format": "docker",
                                "name": "Fedora-Docker-Base",
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)
        server_args = {
            "image_conf": {
                "image-build": {
                    "install_tree": self.topdir + "/compose/Server/$arch/os",
                    "kickstart": "fedora-docker-base.ks",
                    "format": ["docker"],
                    "repo": self.topdir + "/compose/Server/$arch/os",
                    "variant": compose.variants["Server"],
                    "target": "f24",
                    "disk_size": 3,
                    "name": "Fedora-Docker-Base",
                    "arches": ["amd64", "x86_64"],
                    "version": "25",
                    "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                    "distro": "Fedora-20",
                    "release": "20151203.t.0",
                }
            },
            "conf_file": self.topdir
            + "/work/image-build/Server/docker_Fedora-Docker-Base_amd64-x86_64.cfg",
            "image_dir": self.topdir + "/compose/Server/%(arch)s/images",
            "relative_image_dir": "Server/%(arch)s/images",
            "link_type": "hardlink-or-copy",
            "scratch": False,
        }
        self.assertEqual(
            phase.pool.queue_put.mock_calls, [mock.call((compose, server_args))]
        )

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_filter_all_variants(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Client|Server$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "arches": ["non-existing"],
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertFalse(phase.pool.add.called)
        self.assertFalse(phase.pool.queue_put.called)

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_set_install_tree(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Server$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "arches": ["x86_64"],
                                "install_tree_from": "Server-optional",
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )
        compose.setup_optional()

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)

        self.assertTrue(phase.pool.queue_put.called_once)
        args, kwargs = phase.pool.queue_put.call_args
        self.assertEqual(args[0][0], compose)
        self.assertDictEqual(
            args[0][1],
            {
                "image_conf": {
                    "image-build": {
                        "install_tree": self.topdir
                        + "/compose/Server-optional/$arch/os",
                        "kickstart": "fedora-docker-base.ks",
                        "format": ["docker"],
                        "repo": self.topdir + "/compose/Server/$arch/os",
                        "variant": compose.variants["Server"],
                        "target": "f24",
                        "disk_size": 3,
                        "name": "Fedora-Docker-Base",
                        "arches": ["x86_64"],
                        "version": "Rawhide",
                        "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                        "distro": "Fedora-20",
                    }
                },
                "conf_file": self.topdir
                + "/work/image-build/Server/docker_Fedora-Docker-Base_x86_64.cfg",
                "image_dir": self.topdir + "/compose/Server/%(arch)s/images",
                "relative_image_dir": "Server/%(arch)s/images",
                "link_type": "hardlink-or-copy",
                "scratch": False,
            },
        )

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_set_install_tree_from_path(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Server$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "arches": ["x86_64"],
                                "install_tree_from": "/my/tree",
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
                "translate_paths": [("/my", "http://example.com")],
            },
        )

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)

        self.assertTrue(phase.pool.queue_put.called_once)
        args, kwargs = phase.pool.queue_put.call_args
        self.assertEqual(args[0][0], compose)
        self.assertDictEqual(
            args[0][1],
            {
                "image_conf": {
                    "image-build": {
                        "install_tree": "http://example.com/tree",
                        "kickstart": "fedora-docker-base.ks",
                        "format": ["docker"],
                        "repo": self.topdir + "/compose/Server/$arch/os",
                        "variant": compose.variants["Server"],
                        "target": "f24",
                        "disk_size": 3,
                        "name": "Fedora-Docker-Base",
                        "arches": ["x86_64"],
                        "version": "Rawhide",
                        "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                        "distro": "Fedora-20",
                    }
                },
                "conf_file": self.topdir
                + "/work/image-build/Server/docker_Fedora-Docker-Base_x86_64.cfg",
                "image_dir": self.topdir + "/compose/Server/%(arch)s/images",
                "relative_image_dir": "Server/%(arch)s/images",
                "link_type": "hardlink-or-copy",
                "scratch": False,
            },
        )

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_set_extra_repos(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Server$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "arches": ["x86_64"],
                                "repo_from": ["Everything", "Server-optional"],
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )
        compose.setup_optional()

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)

        self.assertTrue(phase.pool.queue_put.called_once)
        args, kwargs = phase.pool.queue_put.call_args
        self.assertEqual(args[0][0], compose)
        self.assertDictEqual(
            args[0][1],
            {
                "image_conf": {
                    "image-build": {
                        "install_tree": self.topdir + "/compose/Server/$arch/os",
                        "kickstart": "fedora-docker-base.ks",
                        "format": ["docker"],
                        "repo": ",".join(
                            [
                                self.topdir + "/compose/Everything/$arch/os",
                                self.topdir + "/compose/Server-optional/$arch/os",
                                self.topdir + "/compose/Server/$arch/os",
                            ]
                        ),
                        "variant": compose.variants["Server"],
                        "target": "f24",
                        "disk_size": 3,
                        "name": "Fedora-Docker-Base",
                        "arches": ["x86_64"],
                        "version": "Rawhide",
                        "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                        "distro": "Fedora-20",
                    }
                },
                "conf_file": self.topdir
                + "/work/image-build/Server/docker_Fedora-Docker-Base_x86_64.cfg",
                "image_dir": self.topdir + "/compose/Server/%(arch)s/images",
                "relative_image_dir": "Server/%(arch)s/images",
                "link_type": "hardlink-or-copy",
                "scratch": False,
            },
        )

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_set_external_install_tree(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Server$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "arches": ["x86_64"],
                                "install_tree_from": "http://example.com/install-tree/",
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)

        self.assertTrue(phase.pool.queue_put.called_once)
        args, kwargs = phase.pool.queue_put.call_args
        self.assertEqual(args[0][0], compose)
        self.assertDictEqual(
            args[0][1],
            {
                "image_conf": {
                    "image-build": {
                        "install_tree": "http://example.com/install-tree/",
                        "kickstart": "fedora-docker-base.ks",
                        "format": ["docker"],
                        "repo": ",".join([self.topdir + "/compose/Server/$arch/os"]),
                        "variant": compose.variants["Server"],
                        "target": "f24",
                        "disk_size": 3,
                        "name": "Fedora-Docker-Base",
                        "arches": ["x86_64"],
                        "version": "Rawhide",
                        "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                        "distro": "Fedora-20",
                    }
                },
                "conf_file": self.topdir
                + "/work/image-build/Server/docker_Fedora-Docker-Base_x86_64.cfg",
                "image_dir": self.topdir + "/compose/Server/%(arch)s/images",
                "relative_image_dir": "Server/%(arch)s/images",
                "link_type": "hardlink-or-copy",
                "scratch": False,
            },
        )

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_create_release(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Server$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "arches": ["x86_64"],
                                "release": None,
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)

        self.assertTrue(phase.pool.queue_put.called_once)
        args, kwargs = phase.pool.queue_put.call_args
        self.assertEqual(
            args[0][1].get("image_conf", {}).get("image-build", {}).get("release"),
            "20151203.t.0",
        )

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_create_release_with_explicit_config(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Server$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "arches": ["x86_64"],
                                "release": "!RELEASE_FROM_LABEL_DATE_TYPE_RESPIN",
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)

        self.assertTrue(phase.pool.queue_put.called_once)
        args, kwargs = phase.pool.queue_put.call_args
        self.assertEqual(
            args[0][1].get("image_conf", {}).get("image-build", {}).get("release"),
            "20151203.t.0",
        )

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_scratch_build(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Server$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "arches": ["x86_64"],
                                "scratch": True,
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)

        self.assertTrue(phase.pool.queue_put.called_once)
        args, kwargs = phase.pool.queue_put.call_args
        self.assertTrue(args[0][1].get("scratch"))

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_image_build_optional(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Server-optional$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "failable": ["x86_64"],
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )
        compose.setup_optional()

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)
        server_args = {
            "image_conf": {
                "image-build": {
                    "install_tree": self.topdir + "/compose/Server/$arch/os",
                    "kickstart": "fedora-docker-base.ks",
                    "format": ["docker"],
                    "repo": self.topdir + "/compose/Server-optional/$arch/os",
                    "variant": compose.all_variants["Server-optional"],
                    "target": "f24",
                    "disk_size": 3,
                    "name": "Fedora-Docker-Base",
                    "arches": ["x86_64"],
                    "version": "Rawhide",
                    "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                    "distro": "Fedora-20",
                    "can_fail": ["x86_64"],
                }
            },
            "conf_file": self.topdir
            + "/work/image-build/Server-optional/docker_Fedora-Docker-Base_x86_64.cfg",
            "image_dir": self.topdir + "/compose/Server-optional/%(arch)s/images",
            "relative_image_dir": "Server-optional/%(arch)s/images",
            "link_type": "hardlink-or-copy",
            "scratch": False,
        }
        self.assertEqual(
            phase.pool.queue_put.mock_calls, [mock.call((compose, server_args))]
        )

    @mock.patch("pungi.phases.image_build.ThreadPool")
    def test_failable_star(self, ThreadPool):
        compose = DummyCompose(
            self.topdir,
            {
                "image_build": {
                    "^Server$": [
                        {
                            "image-build": {
                                "format": ["docker"],
                                "name": "Fedora-Docker-Base",
                                "target": "f24",
                                "version": "Rawhide",
                                "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",  # noqa: E501
                                "kickstart": "fedora-docker-base.ks",
                                "distro": "Fedora-20",
                                "disk_size": 3,
                                "failable": ["*"],
                            }
                        }
                    ]
                },
                "koji_profile": "koji",
            },
        )
        compose.setup_optional()

        self.assertValidConfig(compose.conf)

        phase = ImageBuildPhase(compose)

        phase.run()

        # assert at least one thread was started
        self.assertTrue(phase.pool.add.called)
        server_args = {
            "image_conf": {
                "image-build": {
                    "install_tree": self.topdir + "/compose/Server/$arch/os",
                    "kickstart": "fedora-docker-base.ks",
                    "format": ["docker"],
                    "repo": self.topdir + "/compose/Server/$arch/os",
                    "variant": compose.all_variants["Server"],
                    "target": "f24",
                    "disk_size": 3,
                    "name": "Fedora-Docker-Base",
                    "arches": ["amd64", "x86_64"],
                    "version": "Rawhide",
                    "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                    "distro": "Fedora-20",
                    "can_fail": ["amd64", "x86_64"],
                }
            },
            "conf_file": self.topdir
            + "/work/image-build/Server/docker_Fedora-Docker-Base_amd64-x86_64.cfg",
            "image_dir": self.topdir + "/compose/Server/%(arch)s/images",
            "relative_image_dir": "Server/%(arch)s/images",
            "link_type": "hardlink-or-copy",
            "scratch": False,
        }
        self.assertEqual(
            phase.pool.queue_put.mock_calls, [mock.call((compose, server_args))]
        )


class TestCreateImageBuildThread(PungiTestCase):
    @mock.patch("pungi.phases.image_build.get_mtime")
    @mock.patch("pungi.phases.image_build.get_file_size")
    @mock.patch("pungi.phases.image_build.KojiWrapper")
    @mock.patch("pungi.phases.image_build.Linker")
    def test_process(self, Linker, KojiWrapper, get_file_size, get_mtime):
        compose = DummyCompose(self.topdir, {"koji_profile": "koji"})
        pool = mock.Mock()
        cmd = {
            "image_conf": {
                "image-build": {
                    "install_tree": "/ostree/$arch/Client",
                    "kickstart": "fedora-docker-base.ks",
                    "format": ["docker", "qcow2"],
                    "repo": "/ostree/$arch/Client",
                    "variant": compose.variants["Client"],
                    "target": "f24",
                    "disk_size": 3,
                    "name": "Fedora-Docker-Base",
                    "arches": ["amd64", "x86_64"],
                    "version": "Rawhide",
                    "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                    "distro": "Fedora-20",
                    "subvariant": "KDE",
                }
            },
            "conf_file": "amd64,x86_64-Client-Fedora-Docker-Base-docker",
            "image_dir": self.topdir + "/compose/Client/%(arch)s/images",
            "relative_image_dir": "image_dir/Client/%(arch)s",
            "link_type": "hardlink-or-copy",
            "scratch": False,
        }
        koji_wrapper = KojiWrapper.return_value
        koji_wrapper.run_blocking_cmd.return_value = {
            "retcode": 0,
            "output": None,
            "task_id": 1234,
        }
        koji_wrapper.get_image_paths.return_value = {
            "amd64": [
                "/koji/task/1235/tdl-amd64.xml",
                "/koji/task/1235/Fedora-Docker-Base-20160103.amd64.qcow2",
                "/koji/task/1235/Fedora-Docker-Base-20160103.amd64.tar.gz",
            ],
            "x86_64": [
                "/koji/task/1235/tdl-x86_64.xml",
                "/koji/task/1235/Fedora-Docker-Base-20160103.x86_64.qcow2",
                "/koji/task/1235/Fedora-Docker-Base-20160103.x86_64.tar.gz",
            ],
        }

        linker = Linker.return_value
        get_file_size.return_value = 1024
        get_mtime.return_value = 13579

        t = CreateImageBuildThread(pool)
        with mock.patch("time.sleep"):
            t.process((compose, cmd), 1)

        self.assertEqual(
            koji_wrapper.get_image_build_cmd.call_args_list,
            [
                mock.call(
                    cmd["image_conf"],
                    conf_file_dest="amd64,x86_64-Client-Fedora-Docker-Base-docker",
                    scratch=False,
                )
            ],
        )

        self.assertEqual(
            koji_wrapper.run_blocking_cmd.call_args_list,
            [
                mock.call(
                    koji_wrapper.get_image_build_cmd.return_value,
                    log_file=self.topdir
                    + "/logs/amd64-x86_64/imagebuild-Client-KDE-docker-qcow2.amd64-x86_64.log",  # noqa: E501
                )
            ],
        )

        six.assertCountEqual(
            self,
            linker.mock_calls,
            [
                mock.call.link(
                    "/koji/task/1235/Fedora-Docker-Base-20160103.amd64.qcow2",
                    self.topdir
                    + "/compose/Client/amd64/images/Fedora-Docker-Base-20160103.amd64.qcow2",  # noqa: E501
                    link_type="hardlink-or-copy",
                ),
                mock.call.link(
                    "/koji/task/1235/Fedora-Docker-Base-20160103.amd64.tar.gz",
                    self.topdir
                    + "/compose/Client/amd64/images/Fedora-Docker-Base-20160103.amd64.tar.gz",  # noqa: E501
                    link_type="hardlink-or-copy",
                ),
                mock.call.link(
                    "/koji/task/1235/Fedora-Docker-Base-20160103.x86_64.qcow2",
                    self.topdir
                    + "/compose/Client/x86_64/images/Fedora-Docker-Base-20160103.x86_64.qcow2",  # noqa: E501
                    link_type="hardlink-or-copy",
                ),
                mock.call.link(
                    "/koji/task/1235/Fedora-Docker-Base-20160103.x86_64.tar.gz",
                    self.topdir
                    + "/compose/Client/x86_64/images/Fedora-Docker-Base-20160103.x86_64.tar.gz",  # noqa: E501
                    link_type="hardlink-or-copy",
                ),
            ],
        )

        image_relative_paths = {
            "image_dir/Client/amd64/Fedora-Docker-Base-20160103.amd64.qcow2": {
                "format": "qcow2",
                "type": "qcow2",
                "arch": "amd64",
            },
            "image_dir/Client/amd64/Fedora-Docker-Base-20160103.amd64.tar.gz": {
                "format": "tar.gz",
                "type": "docker",
                "arch": "amd64",
            },
            "image_dir/Client/x86_64/Fedora-Docker-Base-20160103.x86_64.qcow2": {
                "format": "qcow2",
                "type": "qcow2",
                "arch": "x86_64",
            },
            "image_dir/Client/x86_64/Fedora-Docker-Base-20160103.x86_64.tar.gz": {
                "format": "tar.gz",
                "type": "docker",
                "arch": "x86_64",
            },
        }

        # Assert there are 4 images added to manifest and the arguments are sane
        self.assertEqual(len(compose.im.add.call_args_list), 4)
        for call in compose.im.add.call_args_list:
            _, kwargs = call
            image = kwargs["image"]
            self.assertEqual(kwargs["variant"], "Client")
            self.assertIn(kwargs["arch"], ("amd64", "x86_64"))
            self.assertEqual(kwargs["arch"], image.arch)
            self.assertIn(image.path, image_relative_paths)
            data = image_relative_paths.pop(image.path)
            self.assertEqual(data["format"], image.format)
            self.assertEqual(data["type"], image.type)
            self.assertEqual("KDE", image.subvariant)

        self.assertTrue(os.path.isdir(self.topdir + "/compose/Client/amd64/images"))
        self.assertTrue(os.path.isdir(self.topdir + "/compose/Client/x86_64/images"))

    @mock.patch("pungi.phases.image_build.KojiWrapper")
    @mock.patch("pungi.phases.image_build.Linker")
    def test_process_handle_fail(self, Linker, KojiWrapper):
        compose = DummyCompose(self.topdir, {"koji_profile": "koji"})
        pool = mock.Mock()
        cmd = {
            "image_conf": {
                "image-build": {
                    "install_tree": "/ostree/$arch/Client",
                    "kickstart": "fedora-docker-base.ks",
                    "format": ["docker", "qcow2"],
                    "repo": "/ostree/$arch/Client",
                    "variant": compose.variants["Client"],
                    "target": "f24",
                    "disk_size": 3,
                    "name": "Fedora-Docker-Base",
                    "arches": ["amd64", "x86_64"],
                    "version": "Rawhide",
                    "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                    "distro": "Fedora-20",
                    "can_fail": ["amd64", "x86_64"],
                }
            },
            "conf_file": "amd64,x86_64-Client-Fedora-Docker-Base-docker",
            "image_dir": "/image_dir/Client/%(arch)s",
            "relative_image_dir": "image_dir/Client/%(arch)s",
            "link_type": "hardlink-or-copy",
            "scratch": False,
        }
        koji_wrapper = KojiWrapper.return_value
        koji_wrapper.run_blocking_cmd.return_value = {
            "retcode": 1,
            "output": None,
            "task_id": 1234,
        }

        t = CreateImageBuildThread(pool)
        with mock.patch("time.sleep"):
            t.process((compose, cmd), 1)

        pool._logger.error.assert_has_calls(
            [
                mock.call(
                    "[FAIL] Image build (variant Client, arch *, subvariant Client) failed, but going on anyway."  # noqa: E501
                ),
                mock.call(
                    "ImageBuild task failed: 1234. See %s for more details."
                    % (
                        os.path.join(
                            self.topdir,
                            "logs/amd64-x86_64/imagebuild-Client-Client-docker-qcow2.amd64-x86_64.log",  # noqa: E501
                        )
                    )
                ),
            ]
        )

    @mock.patch("pungi.phases.image_build.KojiWrapper")
    @mock.patch("pungi.phases.image_build.Linker")
    def test_process_handle_exception(self, Linker, KojiWrapper):
        compose = DummyCompose(self.topdir, {"koji_profile": "koji"})
        pool = mock.Mock()
        cmd = {
            "image_conf": {
                "image-build": {
                    "install_tree": "/ostree/$arch/Client",
                    "kickstart": "fedora-docker-base.ks",
                    "format": ["docker", "qcow2"],
                    "repo": "/ostree/$arch/Client",
                    "variant": compose.variants["Client"],
                    "target": "f24",
                    "disk_size": 3,
                    "name": "Fedora-Docker-Base",
                    "arches": ["amd64", "x86_64"],
                    "version": "Rawhide",
                    "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                    "distro": "Fedora-20",
                    "can_fail": ["amd64", "x86_64"],
                }
            },
            "conf_file": "amd64,x86_64-Client-Fedora-Docker-Base-docker",
            "image_dir": "/image_dir/Client/%(arch)s",
            "relative_image_dir": "image_dir/Client/%(arch)s",
            "link_type": "hardlink-or-copy",
            "scratch": False,
        }

        koji_wrapper = KojiWrapper.return_value
        koji_wrapper.run_blocking_cmd.side_effect = boom

        t = CreateImageBuildThread(pool)
        with mock.patch("time.sleep"):
            t.process((compose, cmd), 1)

        pool._logger.error.assert_has_calls(
            [
                mock.call(
                    "[FAIL] Image build (variant Client, arch *, subvariant Client) failed, but going on anyway."  # noqa: E501
                ),
                mock.call("BOOM"),
            ]
        )

    @mock.patch("pungi.phases.image_build.KojiWrapper")
    @mock.patch("pungi.phases.image_build.Linker")
    def test_process_handle_fail_only_one_optional(self, Linker, KojiWrapper):
        compose = DummyCompose(self.topdir, {"koji_profile": "koji"})
        pool = mock.Mock()
        cmd = {
            "image_conf": {
                "image-build": {
                    "install_tree": "/ostree/$arch/Client",
                    "kickstart": "fedora-docker-base.ks",
                    "format": ["docker", "qcow2"],
                    "repo": "/ostree/$arch/Client",
                    "variant": compose.variants["Client"],
                    "target": "f24",
                    "disk_size": 3,
                    "name": "Fedora-Docker-Base",
                    "arches": ["amd64", "x86_64"],
                    "version": "Rawhide",
                    "ksurl": "git://git.fedorahosted.org/git/spin-kickstarts.git",
                    "distro": "Fedora-20",
                    "can_fail": ["amd64"],
                }
            },
            "conf_file": "amd64,x86_64-Client-Fedora-Docker-Base-docker",
            "image_dir": "/image_dir/Client/%(arch)s",
            "relative_image_dir": "image_dir/Client/%(arch)s",
            "link_type": "hardlink-or-copy",
            "scratch": False,
        }

        koji_wrapper = KojiWrapper.return_value
        koji_wrapper.run_blocking_cmd.return_value = {
            "retcode": 1,
            "output": None,
            "task_id": 1234,
        }

        t = CreateImageBuildThread(pool)
        with self.assertRaises(RuntimeError):
            with mock.patch("time.sleep"):
                t.process((compose, cmd), 1)