apache_test.py
  1  """Tests for letshelp.letshelp_letsencrypt_apache.py"""
  2  import argparse
  3  import functools
  4  import os
  5  import pkg_resources
  6  import subprocess
  7  import tarfile
  8  import tempfile
  9  import unittest
 10  
 11  import mock
 12  
 13  import letshelp_letsencrypt.apache as letshelp_le_apache
 14  
 15  
 16  _PARTIAL_CONF_PATH = os.path.join("mods-available", "ssl.load")
 17  _PARTIAL_LINK_PATH = os.path.join("mods-enabled", "ssl.load")
 18  _CONFIG_FILE = pkg_resources.resource_filename(
 19      __name__, os.path.join("testdata", _PARTIAL_CONF_PATH))
 20  _PASSWD_FILE = pkg_resources.resource_filename(
 21      __name__, os.path.join("testdata", "uncommonly_named_p4sswd"))
 22  _KEY_FILE = pkg_resources.resource_filename(
 23      __name__, os.path.join("testdata", "uncommonly_named_k3y"))
 24  _SECRET_FILE = pkg_resources.resource_filename(
 25      __name__, os.path.join("testdata", "super_secret_file.txt"))
 26  
 27  
 28  _MODULE_NAME = "letshelp_letsencrypt.apache"
 29  
 30  
 31  _COMPILE_SETTINGS = """Server version: Apache/2.4.10 (Debian)
 32  Server built:   Mar 15 2015 09:51:43
 33  Server's Module Magic Number: 20120211:37
 34  Server loaded:  APR 1.5.1, APR-UTIL 1.5.4
 35  Compiled using: APR 1.5.1, APR-UTIL 1.5.4
 36  Architecture:   64-bit
 37  Server MPM:     event
 38    threaded:     yes (fixed thread count)
 39      forked:     yes (variable process count)
 40  Server compiled with....
 41   -D APR_HAS_SENDFILE
 42   -D APR_HAS_MMAP
 43   -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 44   -D APR_USE_SYSVSEM_SERIALIZE
 45   -D APR_USE_PTHREAD_SERIALIZE
 46   -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 47   -D APR_HAS_OTHER_CHILD
 48   -D AP_HAVE_RELIABLE_PIPED_LOGS
 49   -D DYNAMIC_MODULE_LIMIT=256
 50   -D HTTPD_ROOT="/etc/apache2"
 51   -D SUEXEC_BIN="/usr/lib/apache2/suexec"
 52   -D DEFAULT_PIDLOG="/var/run/apache2.pid"
 53   -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 54   -D DEFAULT_ERRORLOG="logs/error_log"
 55   -D AP_TYPES_CONFIG_FILE="mime.types"
 56   -D SERVER_CONFIG_FILE="apache2.conf"
 57  
 58  """
 59  
 60  
 61  class LetsHelpApacheTest(unittest.TestCase):
 62      @mock.patch(_MODULE_NAME + ".copy_config")
 63      def test_make_and_verify_selection(self, mock_copy_config):
 64          mock_copy_config.return_value = (["apache2.conf"], ["apache2"])
 65  
 66          with mock.patch("__builtin__.raw_input") as mock_input:
 67              with mock.patch(_MODULE_NAME + ".sys.stdout"):
 68                  mock_input.side_effect = ["Yes", "No"]
 69                  letshelp_le_apache.make_and_verify_selection("root", "temp")
 70                  self.assertRaises(
 71                      SystemExit, letshelp_le_apache.make_and_verify_selection,
 72                      "server_root", "temp_dir")
 73  
 74      def test_copy_config(self):
 75          tempdir = tempfile.mkdtemp()
 76          server_root = pkg_resources.resource_filename(__name__, "testdata")
 77          letshelp_le_apache.copy_config(server_root, tempdir)
 78  
 79          temp_testdata = os.path.join(tempdir, "testdata")
 80          self.assertFalse(os.path.exists(os.path.join(
 81              temp_testdata, os.path.basename(_PASSWD_FILE))))
 82          self.assertFalse(os.path.exists(os.path.join(
 83              temp_testdata, os.path.basename(_KEY_FILE))))
 84          self.assertFalse(os.path.exists(os.path.join(
 85              temp_testdata, os.path.basename(_SECRET_FILE))))
 86          self.assertTrue(os.path.exists(os.path.join(
 87              temp_testdata, _PARTIAL_CONF_PATH)))
 88          self.assertTrue(os.path.exists(os.path.join(
 89              temp_testdata, _PARTIAL_LINK_PATH)))
 90  
 91      def test_copy_file_without_comments(self):
 92          dest = tempfile.mkstemp()[1]
 93          letshelp_le_apache.copy_file_without_comments(_PASSWD_FILE, dest)
 94  
 95          with open(_PASSWD_FILE) as original:
 96              with open(dest) as copy:
 97                  for original_line, copied_line in zip(original, copy):
 98                      self.assertEqual(original_line, copied_line)
 99  
