# This file is part of cloud-init. See LICENSE file for license information. import configparser import glob import logging import os from cloudinit import util from cloudinit.config import cc_zypper_add_repo from tests.unittests import helpers from tests.unittests.helpers import mock LOG = logging.getLogger(__name__) class TestConfig(helpers.FilesystemMockingTestCase): def setUp(self): super(TestConfig, self).setUp() self.tmp = self.tmp_dir() self.zypp_conf = "etc/zypp/zypp.conf" def test_bad_repo_config(self): """Config has no baseurl, no file should be written""" cfg = { "repos": [ {"id": "foo", "name": "suse-test", "enabled": "1"}, ] } self.patchUtils(self.tmp) cc_zypper_add_repo._write_repos(cfg["repos"], "/etc/zypp/repos.d") self.assertRaises( IOError, util.load_file, "/etc/zypp/repos.d/foo.repo" ) def test_write_repos(self): """Verify valid repos get written""" cfg = self._get_base_config_repos() root_d = self.tmp_dir() cc_zypper_add_repo._write_repos(cfg["zypper"]["repos"], root_d) repos = glob.glob("%s/*.repo" % root_d) expected_repos = ["testing-foo.repo", "testing-bar.repo"] if len(repos) != 2: assert 'Number of repos written is "%d" expected 2' % len(repos) for repo in repos: repo_name = os.path.basename(repo) if repo_name not in expected_repos: assert 'Found repo with name "%s"; unexpected' % repo_name # Validation that the content gets properly written is in another test def test_write_repo(self): """Verify the content of a repo file""" cfg = { "repos": [ { "baseurl": "http://foo", "name": "test-foo", "id": "testing-foo", }, ] } root_d = self.tmp_dir() cc_zypper_add_repo._write_repos(cfg["repos"], root_d) contents = util.load_file("%s/testing-foo.repo" % root_d) parser = configparser.ConfigParser() parser.read_string(contents) expected = { "testing-foo": { "name": "test-foo", "baseurl": "http://foo", "enabled": "1", "autorefresh": "1", } } for section in expected: self.assertTrue( parser.has_section(section), "Contains section {0}".format(section), ) for k, v in expected[section].items(): self.assertEqual(parser.get(section, k), v) def test_config_write(self): """Write valid configuration data""" cfg = {"config": {"download.deltarpm": "False", "reposdir": "foo"}} root_d = self.tmp_dir() helpers.populate_dir(root_d, {self.zypp_conf: "# Zypp config\n"}) self.reRoot(root_d) cc_zypper_add_repo._write_zypp_config(cfg["config"]) cfg_out = os.path.join(root_d, self.zypp_conf) contents = util.load_file(cfg_out) expected = [ "# Zypp config", "# Added via cloud.cfg", "download.deltarpm=False", "reposdir=foo", ] for item in contents.split("\n"): if item not in expected: self.assertIsNone(item) @mock.patch("cloudinit.log.logging") def test_config_write_skip_configdir(self, mock_logging): """Write configuration but skip writing 'configdir' setting""" cfg = { "config": { "download.deltarpm": "False", "reposdir": "foo", "configdir": "bar", } } root_d = self.tmp_dir() helpers.populate_dir(root_d, {self.zypp_conf: "# Zypp config\n"}) self.reRoot(root_d) cc_zypper_add_repo._write_zypp_config(cfg["config"]) cfg_out = os.path.join(root_d, self.zypp_conf) contents = util.load_file(cfg_out) expected = [ "# Zypp config", "# Added via cloud.cfg", "download.deltarpm=False", "reposdir=foo", ] for item in contents.split("\n"): if item not in expected: self.assertIsNone(item) # Not finding teh right path for mocking :( # assert mock_logging.warning.called def test_empty_config_section_no_new_data(self): """When the config section is empty no new data should be written to zypp.conf""" cfg = self._get_base_config_repos() cfg["zypper"]["config"] = None root_d = self.tmp_dir() helpers.populate_dir(root_d, {self.zypp_conf: "# No data"}) self.reRoot(root_d) cc_zypper_add_repo._write_zypp_config(cfg.get("config", {})) cfg_out = os.path.join(root_d, self.zypp_conf) contents = util.load_file(cfg_out) self.assertEqual(contents, "# No data") def test_empty_config_value_no_new_data(self): """When the config section is not empty but there are no values no new data should be written to zypp.conf""" cfg = self._get_base_config_repos() cfg["zypper"]["config"] = {"download.deltarpm": None} root_d = self.tmp_dir() helpers.populate_dir(root_d, {self.zypp_conf: "# No data"}) self.reRoot(root_d) cc_zypper_add_repo._write_zypp_config(cfg.get("config", {})) cfg_out = os.path.join(root_d, self.zypp_conf) contents = util.load_file(cfg_out) self.assertEqual(contents, "# No data") def test_handler_full_setup(self): """Test that the handler ends up calling the renderers""" cfg = self._get_base_config_repos() cfg["zypper"]["config"] = { "download.deltarpm": "False", } root_d = self.tmp_dir() os.makedirs("%s/etc/zypp/repos.d" % root_d) helpers.populate_dir(root_d, {self.zypp_conf: "# Zypp config\n"}) self.reRoot(root_d) cc_zypper_add_repo.handle("zypper_add_repo", cfg, None, LOG, []) cfg_out = os.path.join(root_d, self.zypp_conf) contents = util.load_file(cfg_out) expected = [ "# Zypp config", "# Added via cloud.cfg", "download.deltarpm=False", ] for item in contents.split("\n"): if item not in expected: self.assertIsNone(item) repos = glob.glob("%s/etc/zypp/repos.d/*.repo" % root_d) expected_repos = ["testing-foo.repo", "testing-bar.repo"] if len(repos) != 2: assert 'Number of repos written is "%d" expected 2' % len(repos) for repo in repos: repo_name = os.path.basename(repo) if repo_name not in expected_repos: assert 'Found repo with name "%s"; unexpected' % repo_name def test_no_config_section_no_new_data(self): """When there is no config section no new data should be written to zypp.conf""" cfg = self._get_base_config_repos() root_d = self.tmp_dir() helpers.populate_dir(root_d, {self.zypp_conf: "# No data"}) self.reRoot(root_d) cc_zypper_add_repo._write_zypp_config(cfg.get("config", {})) cfg_out = os.path.join(root_d, self.zypp_conf) contents = util.load_file(cfg_out) self.assertEqual(contents, "# No data") def test_no_repo_data(self): """When there is no repo data nothing should happen""" root_d = self.tmp_dir() self.reRoot(root_d) cc_zypper_add_repo._write_repos(None, root_d) content = glob.glob("%s/*" % root_d) self.assertEqual(len(content), 0) def _get_base_config_repos(self): """Basic valid repo configuration""" cfg = { "zypper": { "repos": [ { "baseurl": "http://foo", "name": "test-foo", "id": "testing-foo", }, { "baseurl": "http://bar", "name": "test-bar", "id": "testing-bar", }, ] } } return cfg