def get_repository_images(namespace_name, repo_name): repository_ref = registry_model.lookup_repository(namespace_name, repo_name, kind_filter="image") permission = ReadRepositoryPermission(namespace_name, repo_name) if permission.can() or (repository_ref and repository_ref.is_public): # We can't rely on permissions to tell us if a repo exists anymore if repository_ref is None: image_pulls.labels("v1", "tag", 404).inc() abort(404, message="Unknown repository", issue="unknown-repo") logger.debug("Building repository image response") resp = make_response(json.dumps([]), 200) resp.mimetype = "application/json" track_and_log("pull_repo", repository_ref, analytics_name="pull_repo_100x", analytics_sample=0.01) image_pulls.labels("v1", "tag", 200).inc() return resp image_pulls.labels("v1", "tag", 403).inc() abort(403)
def get_image_ancestry(namespace, repository, image_id, headers): logger.debug("Checking repo permissions") permission = ReadRepositoryPermission(namespace, repository) repository_ref = registry_model.lookup_repository(namespace, repository, kind_filter="image") if not permission.can() and not (repository_ref is not None and repository_ref.is_public): abort(403) logger.debug("Looking up repo image") legacy_image = registry_model.get_legacy_image(repository_ref, image_id, include_parents=True) if legacy_image is None: abort(404, "Image %(image_id)s not found", issue="unknown-image", image_id=image_id) # NOTE: We can not use jsonify here because we are returning a list not an object. ancestor_ids = [legacy_image.docker_image_id ] + [a.docker_image_id for a in legacy_image.parents] response = make_response(json.dumps(ancestor_ids), 200) response.headers.extend(headers) return response
def get_image_json(namespace, repository, image_id, headers): logger.debug("Checking repo permissions") permission = ReadRepositoryPermission(namespace, repository) repository_ref = registry_model.lookup_repository(namespace, repository, kind_filter="image") if not permission.can() and not (repository_ref is not None and repository_ref.is_public): abort(403) logger.debug("Looking up repo image") legacy_image = registry_model.get_legacy_image(repository_ref, image_id, store, include_blob=True) if legacy_image is None: flask_abort(404) size = legacy_image.blob.compressed_size if size is not None: # Note: X-Docker-Size is optional and we *can* end up with a NULL image_size, # so handle this case rather than failing. headers["X-Docker-Size"] = str(size) response = make_response(legacy_image.v1_metadata_string, 200) response.headers.extend(headers) return response
def get_tag_torrent(namespace_name, repo_name, digest): repo = model.repository.get_repository(namespace_name, repo_name) repo_is_public = repo is not None and model.repository.is_repository_public( repo) permission = ReadRepositoryPermission(namespace_name, repo_name) if not permission.can() and not repo_is_public: abort(403) user = get_authenticated_user() if user is None and not repo_is_public: # We can not generate a private torrent cluster without a user uuid (e.g. token auth) abort(403) if repo is not None and repo.kind.name != "image": abort(405) repo_ref = registry_model.lookup_repository(namespace_name, repo_name) if repo_ref is None: abort(404) blob = registry_model.get_repo_blob_by_digest(repo_ref, digest, include_placements=True) if blob is None: abort(404) return _torrent_for_blob(blob, repo_is_public)
def head_image_layer(namespace, repository, image_id, headers): permission = ReadRepositoryPermission(namespace, repository) repository_ref = registry_model.lookup_repository(namespace, repository, kind_filter="image") logger.debug("Checking repo permissions") if permission.can() or (repository_ref is not None and repository_ref.is_public): if repository_ref is None: abort(404) logger.debug("Looking up placement locations") legacy_image = registry_model.get_legacy_image(repository_ref, image_id, include_blob=True) if legacy_image is None: logger.debug("Could not find any blob placement locations") abort(404, "Image %(image_id)s not found", issue="unknown-image", image_id=image_id) # Add the Accept-Ranges header if the storage engine supports resumable # downloads. extra_headers = {} if store.get_supports_resumable_downloads(legacy_image.blob.placements): logger.debug("Storage supports resumable downloads") extra_headers["Accept-Ranges"] = "bytes" resp = make_response("") resp.headers.extend(headers) resp.headers.extend(extra_headers) return resp abort(403)
def get_image_layer(namespace, repository, image_id, headers): permission = ReadRepositoryPermission(namespace, repository) repository_ref = registry_model.lookup_repository(namespace, repository, kind_filter="image") logger.debug("Checking repo permissions") if permission.can() or (repository_ref is not None and repository_ref.is_public): if repository_ref is None: abort(404) legacy_image = registry_model.get_legacy_image(repository_ref, image_id, include_blob=True) if legacy_image is None: abort(404, "Image %(image_id)s not found", issue="unknown-image", image_id=image_id) path = legacy_image.blob.storage_path image_pulled_bytes.labels("v1").inc(legacy_image.blob.compressed_size) try: logger.debug("Looking up the direct download URL for path: %s", path) direct_download_url = store.get_direct_download_url( legacy_image.blob.placements, path, get_request_ip() ) if direct_download_url: logger.debug("Returning direct download URL") resp = redirect(direct_download_url) return resp # Close the database handle here for this process before we send the long download. database.close_db_filter(None) logger.debug("Streaming layer data") return Response(store.stream_read(legacy_image.blob.placements, path), headers=headers) except (IOError, AttributeError): logger.exception("Image layer data not found") abort(404, "Image %(image_id)s not found", issue="unknown-image", image_id=image_id) abort(403)
def redirect_to_repository(namespace_name, repo_name, tag_name): # Always return 200 for ac-discovery, to ensure that rkt and other ACI-compliant clients can # find the metadata they need. Permissions will be checked in the registry API. if request.args.get("ac-discovery", 0) == 1: return index("") # Redirect to the repository page if the user can see the repository. is_public = model.repository.repository_is_public(namespace_name, repo_name) permission = ReadRepositoryPermission(namespace_name, repo_name) repo = model.repository.get_repository(namespace_name, repo_name) if repo and (permission.can() or is_public): repo_path = "/".join([namespace_name, repo_name]) if repo.kind.name == "application": return redirect(url_for("web.application", path=repo_path)) else: return redirect( url_for("web.repository", path=repo_path, tab="tags", tag=tag_name)) namespace_exists = bool(model.user.get_user_or_org(namespace_name)) namespace_permission = OrganizationMemberPermission(namespace_name).can() if get_authenticated_user() and get_authenticated_user( ).username == namespace_name: namespace_permission = True # Otherwise, we display an error for the user. Which error we display depends on permissions: # > If the namespace doesn't exist, 404. # > If the user is a member of the namespace: # - If the repository doesn't exist, 404 # - If the repository does exist (no access), 403 # > If the user is not a member of the namespace: 403 error_info = { "reason": "notfound", "for_repo": True, "namespace_exists": namespace_exists, "namespace": namespace_name, "repo_name": repo_name, } if not namespace_exists or (namespace_permission and repo is None): resp = index("", error_code=404, error_info=json.dumps(error_info)) resp.status_code = 404 return resp else: resp = index("", error_code=403, error_info=json.dumps(error_info)) resp.status_code = 403 return resp
def get_tags(namespace_name, repo_name): permission = ReadRepositoryPermission(namespace_name, repo_name) repository_ref = registry_model.lookup_repository(namespace_name, repo_name, kind_filter="image") if permission.can() or (repository_ref is not None and repository_ref.is_public): if repository_ref is None: abort(404) tag_map = registry_model.get_legacy_tags_map(repository_ref, storage) return jsonify(tag_map) abort(403)
def get_image_layer(namespace, repository, image_id, headers): permission = ReadRepositoryPermission(namespace, repository) repository_ref = registry_model.lookup_repository(namespace, repository, kind_filter='image') logger.debug('Checking repo permissions') if permission.can() or (repository_ref is not None and repository_ref.is_public): if repository_ref is None: abort(404) legacy_image = registry_model.get_legacy_image(repository_ref, image_id, include_blob=True) if legacy_image is None: abort(404, 'Image %(image_id)s not found', issue='unknown-image', image_id=image_id) path = legacy_image.blob.storage_path metric_queue.pull_byte_count.Inc(legacy_image.blob.compressed_size, labelvalues=['v1']) try: logger.debug('Looking up the direct download URL for path: %s', path) direct_download_url = store.get_direct_download_url( legacy_image.blob.placements, path, get_request_ip()) if direct_download_url: logger.debug('Returning direct download URL') resp = redirect(direct_download_url) return resp # Close the database handle here for this process before we send the long download. database.close_db_filter(None) logger.debug('Streaming layer data') return Response(store.stream_read(legacy_image.blob.placements, path), headers=headers) except (IOError, AttributeError): logger.exception('Image layer data not found') abort(404, 'Image %(image_id)s not found', issue='unknown-image', image_id=image_id) abort(403)
def get_tag(namespace_name, repo_name, tag): permission = ReadRepositoryPermission(namespace_name, repo_name) repository_ref = registry_model.lookup_repository(namespace_name, repo_name, kind_filter="image") if permission.can() or (repository_ref is not None and repository_ref.is_public): if repository_ref is None: abort(404) image_id = registry_model.get_tag_legacy_image_id( repository_ref, tag, storage) if image_id is None: abort(404) resp = make_response('"%s"' % image_id) resp.headers["Content-Type"] = "application/json" return resp abort(403)
def get_repository_images(namespace_name, repo_name): repository_ref = registry_model.lookup_repository(namespace_name, repo_name, kind_filter='image') permission = ReadRepositoryPermission(namespace_name, repo_name) if permission.can() or (repository_ref and repository_ref.is_public): # We can't rely on permissions to tell us if a repo exists anymore if repository_ref is None: abort(404, message='Unknown repository', issue='unknown-repo') logger.debug('Building repository image response') resp = make_response(json.dumps([]), 200) resp.mimetype = 'application/json' track_and_log('pull_repo', repository_ref, analytics_name='pull_repo_100x', analytics_sample=0.01) metric_queue.repository_pull.Inc( labelvalues=[namespace_name, repo_name, 'v1', True]) return resp abort(403)
def _verify_repo_verb(_, namespace, repo_name, tag_name, verb, checker=None): permission = ReadRepositoryPermission(namespace, repo_name) repo = model.repository.get_repository(namespace, repo_name) repo_is_public = repo is not None and model.repository.is_repository_public(repo) if not permission.can() and not repo_is_public: logger.debug( "No permission to read repository %s/%s for user %s with verb %s", namespace, repo_name, get_authenticated_user(), verb, ) abort(403) if repo is not None and repo.kind.name != "image": logger.debug( "Repository %s/%s for user %s is not an image repo", namespace, repo_name, get_authenticated_user(), ) abort(405) # Make sure the repo's namespace isn't disabled. if not registry_model.is_namespace_enabled(namespace): abort(400) # Lookup the requested tag. repo_ref = registry_model.lookup_repository(namespace, repo_name) if repo_ref is None: abort(404) tag = registry_model.get_repo_tag(repo_ref, tag_name) if tag is None: logger.debug( "Tag %s does not exist in repository %s/%s for user %s", tag, namespace, repo_name, get_authenticated_user(), ) abort(404) # Get its associated manifest. manifest = registry_model.get_manifest_for_tag(tag, backfill_if_necessary=True) if manifest is None: logger.debug("Could not get manifest on %s/%s:%s::%s", namespace, repo_name, tag.name, verb) abort(404) # Retrieve the schema1-compatible version of the manifest. try: schema1_manifest = registry_model.get_schema1_parsed_manifest( manifest, namespace, repo_name, tag.name, storage ) except ManifestException: logger.exception( "Could not get manifest on %s/%s:%s::%s", namespace, repo_name, tag.name, verb ) abort(400) if schema1_manifest is None: abort(404) # If there is a data checker, call it first. if checker is not None: if not checker(tag, schema1_manifest): logger.debug( "Check mismatch on %s/%s:%s, verb %s", namespace, repo_name, tag.name, verb ) abort(404) # Preload the tag's repository information, so it gets cached. assert tag.repository.namespace_name assert tag.repository.name return tag, manifest, schema1_manifest