100      @mock.patch(_MODULE_NAME + ".subprocess.Popen")
101      def test_safe_config_file(self, mock_popen):
102          mock_popen().communicate.return_value = ("PEM RSA private key", None)
103          self.assertFalse(letshelp_le_apache.safe_config_file("filename"))
104  
105          mock_popen().communicate.return_value = ("ASCII text", None)
106          self.assertFalse(letshelp_le_apache.safe_config_file(_PASSWD_FILE))
107          self.assertFalse(letshelp_le_apache.safe_config_file(_KEY_FILE))
108          self.assertFalse(letshelp_le_apache.safe_config_file(_SECRET_FILE))
109          self.assertTrue(letshelp_le_apache.safe_config_file(_CONFIG_FILE))
110  
111      @mock.patch(_MODULE_NAME + ".subprocess.Popen")
112      def test_tempdir(self, mock_popen):
113          mock_popen().communicate.side_effect = [
114              ("version", None), ("modules", None), ("vhosts", None)]
115          args = _get_args()
116  
117          tempdir = letshelp_le_apache.setup_tempdir(args)
118  
119          with open(os.path.join(tempdir, "config_file")) as config_fd:
120              self.assertEqual(config_fd.read(), args.config_file + "\n")
121  
122          with open(os.path.join(tempdir, "version")) as version_fd:
123              self.assertEqual(version_fd.read(), "version")
124  
125          with open(os.path.join(tempdir, "modules")) as modules_fd:
126              self.assertEqual(modules_fd.read(), "modules")
127  
128          with open(os.path.join(tempdir, "vhosts")) as vhosts_fd:
129              self.assertEqual(vhosts_fd.read(), "vhosts")
130  
131      @mock.patch(_MODULE_NAME + ".subprocess.check_call")
132      def test_verify_config(self, mock_check_call):
133          args = _get_args()
134          mock_check_call.side_effect = [
135              None, OSError, subprocess.CalledProcessError(1, "apachectl")]
136  
137          letshelp_le_apache.verify_config(args)
138          self.assertRaises(SystemExit, letshelp_le_apache.verify_config, args)
139          self.assertRaises(SystemExit, letshelp_le_apache.verify_config, args)
140  
141      @mock.patch(_MODULE_NAME + ".subprocess.Popen")
142      def test_locate_config(self, mock_popen):
143          mock_popen().communicate.side_effect = [
144              OSError, ("bad_output", None), (_COMPILE_SETTINGS, None)]
145  
146          self.assertRaises(
147              SystemExit, letshelp_le_apache.locate_config, "ctl")
148          self.assertRaises(
149              SystemExit, letshelp_le_apache.locate_config, "ctl")
150          server_root, config_file = letshelp_le_apache.locate_config("ctl")
151          self.assertEqual(server_root, "/etc/apache2")
152          self.assertEqual(config_file, "apache2.conf")
153  
154      @mock.patch(_MODULE_NAME + ".argparse")
155      def test_get_args(self, mock_argparse):
156          argv = ["-d", "/etc/apache2"]
157          mock_argparse.ArgumentParser.return_value = _create_mock_parser(argv)
158          self.assertRaises(SystemExit, letshelp_le_apache.get_args)
159  
160          server_root = "/etc/apache2"
161          config_file = server_root + "/apache2.conf"
162          argv = ["-d", server_root, "-f", config_file]
163          mock_argparse.ArgumentParser.return_value = _create_mock_parser(argv)
164          args = letshelp_le_apache.get_args()
165          self.assertEqual(args.apache_ctl, "apachectl")
166          self.assertEqual(args.server_root, server_root)
167          self.assertEqual(args.config_file, os.path.basename(config_file))
168  
169          server_root = "/etc/apache2"
170          config_file = "/etc/httpd/httpd.conf"
171          argv = ["-d", server_root, "-f", config_file]
172          mock_argparse.ArgumentParser.return_value = _create_mock_parser(argv)
173          self.assertRaises(SystemExit, letshelp_le_apache.get_args)
174  
175      def test_main_with_args(self):
176          with mock.patch(_MODULE_NAME + ".get_args"):
177              self._test_main_common()
178  
179      def test_main_without_args(self):
180          with mock.patch(_MODULE_NAME + ".get_args") as get_args:
181              args = _get_args()
182              server_root, config_file = args.server_root, args.config_file
183              args.server_root = args.config_file = None
184              get_args.return_value = args
185              with mock.patch(_MODULE_NAME + ".locate_config") as locate:
186                  locate.return_value = (server_root, config_file)
187                  self._test_main_common()
188  
189      def _test_main_common(self):
190          with mock.patch(_MODULE_NAME + ".verify_config"):
191              with mock.patch(_MODULE_NAME + ".setup_tempdir") as mock_setup:
192                  tempdir_path = tempfile.mkdtemp()
193                  mock_setup.return_value = tempdir_path
194                  with mock.patch(_MODULE_NAME + ".make_and_verify_selection"):
195                      testdir_basename = "test"
196                      os.mkdir(os.path.join(tempdir_path, testdir_basename))
197  
198                      letshelp_le_apache.main()
199  
200                      tar = tarfile.open(os.path.join(
201                          tempdir_path, "config.tar.gz"))
202  
203                      tempdir = tar.next()
204                      self.assertTrue(tempdir.isdir())
205                      self.assertEqual(tempdir.name, ".")
206  
207                      testdir = tar.next()
208                      self.assertTrue(testdir.isdir())
209                      self.assertEqual(os.path.basename(testdir.name),
210                                       testdir_basename)
211  
212                      self.assertEqual(tar.next(), None)
213  
214  
215  def _create_mock_parser(argv):
216      parser = argparse.ArgumentParser()
217      mock_parser = mock.MagicMock()
218      mock_parser.add_argument = parser.add_argument
219      mock_parser.parse_args = functools.partial(parser.parse_args, argv)
220  
221      return mock_parser
222  
223  
224  def _get_args():
225      args = argparse.Namespace()
226      args.apache_ctl = "apache_ctl"
227      args.config_file = "config_file"
228      args.server_root = "server_root"
229  
230      return args
231  
232  
233  if __name__ == "__main__":
234      unittest.main()  # pragma: no cover