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

import os
import shutil
import tempfile

from cloudinit import util
from cloudinit.config.cc_rsyslog import (
    DEF_DIR,
    DEF_FILENAME,
    DEF_RELOAD,
    apply_rsyslog_changes,
    load_config,
    parse_remotes_line,
    remotes_to_rsyslog_cfg,
)
from tests.unittests import helpers as t_help


class TestLoadConfig(t_help.TestCase):
    def setUp(self):
        super(TestLoadConfig, self).setUp()
        self.basecfg = {
            "config_filename": DEF_FILENAME,
            "config_dir": DEF_DIR,
            "service_reload_command": DEF_RELOAD,
            "configs": [],
            "remotes": {},
        }

    def test_legacy_full(self):
        found = load_config(
            {
                "rsyslog": ["*.* @192.168.1.1"],
                "rsyslog_dir": "mydir",
                "rsyslog_filename": "myfilename",
            }
        )
        self.basecfg.update(
            {
                "configs": ["*.* @192.168.1.1"],
                "config_dir": "mydir",
                "config_filename": "myfilename",
                "service_reload_command": "auto",
            }
        )

        self.assertEqual(found, self.basecfg)

    def test_legacy_defaults(self):
        found = load_config({"rsyslog": ["*.* @192.168.1.1"]})
        self.basecfg.update({"configs": ["*.* @192.168.1.1"]})
        self.assertEqual(found, self.basecfg)

    def test_new_defaults(self):
        self.assertEqual(load_config({}), self.basecfg)

    def test_new_configs(self):
        cfgs = ["*.* myhost", "*.* my2host"]
        self.basecfg.update({"configs": cfgs})
        self.assertEqual(
            load_config({"rsyslog": {"configs": cfgs}}), self.basecfg
        )


class TestApplyChanges(t_help.TestCase):
    def setUp(self):
        self.tmp = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, self.tmp)

    def test_simple(self):
        cfgline = "*.* foohost"
        changed = apply_rsyslog_changes(
            configs=[cfgline], def_fname="foo.cfg", cfg_dir=self.tmp
        )

        fname = os.path.join(self.tmp, "foo.cfg")
        self.assertEqual([fname], changed)
        self.assertEqual(util.load_file(fname), cfgline + "\n")

    def test_multiple_files(self):
        configs = [
            "*.* foohost",
            {"content": "abc", "filename": "my.cfg"},
            {
                "content": "filefoo-content",
                "filename": os.path.join(self.tmp, "mydir/mycfg"),
            },
        ]

        changed = apply_rsyslog_changes(
            configs=configs, def_fname="default.cfg", cfg_dir=self.tmp
        )

        expected = [
            (os.path.join(self.tmp, "default.cfg"), "*.* foohost\n"),
            (os.path.join(self.tmp, "my.cfg"), "abc\n"),
            (os.path.join(self.tmp, "mydir/mycfg"), "filefoo-content\n"),
        ]
        self.assertEqual([f[0] for f in expected], changed)
        actual = []
        for fname, _content in expected:
            util.load_file(fname)
            actual.append(
                (
                    fname,
                    util.load_file(fname),
                )
            )
        self.assertEqual(expected, actual)

    def test_repeat_def(self):
        configs = ["*.* foohost", "*.warn otherhost"]

        changed = apply_rsyslog_changes(
            configs=configs, def_fname="default.cfg", cfg_dir=self.tmp
        )

        fname = os.path.join(self.tmp, "default.cfg")
        self.assertEqual([fname], changed)

        expected_content = "\n".join([c for c in configs]) + "\n"
        found_content = util.load_file(fname)
        self.assertEqual(expected_content, found_content)

    def test_multiline_content(self):
        configs = ["line1", "line2\nline3\n"]

        apply_rsyslog_changes(
            configs=configs, def_fname="default.cfg", cfg_dir=self.tmp
        )

        fname = os.path.join(self.tmp, "default.cfg")
        expected_content = "\n".join([c for c in configs])
        found_content = util.load_file(fname)
        self.assertEqual(expected_content, found_content)


class TestParseRemotesLine(t_help.TestCase):
    def test_valid_port(self):
        r = parse_remotes_line("foo:9")
        self.assertEqual(9, r.port)

    def test_invalid_port(self):
        with self.assertRaises(ValueError):
            parse_remotes_line("*.* foo:abc")

    def test_valid_ipv6(self):
        r = parse_remotes_line("*.* [::1]")
        self.assertEqual("*.* @[::1]", str(r))

    def test_valid_ipv6_with_port(self):
        r = parse_remotes_line("*.* [::1]:100")
        self.assertEqual(r.port, 100)
        self.assertEqual(r.addr, "::1")
        self.assertEqual("*.* @[::1]:100", str(r))

    def test_invalid_multiple_colon(self):
        with self.assertRaises(ValueError):
            parse_remotes_line("*.* ::1:100")

    def test_name_in_string(self):
        r = parse_remotes_line("syslog.host", name="foobar")
        self.assertEqual("*.* @syslog.host # foobar", str(r))


class TestRemotesToSyslog(t_help.TestCase):
    def test_simple(self):
        # str rendered line must appear in remotes_to_ryslog_cfg return
        mycfg = "*.* myhost"
        myline = str(parse_remotes_line(mycfg, name="myname"))
        r = remotes_to_rsyslog_cfg({"myname": mycfg})
        lines = r.splitlines()
        self.assertEqual(1, len(lines))
        self.assertTrue(myline in r.splitlines())

    def test_header_footer(self):
        header = "#foo head"
        footer = "#foo foot"
        r = remotes_to_rsyslog_cfg(
            {"myname": "*.* myhost"}, header=header, footer=footer
        )
        lines = r.splitlines()
        self.assertTrue(header, lines[0])
        self.assertTrue(footer, lines[-1])

    def test_with_empty_or_null(self):
        mycfg = "*.* myhost"
        myline = str(parse_remotes_line(mycfg, name="myname"))
        r = remotes_to_rsyslog_cfg(
            {"myname": mycfg, "removed": None, "removed2": ""}
        )
        lines = r.splitlines()
        self.assertEqual(1, len(lines))
        self.assertTrue(myline in r.splitlines())


# vi: ts=4 expandtab
