""" This module controls defines celery tasks and their applicable schedules. The celery beat server and workers will start when invoked. When ran in development mode (LEMUR_CONFIG= 4: error_log["message"] = "Deleting pending certificate" send_pending_failure_notification( pending_cert, notify_owner=pending_cert.notify ) # Mark the pending cert as resolved pending_certificate_service.update( cert.get("pending_cert").id, resolved=True ) else: pending_certificate_service.increment_attempt(pending_cert) pending_certificate_service.update( cert.get("pending_cert").id, status=str(cert.get("last_error")) ) # Add failed pending cert task back to queue fetch_acme_cert.delay(id) current_app.logger.error(error_log) log_data["message"] = "Complete" log_data["new"] = new log_data["failed"] = failed log_data["wrong_issuer"] = wrong_issuer current_app.logger.debug(log_data) print( "[+] Certificates: New: {new} Failed: {failed} Not using ACME: {wrong_issuer}".format( new=new, failed=failed, wrong_issuer=wrong_issuer ) ) @celery.task() def fetch_all_pending_acme_certs(): """Instantiate celery workers to resolve all pending Acme certificates""" pending_certs = pending_certificate_service.get_unresolved_pending_certs() log_data = { "function": "{}.{}".format(__name__, sys._getframe().f_code.co_name), "message": "Starting job.", } current_app.logger.debug(log_data) # We only care about certs using the acme-issuer plugin for cert in pending_certs: cert_authority = get_authority(cert.authority_id) if cert_authority.plugin_name == "acme-issuer": if datetime.now(timezone.utc) - cert.last_updated > timedelta(minutes=5): log_data["message"] = "Triggering job for cert {}".format(cert.name) log_data["cert_name"] = cert.name log_data["cert_id"] = cert.id current_app.logger.debug(log_data) fetch_acme_cert.delay(cert.id) @celery.task() def remove_old_acme_certs(): """Prune old pending acme certificates from the database""" log_data = {"function": "{}.{}".format(__name__, sys._getframe().f_code.co_name)} pending_certs = pending_certificate_service.get_pending_certs("all") # Delete pending certs more than a week old for cert in pending_certs: if datetime.now(timezone.utc) - cert.last_updated > timedelta(days=7): log_data["pending_cert_id"] = cert.id log_data["pending_cert_name"] = cert.name log_data["message"] = "Deleting pending certificate" current_app.logger.debug(log_data) pending_certificate_service.delete(cert) @celery.task() def clean_all_sources(): """ This function will clean unused certificates from sources. This is a destructive operation and should only be ran periodically. This function triggers one celery task per source. """ sources = validate_sources("all") for source in sources: current_app.logger.debug( "Creating celery task to clean source {}".format(source.label) ) clean_source.delay(source.label) @celery.task() def clean_source(source): """ This celery task will clean the specified source. This is a destructive operation that will delete unused certificates from each source. :param source: :return: """ current_app.logger.debug("Cleaning source {}".format(source)) clean([source], True) @celery.task() def sync_all_sources(): """ This function will sync certificates from all sources. This function triggers one celery task per source. """ sources = validate_sources("all") for source in sources: current_app.logger.debug( "Creating celery task to sync source {}".format(source.label) ) sync_source.delay(source.label) @celery.task(soft_time_limit=3600) def sync_source(source): """ This celery task will sync the specified source. :param source: :return: """ function = "{}.{}".format(__name__, sys._getframe().f_code.co_name) task_id = None if celery.current_task: task_id = celery.current_task.request.id log_data = { "function": function, "message": "Syncing source", "source": source, "task_id": task_id, } current_app.logger.debug(log_data) if task_id and is_task_active(function, task_id, (source,)): log_data["message"] = "Skipping task: Task is already active" current_app.logger.debug(log_data) return try: sync([source]) except SoftTimeLimitExceeded: log_data["message"] = "Error syncing source: Time limit exceeded." current_app.logger.error(log_data) sentry.captureException() metrics.send( "sync_source_timeout", "counter", 1, metric_tags={"source": source} ) return log_data["message"] = "Done syncing source" current_app.logger.debug(log_data) @celery.task() def sync_source_destination(): """ This celery task will sync destination and source, to make sure all new destinations are also present as source. Some destinations do not qualify as sources, and hence should be excluded from being added as sources We identify qualified destinations based on the sync_as_source attributed of the plugin. The destination sync_as_source_name reveals the name of the suitable source-plugin. We rely on account numbers to avoid duplicates. """ current_app.logger.debug("Syncing AWS destinations and sources") for dst in destinations_service.get_all(): if add_aws_destination_to_sources(dst): current_app.logger.debug("Source: %s added", dst.label) current_app.logger.debug("Completed Syncing AWS destinations and sources")