Merge pull request #1479 from joohoi/domains_flag

Possibility to use comma separated list of domains in config files and cli
This commit is contained in:
Peter Eckersley 2015-11-13 01:22:35 -08:00
commit 4cfd22e31f
4 changed files with 60 additions and 14 deletions

View file

@ -18,7 +18,7 @@ letsencrypt_test_nginx () {
"$@"
}
letsencrypt_test_nginx --domain nginx.wtf run
letsencrypt_test_nginx --domains nginx.wtf run
echo | openssl s_client -connect localhost:5001 \
| openssl x509 -out $root/nginx.pem
diff -q $root/nginx.pem $root/conf/live/nginx.wtf/cert.pem

View file

@ -106,7 +106,7 @@ def _find_domains(args, installer):
domains = args.domains
if not domains:
raise errors.Error("Please specify --domain, or --installer that "
raise errors.Error("Please specify --domains, or --installer that "
"will help in domain names autodiscovery")
return domains
@ -465,9 +465,9 @@ def obtaincert(args, config, plugins):
"""Authenticate & obtain cert, but do not install it."""
if args.domains is not None and args.csr is not None:
# TODO: --csr could have a priority, when --domain is
# TODO: --csr could have a priority, when --domains is
# supplied, check if CSR matches given domains?
return "--domain and --csr are mutually exclusive"
return "--domains and --csr are mutually exclusive"
try:
# installers are used in auth mode to determine domain names
@ -672,8 +672,32 @@ class HelpfulArgumentParser(object):
parsed_args = self.parser.parse_args(self.args)
parsed_args.func = self.VERBS[self.verb]
parsed_args.domains = self._parse_domains(parsed_args.domains)
return parsed_args
def _parse_domains(self, domains):
"""Helper function for parse_args() that parses domains from a
(possibly) comma separated list and returns list of unique domains.
:param domains: List of domain flags
:type domains: `list` of `string`
:returns: List of unique domains
:rtype: `list` of `string`
"""
uniqd = None
if domains:
dlist = []
for domain in domains:
dlist.extend([d.strip() for d in domain.split(",")])
# Make sure we don't have duplicates
uniqd = [d for i, d in enumerate(dlist) if d not in dlist[:i]]
return uniqd
def determine_verb(self):
"""Determines the verb/subcommand provided by the user.
@ -811,14 +835,15 @@ def prepare_and_parse_args(plugins, args):
None, "-t", "--text", dest="text_mode", action="store_true",
help="Use the text output instead of the curses UI.")
helpful.add(None, "-m", "--email", help=config_help("email"))
# positional arg shadows --domain, instead of appending, and
# --domain is useful, because it can be stored in config
# positional arg shadows --domains, instead of appending, and
# --domains is useful, because it can be stored in config
#for subparser in parser_run, parser_auth, parser_install:
# subparser.add_argument("domains", nargs="*", metavar="domain")
helpful.add(None, "-d", "--domain", dest="domains",
helpful.add(None, "-d", "--domains", dest="domains",
metavar="DOMAIN", action="append",
help="Domain names to apply. Use multiple -d flags if you want "
"to specify multiple domains")
help="Domain names to apply. For multiple domains you can use "
"multiple -d flags or enter a comma separated list of domains"
"as a parameter.")
helpful.add(
None, "--duplicate", dest="duplicate", action="store_true",
help="Allow getting a certificate that duplicates an existing one")

View file

@ -135,7 +135,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
@mock.patch('letsencrypt.cli.display_ops')
def test_installer_selection(self, mock_display_ops):
self._call(['install', '--domain', 'foo.bar', '--cert-path', 'cert',
self._call(['install', '--domains', 'foo.bar', '--cert-path', 'cert',
'--key-path', 'key', '--chain-path', 'chain'])
self.assertEqual(mock_display_ops.pick_installer.call_count, 1)
@ -249,7 +249,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
def test_certonly_bad_args(self):
ret, _, _, _ = self._call(['-d', 'foo.bar', 'certonly', '--csr', CSR])
self.assertEqual(ret, '--domain and --csr are mutually exclusive')
self.assertEqual(ret, '--domains and --csr are mutually exclusive')
ret, _, _, _ = self._call(['-a', 'bad_auth', 'certonly'])
self.assertEqual(ret, 'The requested bad_auth plugin does not appear to be installed')
@ -272,6 +272,27 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
self._call,
['-d', '*.wildcard.tld'])
def test_parse_domains(self):
from letsencrypt import cli
plugins = disco.PluginsRegistry.find_all()
short_args = ['-d', 'example.com']
namespace = cli.prepare_and_parse_args(plugins, short_args)
self.assertEqual(namespace.domains, ['example.com'])
short_args = ['-d', 'example.com,another.net,third.org,example.com']
namespace = cli.prepare_and_parse_args(plugins, short_args)
self.assertEqual(namespace.domains, ['example.com', 'another.net',
'third.org'])
long_args = ['--domains', 'example.com']
namespace = cli.prepare_and_parse_args(plugins, long_args)
self.assertEqual(namespace.domains, ['example.com'])
long_args = ['--domains', 'example.com,another.net,example.com']
namespace = cli.prepare_and_parse_args(plugins, long_args)
self.assertEqual(namespace.domains, ['example.com', 'another.net'])
@mock.patch('letsencrypt.crypto_util.notAfter')
@mock.patch('letsencrypt.cli.zope.component.getUtility')
def test_certonly_new_request_success(self, mock_get_utility, mock_notAfter):

View file

@ -27,8 +27,8 @@ common() {
"$@"
}
common --domain le1.wtf --standalone-supported-challenges tls-sni-01 auth
common --domain le2.wtf --standalone-supported-challenges http-01 run
common --domains le1.wtf --standalone-supported-challenges tls-sni-01 auth
common --domains le2.wtf --standalone-supported-challenges http-01 run
common -a manual -d le.wtf auth
export CSR_PATH="${root}/csr.der" KEY_PATH="${root}/key.pem" \
@ -40,7 +40,7 @@ common auth --csr "$CSR_PATH" \
openssl x509 -in "${root}/csr/0000_cert.pem" -text
openssl x509 -in "${root}/csr/0000_chain.pem" -text
common --domain le3.wtf install \
common --domains le3.wtf install \
--cert-path "${root}/csr/cert.pem" \
--key-path "${root}/csr/key.pem"