Repository: certbot/certbot

Issue:
Command line arguments for plugins are broken
It seems the command line arguments for plugins created in `add_parser_arguments(cls, add):` cant be extracted with `self.conf("argument_name")`. I suspect something with the plugin namespace prefix is going wrong.

My code is here https://github.com/e00E/lets-encrypt-icecast/blob/master/icecast.py .
In line 129 I add an argument via `add("configuration_file")`
and I try to use it in line 150 like so

```
self.config_file = self.conf("configuration_file")
if self.config_file is not None and not os.path.isfile(self.config_file):
    raise PluginError('User supplied icecast configuration file does not exist.')
```

When running `letsencrypt install` it crashes with the last log message

```
Traceback (most recent call last):
  File "./letsencrypt/venv/bin/letsencrypt", line 9, in <module>
    load_entry_point('letsencrypt==0.1', 'console_scripts', 'letsencrypt')()
  File "/home/e/icecast/letsencrypt/letsencrypt/cli.py", line 778, in main
    return args.func(args, config, plugins)
  File "/home/e/icecast/letsencrypt/letsencrypt/cli.py", line 232, in install
    installer = display_ops.pick_installer(config, args.installer, plugins)
  File "/home/e/icecast/letsencrypt/letsencrypt/display/ops.py", line 105, in pick_installer
    config, default, plugins, question, (interfaces.IInstaller,))
  File "/home/e/icecast/letsencrypt/letsencrypt/display/ops.py", line 72, in pick_plugin
    verified.prepare()
  File "/home/e/icecast/letsencrypt/letsencrypt/plugins/disco.py", line 197, in prepare
    return [plugin_ep.prepare() for plugin_ep in self._plugins.itervalues()]
  File "/home/e/icecast/letsencrypt/letsencrypt/plugins/disco.py", line 98, in prepare
    self._initialized.prepare()
  File "/home/e/icecast/letsencrypt/icecast.py", line 151, in prepare
    self.config_file = self.conf("configuration_file")
  File "/home/e/icecast/letsencrypt/letsencrypt/plugins/common.py", line 60, in conf
    return getattr(self.config, self.dest(var))
  File "/home/e/icecast/letsencrypt/letsencrypt/configuration.py", line 39, in __getattr__
    return getattr(self.namespace, name)
AttributeError: 'Namespace' object has no attribute 'letsencrypt-icecast:icecast_installer_configuration_file'
```

I think if an argument is not specified on the command line, the argument should default to None and not throw an AttributeError. But even if I manually have default=None or set default to anything I still get this error. Curiously the attribute error is about `letsencrypt-icecast:icecast_installer_configuration_file` while when running `letsencrypt --help all` the corresponding command line argument is listed as `--letsencrypt-icecast:icecast_installer-configuration_file` (note the the dash instead of underscore near the end) which leads me to believe the conversion of the argument name in the plugin to the global name is going wrong.



Discussion:
Hmm... I am looking into this now.  It has been awhile since I have looked at this code.

My guess is that the problem is here.
https://github.com/letsencrypt/letsencrypt/blob/74b677248d082c35489ed96a806e50bd648627e1/letsencrypt/plugins/common.py#L56

Problem is in https://github.com/letsencrypt/letsencrypt/blob/74b677248d082c35489ed96a806e50bd648627e1/letsencrypt/plugins/common.py#L23. It should read: `return name.replace("-", "_") + "_"`.


Resolving patch:
diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py
--- a/letsencrypt/plugins/common.py
+++ b/letsencrypt/plugins/common.py
@@ -20,7 +20,7 @@ def option_namespace(name):
 
 def dest_namespace(name):
     """ArgumentParser dest namespace (prefix of all destinations)."""
-    return name + "_"
+    return name.replace("-", "_") + "_"
 
 private_ips_regex = re.compile(  # pylint: disable=invalid-name
     r"(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|"


Test patch:
diff --git a/letsencrypt/plugins/common_test.py b/letsencrypt/plugins/common_test.py
--- a/letsencrypt/plugins/common_test.py
+++ b/letsencrypt/plugins/common_test.py
@@ -23,6 +23,10 @@ def test_dest_namespace(self):
         from letsencrypt.plugins.common import dest_namespace
         self.assertEqual("foo_", dest_namespace("foo"))
 
+    def test_dest_namespace_with_dashes(self):
+        from letsencrypt.plugins.common import dest_namespace
+        self.assertEqual("foo_bar_", dest_namespace("foo-bar"))
+
 
 class PluginTest(unittest.TestCase):
     """Test for letsencrypt.plugins.common.Plugin."""