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