# -*- coding: utf-8 -*-
"""
test for pillar file_tree.py
"""

# Import python libs
from __future__ import absolute_import, print_function, unicode_literals

import os
import shutil
import tempfile

import salt.pillar.file_tree as file_tree

# Import Salt Libs
import salt.utils.files
import salt.utils.stringutils
from tests.support.helpers import TstSuiteLoggingHandler
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.mock import MagicMock, patch

# Import Salt Testing libs
from tests.support.runtests import RUNTIME_VARS
from tests.support.unit import TestCase

MINION_ID = "test-host"
NODEGROUP_PATH = os.path.join("nodegroups", "test-group", "files")
HOST_PATH = os.path.join("hosts", MINION_ID, "files")

BASE_PILLAR_CONTENT = {"files": {"hostfile": b"base", "groupfile": b"base"}}
DEV_PILLAR_CONTENT = {
    "files": {
        "hostfile": b"base",
        "groupfile": b"dev2",
        "hostfile1": b"dev1",
        "groupfile1": b"dev1",
        "hostfile2": b"dev2",
    }
}
PARENT_PILLAR_CONTENT = {
    "files": {"hostfile": b"base", "groupfile": b"base", "hostfile2": b"dev2"}
}

FILE_DATA = {
    os.path.join("base", HOST_PATH, "hostfile"): "base",
    os.path.join("dev1", HOST_PATH, "hostfile1"): "dev1",
    os.path.join("dev2", HOST_PATH, "hostfile2"): "dev2",
    os.path.join("base", NODEGROUP_PATH, "groupfile"): "base",
    os.path.join("dev1", NODEGROUP_PATH, "groupfile1"): "dev1",
    os.path.join("dev2", NODEGROUP_PATH, "groupfile"): "dev2",  # test merging
}

_CHECK_MINIONS_RETURN = {"minions": [MINION_ID], "missing": []}


class FileTreePillarTestCase(TestCase, LoaderModuleMockMixin):
    "test file_tree pillar"
    maxDiff = None

    def setup_loader_modules(self):
        self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
        self.addCleanup(shutil.rmtree, self.tmpdir)
        cachedir = os.path.join(self.tmpdir, "cachedir")
        os.makedirs(os.path.join(cachedir, "file_tree"))
        self.pillar_path = self._create_pillar_files()
        return {
            file_tree: {
                "__opts__": {
                    "cachedir": cachedir,
                    "pillar_roots": {
                        "base": [os.path.join(self.pillar_path, "base")],
                        "dev": [
                            os.path.join(self.pillar_path, "base"),
                            os.path.join(self.pillar_path, "dev1"),
                            os.path.join(self.pillar_path, "dev2"),
                        ],
                        "parent": [
                            os.path.join(self.pillar_path, "base", "sub1"),
                            os.path.join(self.pillar_path, "dev2", "sub"),
                            os.path.join(self.pillar_path, "base", "sub2"),
                        ],
                    },
                    "pillarenv": "base",
                    "nodegroups": {"test-group": [MINION_ID]},
                    "optimization_order": [0, 1, 2],
                    "file_buffer_size": 262144,
                    "file_roots": {"base": "", "dev": "", "parent": ""},
                    "extension_modules": "",
                    "renderer": "yaml_jinja",
                    "renderer_blacklist": [],
                    "renderer_whitelist": [],
                }
            }
        }

    def _create_pillar_files(self):
        "create files in tempdir"
        pillar_path = os.path.join(self.tmpdir, "file_tree")
        for filename in FILE_DATA:
            filepath = os.path.join(pillar_path, filename)
            os.makedirs(os.path.dirname(filepath))
            with salt.utils.files.fopen(filepath, "w") as data_file:
                data_file.write(salt.utils.stringutils.to_str(FILE_DATA[filename]))
        return pillar_path

    def test_absolute_path(self):
        "check file tree is imported correctly with an absolute path"
        absolute_path = os.path.join(self.pillar_path, "base")
        with patch(
            "salt.utils.minions.CkMinions.check_minions",
            MagicMock(return_value=_CHECK_MINIONS_RETURN),
        ):
            mypillar = file_tree.ext_pillar(MINION_ID, None, absolute_path)
            self.assertEqual(BASE_PILLAR_CONTENT, mypillar)

            with patch.dict(file_tree.__opts__, {"pillarenv": "dev"}):
                mypillar = file_tree.ext_pillar(MINION_ID, None, absolute_path)
                self.assertEqual(BASE_PILLAR_CONTENT, mypillar)

    def test_relative_path(self):
        "check file tree is imported correctly with a relative path"
        with patch(
            "salt.utils.minions.CkMinions.check_minions",
            MagicMock(return_value=_CHECK_MINIONS_RETURN),
        ):
            mypillar = file_tree.ext_pillar(MINION_ID, None, ".")
            self.assertEqual(BASE_PILLAR_CONTENT, mypillar)

            with patch.dict(file_tree.__opts__, {"pillarenv": "dev"}):
                mypillar = file_tree.ext_pillar(MINION_ID, None, ".")
                self.assertEqual(DEV_PILLAR_CONTENT, mypillar)

    def test_parent_path(self):
        "check if file tree is merged correctly with a .. path"
        with patch(
            "salt.utils.minions.CkMinions.check_minions",
            MagicMock(return_value=_CHECK_MINIONS_RETURN),
        ):
            with patch.dict(file_tree.__opts__, {"pillarenv": "parent"}):
                mypillar = file_tree.ext_pillar(MINION_ID, None, "..")
                self.assertEqual(PARENT_PILLAR_CONTENT, mypillar)

    def test_no_pillarenv(self):
        "confirm that file_tree yells when pillarenv is missing for a relative path"
        with patch(
            "salt.utils.minions.CkMinions.check_minions",
            MagicMock(return_value=_CHECK_MINIONS_RETURN),
        ):
            with patch.dict(file_tree.__opts__, {"pillarenv": None}):
                with TstSuiteLoggingHandler() as handler:
                    mypillar = file_tree.ext_pillar(MINION_ID, None, ".")
                    self.assertEqual({}, mypillar)

                    for message in handler.messages:
                        if (
                            message.startswith("ERROR:")
                            and "pillarenv is not set" in message
                        ):
                            break
                    else:
                        raise AssertionError("Did not find error message")
