Repository: certbot/certbot

Issue:
nginx runs without checking for binary presence
`apt-get remove nginx`, files in `/etc/nginx` left intact, plugin decides to continue:

```
~# letsencrypt --text --agree-dev-preview -vvvvv
2015-11-01 17:37:10,103:DEBUG:letsencrypt.cli:Root logging level set at -20
2015-11-01 17:37:10,105:INFO:letsencrypt.cli:Saving debug log to /var/log/letsencrypt/letsencrypt.log
2015-11-01 17:37:10,107:DEBUG:letsencrypt.cli:letsencrypt version: 0.1.0.dev0
2015-11-01 17:37:10,108:DEBUG:letsencrypt.cli:Arguments: ['--text', '--agree-dev-preview', '-vvvvv']
2015-11-01 17:37:10,109:DEBUG:letsencrypt.cli:Discovered plugins: PluginsRegistry(PluginEntryPoint#webroot,PluginEntryPoint#null,PluginEntryPoint#manual,PluginEntryPoint#nginx,PluginEntryPoint#standalone)
2015-11-01 17:37:10,111:DEBUG:letsencrypt.cli:Requested authenticator None and installer None
2015-11-01 17:37:10,141:DEBUG:letsencrypt_nginx.parser:Could not parse file: /etc/nginx/mime.types
2015-11-01 17:37:10,181:DEBUG:letsencrypt.display.ops:Single candidate plugin: * nginx
Description: Nginx Web Server - currently doesn't work
Interfaces: IAuthenticator, IInstaller, IPlugin
Entry point: nginx = letsencrypt_nginx.configurator:NginxConfigurator
Initialized: <letsencrypt_nginx.configurator.NginxConfigurator object at 0x7f6accc26f50>
Prep: True
2015-11-01 17:37:10,186:DEBUG:letsencrypt.cli:Selected authenticator <letsencrypt_nginx.configurator.NginxConfigurator object at 0x7f6accc26f50> and installer <letsencrypt_nginx.configurator.NginxConfigurator object at 0x7f6accc26f50>

Which names would you like to activate HTTPS for?
-------------------------------------------------------------------------------
1: _
-------------------------------------------------------------------------------
Select the appropriate numbers separated by commas and/or spaces (Enter 'c' to
cancel):
```

This does not affect apache plugin as far as I can tell.



Discussion:

Resolving patch:
diff --git a/letsencrypt-nginx/letsencrypt_nginx/configurator.py b/letsencrypt-nginx/letsencrypt_nginx/configurator.py
--- a/letsencrypt-nginx/letsencrypt_nginx/configurator.py
+++ b/letsencrypt-nginx/letsencrypt_nginx/configurator.py
@@ -107,6 +107,10 @@ def mod_ssl_conf(self):
     # This is called in determine_authenticator and determine_installer
     def prepare(self):
         """Prepare the authenticator/installer."""
+        # Verify Nginx is installed
+        if not le_util.exe_exists(self.conf('ctl')):
+            raise errors.NoInstallationError
+
         self.parser = parser.NginxParser(
             self.conf('server-root'), self.mod_ssl_conf)
 


Test patch:
diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py
--- a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py
+++ b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py
@@ -1,3 +1,4 @@
+# pylint: disable=too-many-public-methods
 """Test for letsencrypt_nginx.configurator."""
 import os
 import shutil
@@ -29,6 +30,12 @@ def tearDown(self):
         shutil.rmtree(self.config_dir)
         shutil.rmtree(self.work_dir)
 
+    @mock.patch("letsencrypt_nginx.configurator.le_util.exe_exists")
+    def test_prepare_no_install(self, mock_exe_exists):
+        mock_exe_exists.return_value = False
+        self.assertRaises(
+            errors.NoInstallationError, self.config.prepare)
+
     def test_prepare(self):
         self.assertEquals((1, 6, 2), self.config.version)
         self.assertEquals(5, len(self.config.parser.parsed))
diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/util.py b/letsencrypt-nginx/letsencrypt_nginx/tests/util.py
--- a/letsencrypt-nginx/letsencrypt_nginx/tests/util.py
+++ b/letsencrypt-nginx/letsencrypt_nginx/tests/util.py
@@ -49,21 +49,25 @@ def get_nginx_configurator(
 
     backups = os.path.join(work_dir, "backups")
 
-    config = configurator.NginxConfigurator(
-        config=mock.MagicMock(
-            nginx_server_root=config_path,
-            le_vhost_ext="-le-ssl.conf",
-            config_dir=config_dir,
-            work_dir=work_dir,
-            backup_dir=backups,
-            temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"),
-            in_progress_dir=os.path.join(backups, "IN_PROGRESS"),
-            server="https://acme-server.org:443/new",
-            tls_sni_01_port=5001,
-        ),
-        name="nginx",
-        version=version)
-    config.prepare()
+    with mock.patch("letsencrypt_nginx.configurator.le_util."
+            "exe_exists") as mock_exe_exists:
+        mock_exe_exists.return_value = True
+
+        config = configurator.NginxConfigurator(
+            config=mock.MagicMock(
+                nginx_server_root=config_path,
+                le_vhost_ext="-le-ssl.conf",
+                config_dir=config_dir,
+                work_dir=work_dir,
+                backup_dir=backups,
+                temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"),
+                in_progress_dir=os.path.join(backups, "IN_PROGRESS"),
+                server="https://acme-server.org:443/new",
+                tls_sni_01_port=5001,
+            ),
+            name="nginx",
+            version=version)
+        config.prepare()
 
     # Provide general config utility.
     nsconfig = configuration.NamespaceConfig(config.config)