def test_release_mirror(initialized_db): """ Mirrors that are SYNC_NOW, regardless of starting time. """ disable_existing_mirrors() mirror, repo = create_mirror_repo_robot(["updated", "created"], repo_name="first") # mysql rounds the milliseconds on update so force that to happen now query = RepoMirrorConfig.update( sync_start_date=mirror.sync_start_date).where( RepoMirrorConfig.id == mirror.id) query.execute() mirror = RepoMirrorConfig.get_by_id(mirror.id) original_sync_start_date = mirror.sync_start_date assert mirror.sync_retries_remaining == 3 mirror = release_mirror(mirror, RepoMirrorStatus.FAIL) assert mirror.sync_retries_remaining == 2 assert mirror.sync_start_date == original_sync_start_date mirror = release_mirror(mirror, RepoMirrorStatus.FAIL) assert mirror.sync_retries_remaining == 1 assert mirror.sync_start_date == original_sync_start_date mirror = release_mirror(mirror, RepoMirrorStatus.FAIL) assert mirror.sync_retries_remaining == 3 assert mirror.sync_start_date > original_sync_start_date
def perform_mirror(skopeo, mirror): """ Run mirror on all matching tags of remote repository. """ if os.getenv("DEBUGLOG", "false").lower() == "true": verbose_logs = True else: verbose_logs = False mirror = claim_mirror(mirror) if mirror == None: raise PreemptedException emit_log( mirror, "repo_mirror_sync_started", "start", "'%s' with tag pattern '%s'" % (mirror.external_reference, ",".join(mirror.root_rule.rule_value)), ) # Fetch the tags to mirror, being careful to handle exceptions. The 'Exception' is safety net only, allowing # easy communication by user through bug report. tags = [] try: tags = tags_to_mirror(skopeo, mirror) except RepoMirrorSkopeoException as e: emit_log( mirror, "repo_mirror_sync_failed", "end", "'%s' with tag pattern '%s': %s" % (mirror.external_reference, ",".join(mirror.root_rule.rule_value), str(e)), tags=", ".join(tags), stdout=e.stdout, stderr=e.stderr, ) release_mirror(mirror, RepoMirrorStatus.FAIL) return except Exception as e: emit_log( mirror, "repo_mirror_sync_failed", "end", "'%s' with tag pattern '%s': INTERNAL ERROR" % (mirror.external_reference, ",".join(mirror.root_rule.rule_value)), tags=", ".join(tags), stdout="Not applicable", stderr=traceback.format_exc(e), ) release_mirror(mirror, RepoMirrorStatus.FAIL) return if tags == []: emit_log( mirror, "repo_mirror_sync_success", "end", "'%s' with tag pattern '%s'" % (mirror.external_reference, ",".join(mirror.root_rule.rule_value)), tags="No tags matched", ) release_mirror(mirror, RepoMirrorStatus.SUCCESS) return # Sync tags now_ms = database.get_epoch_timestamp_ms() overall_status = RepoMirrorStatus.SUCCESS try: delete_obsolete_tags(mirror, tags) try: username = ( mirror.external_registry_username.decrypt() if mirror.external_registry_username else None ) password = ( mirror.external_registry_password.decrypt() if mirror.external_registry_password else None ) except DecryptionFailureException: logger.exception( "Failed to decrypt username or password for mirroring %s", mirror.repository ) raise dest_server = ( app.config.get("REPO_MIRROR_SERVER_HOSTNAME", None) or app.config["SERVER_HOSTNAME"] ) for tag in tags: src_image = "docker://%s:%s" % (mirror.external_reference, tag) dest_image = "docker://%s/%s/%s:%s" % ( dest_server, mirror.repository.namespace_user.username, mirror.repository.name, tag, ) with database.CloseForLongOperation(app.config): result = skopeo.copy( src_image, dest_image, src_tls_verify=mirror.external_registry_config.get("verify_tls", True), dest_tls_verify=app.config.get( "REPO_MIRROR_TLS_VERIFY", True ), # TODO: is this a config choice or something else? src_username=username, src_password=password, dest_username=mirror.internal_robot.username, dest_password=retrieve_robot_token(mirror.internal_robot), proxy=mirror.external_registry_config.get("proxy", {}), verbose_logs=verbose_logs, ) if not result.success: overall_status = RepoMirrorStatus.FAIL emit_log( mirror, "repo_mirror_sync_tag_failed", "finish", "Source '%s' failed to sync" % src_image, tag=tag, stdout=result.stdout, stderr=result.stderr, ) logger.info("Source '%s' failed to sync." % src_image) else: emit_log( mirror, "repo_mirror_sync_tag_success", "finish", "Source '%s' successful sync" % src_image, tag=tag, stdout=result.stdout, stderr=result.stderr, ) logger.info("Source '%s' successful sync." % src_image) mirror = claim_mirror(mirror) if mirror is None: emit_log( mirror, "repo_mirror_sync_failed", "lost", "'%s' with tag pattern '%s'" % (mirror.external_reference, ",".join(mirror.root_rule.rule_value)), ) except Exception as e: overall_status = RepoMirrorStatus.FAIL emit_log( mirror, "repo_mirror_sync_failed", "end", "'%s' with tag pattern '%s': INTERNAL ERROR" % (mirror.external_reference, ",".join(mirror.root_rule.rule_value)), tags=", ".join(tags), stdout="Not applicable", stderr=traceback.format_exc(e), ) release_mirror(mirror, overall_status) return finally: if overall_status == RepoMirrorStatus.FAIL: emit_log( mirror, "repo_mirror_sync_failed", "lost", "'%s' with tag pattern '%s'" % (mirror.external_reference, ",".join(mirror.root_rule.rule_value)), ) rollback(mirror, now_ms) else: emit_log( mirror, "repo_mirror_sync_success", "end", "'%s' with tag pattern '%s'" % (mirror.external_reference, ",".join(mirror.root_rule.rule_value)), tags=", ".join(tags), ) release_mirror(mirror, overall_status) return overall_status