# This file is part of cloud-init. See LICENSE file for license information.

""" test_handler_apt_configure_sources_list
Test templating of sources list
"""
import logging
import os
import shutil
import tempfile
from unittest import mock

from cloudinit import subp, templater, util
from cloudinit.config import cc_apt_configure
from cloudinit.distros.debian import Distro
from tests.unittests import helpers as t_help
from tests.unittests.util import get_cloud

LOG = logging.getLogger(__name__)

YAML_TEXT_CUSTOM_SL = """
apt_mirror: http://archive.ubuntu.com/ubuntu/
apt_custom_sources_list: |
    ## template:jinja
    ## Note, this file is written by cloud-init on first boot of an instance
    ## modifications made here will not survive a re-bundle.
    ## if you wish to make changes you can:
    ## a.) add 'apt_preserve_sources_list: true' to /etc/cloud/cloud.cfg
    ##     or do the same in user-data
    ## b.) add sources in /etc/apt/sources.list.d
    ## c.) make changes to template file /etc/cloud/templates/sources.list.tmpl

    # See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
    # newer versions of the distribution.
    deb {{mirror}} {{codename}} main restricted
    deb-src {{mirror}} {{codename}} main restricted
    # FIND_SOMETHING_SPECIAL
"""

EXPECTED_CONVERTED_CONTENT = """## Note, this file is written by cloud-init on first boot of an instance
## modifications made here will not survive a re-bundle.
## if you wish to make changes you can:
## a.) add 'apt_preserve_sources_list: true' to /etc/cloud/cloud.cfg
##     or do the same in user-data
## b.) add sources in /etc/apt/sources.list.d
## c.) make changes to template file /etc/cloud/templates/sources.list.tmpl

# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
# newer versions of the distribution.
deb http://archive.ubuntu.com/ubuntu/ fakerelease main restricted
deb-src http://archive.ubuntu.com/ubuntu/ fakerelease main restricted
# FIND_SOMETHING_SPECIAL
"""


class TestAptSourceConfigSourceList(t_help.FilesystemMockingTestCase):
    """TestAptSourceConfigSourceList
    Main Class to test sources list rendering
    """

    def setUp(self):
        super(TestAptSourceConfigSourceList, self).setUp()
        self.subp = subp.subp
        self.new_root = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, self.new_root)

        rpatcher = mock.patch("cloudinit.util.lsb_release")
        get_rel = rpatcher.start()
        get_rel.return_value = {"codename": "fakerelease"}
        self.addCleanup(rpatcher.stop)
        apatcher = mock.patch("cloudinit.util.get_dpkg_architecture")
        get_arch = apatcher.start()
        get_arch.return_value = "amd64"
        self.addCleanup(apatcher.stop)

    def apt_source_list(self, distro, mirror, mirrorcheck=None):
        """apt_source_list
        Test rendering of a source.list from template for a given distro
        """
        if mirrorcheck is None:
            mirrorcheck = mirror

        if isinstance(mirror, list):
            cfg = {"apt_mirror_search": mirror}
        else:
            cfg = {"apt_mirror": mirror}

        mycloud = get_cloud(distro)

        with mock.patch.object(util, "write_file") as mockwf:
            with mock.patch.object(
                util, "load_file", return_value="faketmpl"
            ) as mocklf:
                with mock.patch.object(
                    os.path, "isfile", return_value=True
                ) as mockisfile:
                    with mock.patch.object(
                        templater, "render_string", return_value="fake"
                    ) as mockrnd:
                        with mock.patch.object(util, "rename"):
                            cc_apt_configure.handle(
                                "test", cfg, mycloud, LOG, None
                            )

        mockisfile.assert_any_call(
            "/etc/cloud/templates/sources.list.%s.tmpl" % distro
        )
        mocklf.assert_any_call(
            "/etc/cloud/templates/sources.list.%s.tmpl" % distro
        )
        mockrnd.assert_called_once_with(
            "faketmpl",
            {
                "RELEASE": "fakerelease",
                "PRIMARY": mirrorcheck,
                "MIRROR": mirrorcheck,
                "SECURITY": mirrorcheck,
                "codename": "fakerelease",
                "primary": mirrorcheck,
                "mirror": mirrorcheck,
                "security": mirrorcheck,
            },
        )
        mockwf.assert_called_once_with(
            "/etc/apt/sources.list", "fake", mode=0o644
        )

    def test_apt_v1_source_list_debian(self):
        """Test rendering of a source.list from template for debian"""
        self.apt_source_list("debian", "http://httpredir.debian.org/debian")

    def test_apt_v1_source_list_ubuntu(self):
        """Test rendering of a source.list from template for ubuntu"""
        self.apt_source_list("ubuntu", "http://archive.ubuntu.com/ubuntu/")

    @staticmethod
    def myresolve(name):
        """Fake util.is_resolvable for mirrorfail tests"""
        if name == "does.not.exist":
            print("Faking FAIL for '%s'" % name)
            return False
        else:
            print("Faking SUCCESS for '%s'" % name)
            return True

    def test_apt_v1_srcl_debian_mirrorfail(self):
        """Test rendering of a source.list from template for debian"""
        with mock.patch.object(
            util, "is_resolvable", side_effect=self.myresolve
        ) as mockresolve:
            self.apt_source_list(
                "debian",
                [
                    "http://does.not.exist",
                    "http://httpredir.debian.org/debian",
                ],
                "http://httpredir.debian.org/debian",
            )
        mockresolve.assert_any_call("does.not.exist")
        mockresolve.assert_any_call("httpredir.debian.org")

    def test_apt_v1_srcl_ubuntu_mirrorfail(self):
        """Test rendering of a source.list from template for ubuntu"""
        with mock.patch.object(
            util, "is_resolvable", side_effect=self.myresolve
        ) as mockresolve:
            self.apt_source_list(
                "ubuntu",
                ["http://does.not.exist", "http://archive.ubuntu.com/ubuntu/"],
                "http://archive.ubuntu.com/ubuntu/",
            )
        mockresolve.assert_any_call("does.not.exist")
        mockresolve.assert_any_call("archive.ubuntu.com")

    def test_apt_v1_srcl_custom(self):
        """Test rendering from a custom source.list template"""
        cfg = util.load_yaml(YAML_TEXT_CUSTOM_SL)
        mycloud = get_cloud()

        # the second mock restores the original subp
        with mock.patch.object(util, "write_file") as mockwrite:
            with mock.patch.object(subp, "subp", self.subp):
                with mock.patch.object(
                    Distro, "get_primary_arch", return_value="amd64"
                ):
                    cc_apt_configure.handle(
                        "notimportant", cfg, mycloud, LOG, None
                    )

        mockwrite.assert_called_once_with(
            "/etc/apt/sources.list", EXPECTED_CONVERTED_CONTENT, mode=420
        )


# vi: ts=4 expandtab
