#   Copyright 2016 Red Hat, Inc.
#
#   Licensed under the Apache License, Version 2.0 (the "License"); you may
#   not use this file except in compliance with the License. You may obtain
#   a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#   License for the specific language governing permissions and limitations
#   under the License.
#

import mock

from tripleoclient.tests import fakes
from tripleoclient.tests.v1.overcloud_deploy import fakes as deploy_fakes
from tripleoclient.v2 import tripleo_container_image as tcib


IMAGE_YAML = """---
container_images:
  - image_source: "tripleo"
    imagename: "test/keystone:tag"
"""

MOCK_WALK = [
    ("", ["base"], [],),
    ("/base", ["memcached", "openstack"], ["config.yaml", "test.doc"],),
    ("/base/memcached", [], ["memcached.yaml"],),
    ("/base/openstack", ["glance", "keystone", "neutron", "nova"], [],),
    (
        "/base/openstack/glance",
        [],
        ["glance-registry.yaml", "glance-api.yaml"],
    ),
    ("/base/openstack/keystone", [], ["keystone.yaml"],),
    ("/base/openstack/neutron", ["api"], [],),
    ("/base/openstack/neutron/api", [], ["neutron-api.yml"],),
    ("/base/openstack/nova", [], [],),
]


class TestContainerImages(deploy_fakes.TestDeployOvercloud):
    def setUp(self):
        super(TestContainerImages, self).setUp()
        self.app = fakes.FakeApp()
        self.os_walk = mock.patch(
            "os.walk", autospec=True, return_value=iter(MOCK_WALK)
        )
        self.os_walk.start()
        self.addCleanup(self.os_walk.stop)
        self.os_listdir = mock.patch(
            "os.listdir", autospec=True, return_value=["config.yaml"]
        )
        self.os_listdir.start()
        self.addCleanup(self.os_listdir.stop)
        self.run_ansible_playbook = mock.patch(
            "tripleoclient.utils.run_ansible_playbook", autospec=True
        )
        self.run_ansible_playbook.start()
        self.addCleanup(self.run_ansible_playbook.stop)
        self.buildah_build_all = mock.patch(
            "tripleo_common.image.builder.buildah.BuildahBuilder.build_all",
            autospec=True,
        )
        self.mock_buildah = self.buildah_build_all.start()
        self.addCleanup(self.buildah_build_all.stop)
        self.cmd = tcib.Build(self.app, None)

    def _take_action(self, parsed_args):
        self.cmd.image_parents = {"keystone": "base"}
        mock_open = mock.mock_open(read_data=IMAGE_YAML)
        with mock.patch("os.path.isfile", autospec=True) as mock_isfile:
            mock_isfile.return_value = True
            with mock.patch("os.path.isdir", autospec=True) as mock_isdir:
                mock_isdir.return_value = True
                with mock.patch('six.moves.builtins.open', mock_open):
                    with mock.patch(
                        "tripleoclient.v2.tripleo_container_image.Build"
                        ".find_image",
                        autospec=True,
                    ) as mock_find_image:
                        mock_find_image.return_value = {"tcib_option": "data"}
                        self.cmd.take_action(parsed_args)

    def test_find_image(self):
        mock_open = mock.mock_open(read_data='---\ntcib_option: "data"')
        with mock.patch('six.moves.builtins.open', mock_open):
            image = self.cmd.find_image("keystone", "some/path", "base-image")
        self.assertEqual(image, {"tcib_option": "data"})

    def test_build_tree(self):
        image = self.cmd.build_tree("some/path")
        self.assertEqual(
            image,
            [
                {
                    "base": [
                        "memcached",
                        {
                            "openstack": [
                                "glance",
                                "keystone",
                                {"neutron": ["api"]},
                                "nova",
                            ]
                        },
                    ]
                }
            ],
        )

    def test_image_regex(self):
        image = self.cmd.imagename_to_regex("test/centos-binary-keystone:tag")
        self.assertEqual(image, "keystone")
        image = self.cmd.imagename_to_regex("test/rhel-binary-keystone:tag")
        self.assertEqual(image, "keystone")
        image = self.cmd.imagename_to_regex("test/rhel-source-keystone:tag")
        self.assertEqual(image, "keystone")
        image = self.cmd.imagename_to_regex("test/rhel-rdo-keystone:tag")
        self.assertEqual(image, "keystone")
        image = self.cmd.imagename_to_regex("test/rhel-rhos-keystone:tag")
        self.assertEqual(image, "keystone")
        image = self.cmd.imagename_to_regex("test/other-keystone:tag")
        self.assertEqual(image, "other-keystone")

    def test_rectify_excludes(self):
        self.cmd.identified_images = ["keystone", "nova", "glance"]
        excludes = self.cmd.rectify_excludes(images_to_prepare=["glance"])
        self.assertEqual(excludes, ["keystone", "nova"])

    def test_image_build_yaml(self):
        arglist = ["--config-file", "config.yaml"]
        verifylist = [("config_file", "config.yaml")]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        self._take_action(parsed_args=parsed_args)

        assert self.mock_buildah.called

    def test_image_build_with_skip_build(self):
        arglist = ["--config-file", "config.yaml", "--skip-build"]
        verifylist = [("config_file", "config.yaml"), ("skip_build", True)]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        self._take_action(parsed_args=parsed_args)

        assert not self.mock_buildah.called

    def test_image_build_with_push(self):
        arglist = ["--config-file", "config.yaml", "--push"]
        verifylist = [("config_file", "config.yaml"), ("push", True)]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        self._take_action(parsed_args=parsed_args)

        assert self.mock_buildah.called

    def test_image_build_with_volume(self):
        arglist = ["--config-file", "config.yaml", "--volume", "bind/mount"]
        verifylist = [
            ("config_file", "config.yaml"),
            (
                "volumes",
                [
                    "/etc/yum.repos.d:/etc/distro.repos.d:z",
                    "/etc/pki/rpm-gpg:/etc/pki/rpm-gpg:z",
                    "bind/mount",
                ],
            ),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        self._take_action(parsed_args=parsed_args)

        assert self.mock_buildah.called

    def test_image_build_with_exclude(self):
        arglist = ["--exclude", "image1"]
        verifylist = [
            ("excludes", ["image1"]),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        self._take_action(parsed_args=parsed_args)

        assert self.mock_buildah.called

    def test_image_build_failure_no_config_file(self):
        arglist = ["--config-file", "not-a-file-config.yaml"]
        verifylist = [
            ("config_file", "not-a-file-config.yaml"),
        ]

        self.check_parser(self.cmd, arglist, verifylist)

    def test_image_build_config_dir(self):
        arglist = ["--config-file", "config.yaml", "--config-path", "/foo"]
        verifylist = [("config_file", "config.yaml"), ("config_path", "/foo")]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        self._take_action(parsed_args=parsed_args)
        self.assertEqual(self.cmd.tcib_config_path, '/foo/tcib')

    def test_image_build_failure_no_config_dir(self):
        arglist = ["--config-path", "not-a-path"]
        verifylist = [
            ("config_path", "not-a-path"),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        with mock.patch("os.path.isfile", autospec=True) as mock_isfile:
            mock_isfile.return_value = True
            self.assertRaises(IOError, self.cmd.take_action, parsed_args)

    def test_process_images(self):
        rtn_value = {'yay': 'values'}
        arglist = ["--config-path", "foobar/"]
        verifylist = [
            ("config_path", "foobar/"),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)

        expected_images = ['foo', 'foobar']
        image_configs = {}
        self.cmd.tcib_config_path = '/foo/tcib'
        with mock.patch("tripleoclient.v2.tripleo_container_image.Build"
                        ".find_image", autospec=True) as mock_find_image:

            mock_find_image.return_value = rtn_value
            cfgs = self.cmd.process_images(expected_images, parsed_args,
                                           image_configs)
            mock_find_image.assert_called_once_with(self.cmd, 'foo',
                                                    '/foo/tcib', 'ubi8')
        self.assertEqual(cfgs, {'foo': rtn_value})


class TestContainerImagesHotfix(deploy_fakes.TestDeployOvercloud):
    def setUp(self):
        super(TestContainerImagesHotfix, self).setUp()
        self.run_ansible_playbook = mock.patch(
            "tripleoclient.utils.run_ansible_playbook", autospec=True
        )
        self.run_ansible_playbook.start()
        self.addCleanup(self.run_ansible_playbook.stop)
        self.cmd = tcib.HotFix(self.app, None)

    def _take_action(self, parsed_args):
        with mock.patch("os.path.isfile", autospec=True) as mock_isfile:
            mock_isfile.return_value = True
            self.cmd.take_action(parsed_args)

    def test_image_hotfix(self):
        arglist = ["--image", "container1", "--rpms-path", "/opt"]
        verifylist = [
            ("images", ["container1"]),
            ("rpms_path", "/opt"),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        self._take_action(parsed_args=parsed_args)

    def test_image_hotfix_multi_image(self):
        arglist = [
            "--image",
            "container1",
            "--image",
            "container2",
            "--rpms-path",
            "/opt",
        ]
        verifylist = [
            ("images", ["container1", "container2"]),
            ("rpms_path", "/opt"),
        ]

        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
        self._take_action(parsed_args=parsed_args)

    def test_image_hotfix_missing_args(self):
        arglist = []
        verifylist = []

        self.assertRaises(
            deploy_fakes.fakes.utils.ParserException,
            self.check_parser,
            self.cmd,
            arglist,
            verifylist,
        )
