parser_test.py
1 """Tests for letsencrypt_apache.parser.""" 2 import os 3 import shutil 4 import unittest 5 6 import augeas 7 import mock 8 9 from letsencrypt import errors 10 11 from letsencrypt_apache.tests import util 12 13 14 class BasicParserTest(util.ParserTest): 15 """Apache Parser Test.""" 16 17 def setUp(self): # pylint: disable=arguments-differ 18 super(BasicParserTest, self).setUp() 19 20 def tearDown(self): 21 shutil.rmtree(self.temp_dir) 22 shutil.rmtree(self.config_dir) 23 shutil.rmtree(self.work_dir) 24 25 def test_find_config_root_no_root(self): 26 # pylint: disable=protected-access 27 os.remove(self.parser.loc["root"]) 28 self.assertRaises( 29 errors.NoInstallationError, self.parser._find_config_root) 30 31 def test_parse_file(self): 32 """Test parse_file. 33 34 letsencrypt.conf is chosen as the test file as it will not be 35 included during the normal course of execution. 36 37 """ 38 file_path = os.path.join( 39 self.config_path, "sites-available", "letsencrypt.conf") 40 41 self.parser._parse_file(file_path) # pylint: disable=protected-access 42 43 # search for the httpd incl 44 matches = self.parser.aug.match( 45 "/augeas/load/Httpd/incl [. ='%s']" % file_path) 46 47 self.assertTrue(matches) 48 49 def test_find_dir(self): 50 test = self.parser.find_dir("Listen", "80") 51 # This will only look in enabled hosts 52 test2 = self.parser.find_dir("documentroot") 53 54 self.assertEqual(len(test), 1) 55 self.assertEqual(len(test2), 3) 56 57 def test_add_dir(self): 58 aug_default = "/files" + self.parser.loc["default"] 59 self.parser.add_dir(aug_default, "AddDirective", "test") 60 61 self.assertTrue( 62 self.parser.find_dir("AddDirective", "test", aug_default)) 63 64 self.parser.add_dir(aug_default, "AddList", ["1", "2", "3", "4"]) 65 matches = self.parser.find_dir("AddList", None, aug_default) 66 for i, match in enumerate(matches): 67 self.assertEqual(self.parser.aug.get(match), str(i + 1)) 68 69 def test_add_dir_to_ifmodssl(self): 70 """test add_dir_to_ifmodssl. 71 72 Path must be valid before attempting to add to augeas 73 74 """ 75 from letsencrypt_apache.parser import get_aug_path 76 # This makes sure that find_dir will work 77 self.parser.modules.add("mod_ssl.c") 78 79 self.parser.add_dir_to_ifmodssl( 80 get_aug_path(self.parser.loc["default"]), 81 "FakeDirective", ["123"]) 82 83 matches = self.parser.find_dir("FakeDirective", "123") 84 85 self.assertEqual(len(matches), 1) 86 self.assertTrue("IfModule" in matches[0]) 87 88 def test_add_dir_to_ifmodssl_multiple(self): 89 from letsencrypt_apache.parser import get_aug_path 90 # This makes sure that find_dir will work 91 self.parser.modules.add("mod_ssl.c") 92 93 self.parser.add_dir_to_ifmodssl( 94 get_aug_path(self.parser.loc["default"]), 95 "FakeDirective", ["123", "456", "789"]) 96 97 matches = self.parser.find_dir("FakeDirective") 98 99 self.assertEqual(len(matches), 3) 100 self.assertTrue("IfModule" in matches[0]) 101 102 def test_get_aug_path(self): 103 from letsencrypt_apache.parser import get_aug_path 104 self.assertEqual("/files/etc/apache", get_aug_path("/etc/apache")) 105 106 def test_set_locations(self): 107 with mock.patch("letsencrypt_apache.parser.os.path") as mock_path: 108 109 mock_path.isfile.side_effect = [True, False, False] 110 111 # pylint: disable=protected-access 112 results = self.parser._set_locations() 113 114 self.assertEqual(results["default"], results["listen"]) 115 self.assertEqual(results["default"], results["name"]) 116 117 def test_set_user_config_file(self): 118 # pylint: disable=protected-access 119 path = os.path.join(self.parser.root, "httpd.conf") 120 open(path, 'w').close() 121 self.parser.add_dir(self.parser.loc["default"], "Include", "httpd.conf") 122 123 self.assertEqual( 124 path, self.parser._set_user_config_file()) 125 126 @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") 127 def test_update_runtime_variables(self, mock_cfg): 128 mock_cfg.return_value = ( 129 'ServerRoot: "/etc/apache2"\n' 130 'Main DocumentRoot: "/var/www"\n' 131 'Main ErrorLog: "/var/log/apache2/error.log"\n' 132 'Mutex ssl-stapling: using_defaults\n' 133 'Mutex ssl-cache: using_defaults\n' 134 'Mutex default: dir="/var/lock/apache2" mechanism=fcntl\n' 135 'Mutex watchdog-callback: using_defaults\n' 136 'PidFile: "/var/run/apache2/apache2.pid"\n' 137 'Define: TEST\n' 138 'Define: DUMP_RUN_CFG\n' 139 'Define: U_MICH\n' 140 'Define: TLS=443\n' 141 'Define: example_path=Documents/path\n' 142 'User: name="www-data" id=33 not_used\n' 143 'Group: name="www-data" id=33 not_used\n' 144 ) 145 expected_vars = {"TEST": "", "U_MICH": "", "TLS": "443", 146 "example_path": "Documents/path"} 147 148 self.parser.update_runtime_variables("ctl") 149 self.assertEqual(self.parser.variables, expected_vars) 150 151 @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") 152 def test_update_runtime_vars_bad_output(self, mock_cfg): 153 mock_cfg.return_value = "Define: TLS=443=24" 154 self.assertRaises( 155 errors.PluginError, self.parser.update_runtime_variables, "ctl") 156 157 mock_cfg.return_value = "Define: DUMP_RUN_CFG\nDefine: TLS=443=24" 158 self.assertRaises( 159 errors.PluginError, self.parser.update_runtime_variables, "ctl") 160 161 @mock.patch("letsencrypt_apache.parser.subprocess.Popen") 162 def test_update_runtime_vars_bad_ctl(self, mock_popen): 163 mock_popen.side_effect = OSError 164 self.assertRaises( 165 errors.MisconfigurationError, 166 self.parser.update_runtime_variables, "ctl") 167 168 @mock.patch("letsencrypt_apache.parser.subprocess.Popen") 169 def test_update_runtime_vars_bad_exit(self, mock_popen): 170 mock_popen().communicate.return_value = ("", "") 171 mock_popen.returncode = -1 172 self.assertRaises( 173 errors.MisconfigurationError, 174 self.parser.update_runtime_variables, "ctl") 175 176 177 class ParserInitTest(util.ApacheTest): 178 def setUp(self): # pylint: disable=arguments-differ 179 super(ParserInitTest, self).setUp() 180 self.aug = augeas.Augeas( 181 flags=augeas.Augeas.NONE | augeas.Augeas.NO_MODL_AUTOLOAD) 182 183 def tearDown(self): 184 shutil.rmtree(self.temp_dir) 185 shutil.rmtree(self.config_dir) 186 shutil.rmtree(self.work_dir) 187 188 def test_root_normalized(self): 189 from letsencrypt_apache.parser import ApacheParser 190 191 with mock.patch("letsencrypt_apache.parser.ApacheParser." 192 "update_runtime_variables"): 193 path = os.path.join( 194 self.temp_dir, 195 "debian_apache_2_4/////two_vhost_80/../two_vhost_80/apache2") 196 parser = ApacheParser(self.aug, path, "dummy_ctl") 197 198 self.assertEqual(parser.root, self.config_path) 199 200 def test_root_absolute(self): 201 from letsencrypt_apache.parser import ApacheParser 202 with mock.patch("letsencrypt_apache.parser.ApacheParser." 203 "update_runtime_variables"): 204 parser = ApacheParser( 205 self.aug, os.path.relpath(self.config_path), "dummy_ctl") 206 207 self.assertEqual(parser.root, self.config_path) 208 209 def test_root_no_trailing_slash(self): 210 from letsencrypt_apache.parser import ApacheParser 211 with mock.patch("letsencrypt_apache.parser.ApacheParser." 212 "update_runtime_variables"): 213 parser = ApacheParser( 214 self.aug, self.config_path + os.path.sep, "dummy_ctl") 215 self.assertEqual(parser.root, self.config_path) 216 217 218 if __name__ == "__main__": 219 unittest.main() # pragma: no cover