def get_certnames(config: configuration.NamespaceConfig, verb: str, allow_multiple: bool = False, custom_prompt: Optional[str] = None) -> List[str]: """Get certname from flag, interactively, or error out.""" certname = config.certname if certname: certnames = [certname] else: filenames = storage.renewal_conf_files(config) choices = [storage.lineagename_for_filename(name) for name in filenames] if not choices: raise errors.Error("No existing certificates found.") if allow_multiple: if not custom_prompt: prompt = "Which certificate(s) would you like to {0}?".format(verb) else: prompt = custom_prompt code, certnames = display_util.checklist( prompt, choices, cli_flag="--cert-name", force_interactive=True) if code != display_util.OK: raise errors.Error("User ended interaction.") else: if not custom_prompt: prompt = "Which certificate would you like to {0}?".format(verb) else: prompt = custom_prompt code, index = display_util.menu( prompt, choices, cli_flag="--cert-name", force_interactive=True) if code != display_util.OK or index not in range(0, len(choices)): raise errors.Error("User ended interaction.") certnames = [choices[index]] return certnames
def handle_renewal_request(config): """Examine each lineage; renew if due and report results""" # This is trivially False if config.domains is empty if any(domain not in config.webroot_map for domain in config.domains): # If more plugins start using cli.add_domains, # we may want to only log a warning here raise errors.Error( "Currently, the renew verb is capable of either " "renewing all installed certificates that are due " "to be renewed or renewing a single certificate specified " "by its name. If you would like to renew specific " "certificates by their domains, use the certonly command " "instead. The renew verb may provide other options " "for selecting certificates to renew in the future.") if config.certname: conf_files = [ storage.renewal_file_for_certname(config, config.certname) ] else: conf_files = storage.renewal_conf_files(config) renew_successes = [] renew_failures = [] renew_skipped = [] parse_failures = [] # Noninteractive renewals include a random delay in order to spread # out the load on the certificate authority servers, even if many # users all pick the same time for renewals. This delay precedes # running any hooks, so that side effects of the hooks (such as # shutting down a web service) aren't prolonged unnecessarily. apply_random_sleep = not sys.stdin.isatty( ) and config.random_sleep_on_renew for renewal_file in conf_files: display_util.notification("Processing " + renewal_file, pause=False) lineage_config = copy.deepcopy(config) lineagename = storage.lineagename_for_filename(renewal_file) # Note that this modifies config (to add back the configuration # elements from within the renewal configuration file). try: renewal_candidate = _reconstitute(lineage_config, renewal_file) except Exception as e: # pylint: disable=broad-except logger.error( "Renewal configuration file %s (cert: %s) " "produced an unexpected error: %s. Skipping.", renewal_file, lineagename, e) logger.debug("Traceback was:\n%s", traceback.format_exc()) parse_failures.append(renewal_file) continue try: if renewal_candidate is None: parse_failures.append(renewal_file) else: # This call is done only for retro-compatibility purposes. # TODO: Remove this call once zope dependencies are removed from Certbot. zope.component.provideUtility(lineage_config, interfaces.IConfig) renewal_candidate.ensure_deployed() from certbot._internal import main plugins = plugins_disco.PluginsRegistry.find_all() if should_renew(lineage_config, renewal_candidate): # Apply random sleep upon first renewal if needed if apply_random_sleep: sleep_time = random.uniform(1, 60 * 8) logger.info( "Non-interactive renewal: random delay of %s seconds", sleep_time) time.sleep(sleep_time) # We will sleep only once this day, folks. apply_random_sleep = False # domains have been restored into lineage_config by reconstitute # but they're unnecessary anyway because renew_cert here # will just grab them from the certificate # we already know it's time to renew based on should_renew # and we have a lineage in renewal_candidate main.renew_cert(lineage_config, plugins, renewal_candidate) renew_successes.append(renewal_candidate.fullchain) else: expiry = crypto_util.notAfter( renewal_candidate.version( "cert", renewal_candidate.latest_common_version())) renew_skipped.append("%s expires on %s" % (renewal_candidate.fullchain, expiry.strftime("%Y-%m-%d"))) # Run updater interface methods updater.run_generic_updaters(lineage_config, renewal_candidate, plugins) except Exception as e: # pylint: disable=broad-except # obtain_cert (presumably) encountered an unanticipated problem. logger.error("Failed to renew certificate %s with error: %s", lineagename, e) logger.debug("Traceback was:\n%s", traceback.format_exc()) renew_failures.append(renewal_candidate.fullchain) # Describe all the results _renew_describe_results(config, renew_successes, renew_failures, renew_skipped, parse_failures) if renew_failures or parse_failures: raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format( len(renew_failures), len(parse_failures))) # Windows installer integration tests rely on handle_renewal_request behavior here. # If the text below changes, these tests will need to be updated accordingly. logger.debug("no renewal failures")