Beispiel #1
0
def delete_manifest_by_digest(namespace_name, repo_name, manifest_ref):
    """
    Delete the manifest specified by the digest.

    Note: there is no equivalent method for deleting by tag name because it is
    forbidden by the spec.
    """
    with db_disallow_replica_use():
        repository_ref = registry_model.lookup_repository(namespace_name, repo_name)
        if repository_ref is None:
            raise NameUnknown("repository not found")

        manifest = registry_model.lookup_manifest_by_digest(repository_ref, manifest_ref)
        if manifest is None:
            raise ManifestUnknown()

        tags = registry_model.delete_tags_for_manifest(manifest)
        if not tags:
            raise ManifestUnknown()

        for tag in tags:
            track_and_log("delete_tag", repository_ref, tag=tag.name, digest=manifest_ref)

        if app.config.get("FEATURE_QUOTA_MANAGEMENT", False):
            repository.force_cache_repo_size(repository_ref.id)

        return Response(status=202)
Beispiel #2
0
def fetch_manifest_by_digest(namespace_name, repo_name, manifest_ref, registry_model):
    try:
        repository_ref = registry_model.lookup_repository(
            namespace_name, repo_name, raise_on_error=True, manifest_ref=manifest_ref
        )
    except RepositoryDoesNotExist as e:
        image_pulls.labels("v2", "manifest", 404).inc()
        raise NameUnknown("repository not found")

    try:
        manifest = registry_model.lookup_manifest_by_digest(
            repository_ref, manifest_ref, raise_on_error=True
        )
    except ManifestDoesNotExist as e:
        image_pulls.labels("v2", "manifest", 404).inc()
        raise ManifestUnknown(str(e))

    track_and_log("pull_repo", repository_ref, manifest_digest=manifest_ref)
    image_pulls.labels("v2", "manifest", 200).inc()

    return Response(
        manifest.internal_manifest_bytes.as_unicode(),
        status=200,
        headers={
            "Content-Type": manifest.media_type,
            "Docker-Content-Digest": manifest.digest,
        },
    )
Beispiel #3
0
    def delete(self, namespace_name, repository_name, manifestref, labelid):
        """
        Deletes an existing label from a manifest.
        """
        repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
        if repo_ref is None:
            raise NotFound()

        manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref)
        if manifest is None:
            raise NotFound()

        deleted = registry_model.delete_manifest_label(manifest, labelid)
        if deleted is None:
            raise NotFound()

        metadata = {
            "id": labelid,
            "key": deleted.key,
            "value": deleted.value,
            "manifest_digest": manifestref,
            "namespace": namespace_name,
            "repo": repository_name,
        }

        log_action("manifest_label_delete", namespace_name, metadata, repo_name=repository_name)
        return "", 204
Beispiel #4
0
def fetch_manifest_by_digest(namespace_name, repo_name, manifest_ref):
    repository_ref = registry_model.lookup_repository(namespace_name,
                                                      repo_name)
    if repository_ref is None:
        image_pulls.labels("v2_1", "manifest", 404).inc()
        raise NameUnknown()

    manifest = registry_model.lookup_manifest_by_digest(
        repository_ref, manifest_ref)
    if manifest is None:
        image_pulls.labels("v2_1", "manifest", 404).inc()
        raise ManifestUnknown()

    manifest_bytes, manifest_digest, manifest_media_type = _rewrite_schema_if_necessary(
        namespace_name, repo_name, "$digest", manifest)
    if manifest_digest is None:
        image_pulls.labels("v2_1", "manifest", 404).inc()
        raise ManifestUnknown()

    track_and_log("pull_repo", repository_ref, manifest_digest=manifest_ref)
    image_pulls.labels("v2_1", "manifest", 200).inc()

    return Response(
        manifest_bytes.as_unicode(),
        status=200,
        headers={
            "Content-Type": manifest_media_type,
            "Docker-Content-Digest": manifest_digest,
        },
    )
Beispiel #5
0
def delete_manifest_by_digest(namespace_name, repo_name, manifest_ref):
    """
    Delete the manifest specified by the digest.

    Note: there is no equivalent method for deleting by tag name because it is
    forbidden by the spec.
    """
    with db_disallow_replica_use():
        repository_ref = registry_model.lookup_repository(
            namespace_name, repo_name)
        if repository_ref is None:
            raise NameUnknown()

        manifest = registry_model.lookup_manifest_by_digest(
            repository_ref, manifest_ref)
        if manifest is None:
            raise ManifestUnknown()

        tags = registry_model.delete_tags_for_manifest(manifest)
        if not tags:
            raise ManifestUnknown()

        for tag in tags:
            track_and_log("delete_tag",
                          repository_ref,
                          tag=tag.name,
                          digest=manifest_ref)

        return Response(status=202)
Beispiel #6
0
def fetch_manifest_by_digest(namespace_name, repo_name, manifest_ref):
    repository_ref = registry_model.lookup_repository(namespace_name,
                                                      repo_name)
    if repository_ref is None:
        raise NameUnknown()

    manifest = registry_model.lookup_manifest_by_digest(
        repository_ref, manifest_ref)
    if manifest is None:
        raise ManifestUnknown()

    manifest_bytes, manifest_digest, manifest_media_type = _rewrite_schema_if_necessary(
        namespace_name, repo_name, '$digest', manifest)
    if manifest_digest is None:
        raise ManifestUnknown()

    track_and_log('pull_repo', repository_ref, manifest_digest=manifest_ref)
    metric_queue.repository_pull.Inc(
        labelvalues=[namespace_name, repo_name, 'v2', True])

    return Response(manifest_bytes.as_unicode(),
                    status=200,
                    headers={
                        'Content-Type': manifest_media_type,
                        'Docker-Content-Digest': manifest_digest,
                    })
Beispiel #7
0
    def delete(self, namespace_name, repository_name, manifestref, labelid):
        """ Deletes an existing label from a manifest. """
        repo_ref = registry_model.lookup_repository(namespace_name,
                                                    repository_name)
        if repo_ref is None:
            raise NotFound()

        manifest = registry_model.lookup_manifest_by_digest(
            repo_ref, manifestref)
        if manifest is None:
            raise NotFound()

        deleted = registry_model.delete_manifest_label(manifest, labelid)
        if deleted is None:
            raise NotFound()

        metadata = {
            'id': labelid,
            'key': deleted.key,
            'value': deleted.value,
            'manifest_digest': manifestref,
            'namespace': namespace_name,
            'repo': repository_name,
        }

        log_action('manifest_label_delete',
                   namespace_name,
                   metadata,
                   repo_name=repository_name)
        return '', 204
Beispiel #8
0
    def post(self, namespace_name, repository_name, manifestref):
        """ Adds a new label into the tag manifest. """
        label_data = request.get_json()

        # Check for any reserved prefixes.
        if label_validator.has_reserved_prefix(label_data['key']):
            abort(400, message='Label has a reserved prefix')

        repo_ref = registry_model.lookup_repository(namespace_name,
                                                    repository_name)
        if repo_ref is None:
            raise NotFound()

        manifest = registry_model.lookup_manifest_by_digest(
            repo_ref, manifestref)
        if manifest is None:
            raise NotFound()

        label = None
        try:
            label = registry_model.create_manifest_label(
                manifest, label_data['key'], label_data['value'], 'api',
                label_data['media_type'])
        except InvalidLabelKeyException:
            message = ('Label is of an invalid format or missing please ' +
                       'use %s format for labels' % VALID_LABEL_KEY_REGEX)
            abort(400, message=message)
        except InvalidMediaTypeException:
            message = 'Media type is invalid please use a valid media type: text/plain, application/json'
            abort(400, message=message)

        if label is None:
            raise NotFound()

        metadata = {
            'id': label.uuid,
            'key': label.key,
            'value': label.value,
            'manifest_digest': manifestref,
            'media_type': label.media_type_name,
            'namespace': namespace_name,
            'repo': repository_name,
        }

        log_action('manifest_label_add',
                   namespace_name,
                   metadata,
                   repo_name=repository_name)

        resp = {'label': _label_dict(label)}
        repo_string = '%s/%s' % (namespace_name, repository_name)
        headers = {
            'Location':
            api.url_for(ManageRepositoryManifestLabel,
                        repository=repo_string,
                        manifestref=manifestref,
                        labelid=label.uuid),
        }
        return resp, 201, headers
Beispiel #9
0
    def get(self, namespace_name, repository_name, manifestref):
        repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
        if repo_ref is None:
            raise NotFound()

        manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref)
        if manifest is None:
            raise NotFound()

        return _manifest_dict(manifest)
Beispiel #10
0
    def get(self, namespace, repository, manifestref, parsed_args):
        repo_ref = registry_model.lookup_repository(namespace, repository)
        if repo_ref is None:
            raise NotFound()

        manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref, allow_dead=True)
        if manifest is None:
            raise NotFound()

        return _security_info(manifest, parsed_args.vulnerabilities)
Beispiel #11
0
    def post(self, namespace, repository, tag):
        """
        Restores a repository tag back to a previous image in the repository.
        """
        repo_ref = registry_model.lookup_repository(namespace, repository)
        if repo_ref is None:
            raise NotFound()

        # Restore the tag back to the previous image.
        image_id = request.get_json().get("image", None)
        manifest_digest = request.get_json().get("manifest_digest", None)

        if image_id is None and manifest_digest is None:
            raise InvalidRequest("Missing manifest_digest")

        # Data for logging the reversion/restoration.
        username = get_authenticated_user().username
        log_data = {
            "username": username,
            "repo": repository,
            "tag": tag,
            "image": image_id,
            "manifest_digest": manifest_digest,
        }

        manifest_or_legacy_image = None
        if manifest_digest is not None:
            manifest_or_legacy_image = registry_model.lookup_manifest_by_digest(
                repo_ref,
                manifest_digest,
                allow_dead=True,
                require_available=True)
        elif image_id is not None:
            manifest_or_legacy_image = registry_model.get_legacy_image(
                repo_ref, image_id)

        if manifest_or_legacy_image is None:
            raise NotFound()

        if not registry_model.retarget_tag(
                repo_ref,
                tag,
                manifest_or_legacy_image,
                storage,
                docker_v2_signing_key,
                is_reversion=True,
        ):
            raise InvalidRequest("Could not restore tag")

        log_action("revert_tag", namespace, log_data, repo_name=repository)

        return {}
Beispiel #12
0
    def get(self, namespace_name, repository_name, manifestref, parsed_args):
        repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
        if repo_ref is None:
            raise NotFound()

        manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref)
        if manifest is None:
            raise NotFound()

        labels = registry_model.list_manifest_labels(manifest, parsed_args["filter"])
        if labels is None:
            raise NotFound()

        return {"labels": [_label_dict(label) for label in labels]}
Beispiel #13
0
    def get(self, namespace_name, repository_name, manifestref):
        repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
        if repo_ref is None:
            raise NotFound()

        manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref)
        # sub-manifests created via pull-thru proxy cache as part of a manifest list
        # pull will not contain the manifest bytes unless individually pulled.
        # to avoid a parsing error from `_manifest_dict`, we return a 404 either
        # when the manifest doesn't exist or if the manifest bytes are empty.
        if manifest is None or manifest.internal_manifest_bytes.as_unicode() == "":
            raise NotFound()

        return _manifest_dict(manifest)
Beispiel #14
0
    def get(self, namespace_name, repository_name, manifestref, labelid):
        """ Retrieves the label with the specific ID under the manifest. """
        repo_ref = registry_model.lookup_repository(namespace_name,
                                                    repository_name)
        if repo_ref is None:
            raise NotFound()

        manifest = registry_model.lookup_manifest_by_digest(
            repo_ref, manifestref)
        if manifest is None:
            raise NotFound()

        label = registry_model.get_manifest_label(manifest, labelid)
        if label is None:
            raise NotFound()

        return _label_dict(label)
Beispiel #15
0
    def put(self, namespace, repository, tag):
        """
        Change which image a tag points to or create a new tag.
        """
        if not TAG_REGEX.match(tag):
            abort(400, TAG_ERROR)

        repo_ref = registry_model.lookup_repository(namespace, repository)
        if repo_ref is None:
            raise NotFound()

        if "expiration" in request.get_json():
            tag_ref = registry_model.get_repo_tag(repo_ref, tag)
            if tag_ref is None:
                raise NotFound()

            expiration = request.get_json().get("expiration")
            expiration_date = None
            if expiration is not None:
                try:
                    expiration_date = datetime.utcfromtimestamp(
                        float(expiration))
                except ValueError:
                    abort(400)

                if expiration_date <= datetime.now():
                    abort(400)

            existing_end_ts, ok = registry_model.change_repository_tag_expiration(
                tag_ref, expiration_date)
            if ok:
                if not (existing_end_ts is None and expiration_date is None):
                    log_action(
                        "change_tag_expiration",
                        namespace,
                        {
                            "username": get_authenticated_user().username,
                            "repo": repository,
                            "tag": tag,
                            "namespace": namespace,
                            "expiration_date": expiration_date,
                            "old_expiration_date": existing_end_ts,
                        },
                        repo_name=repository,
                    )
            else:
                raise InvalidRequest(
                    "Could not update tag expiration; Tag has probably changed"
                )

        if "image" in request.get_json(
        ) or "manifest_digest" in request.get_json():
            existing_tag = registry_model.get_repo_tag(
                repo_ref, tag, include_legacy_image=True)

            manifest_or_image = None
            image_id = None
            manifest_digest = None

            if "manifest_digest" in request.get_json():
                manifest_digest = request.get_json()["manifest_digest"]
                manifest_or_image = registry_model.lookup_manifest_by_digest(
                    repo_ref, manifest_digest, require_available=True)
            else:
                image_id = request.get_json()["image"]
                manifest_or_image = registry_model.get_legacy_image(
                    repo_ref, image_id)

            if manifest_or_image is None:
                raise NotFound()

            existing_manifest = (
                registry_model.get_manifest_for_tag(existing_tag)
                if existing_tag else None)
            existing_manifest_digest = existing_manifest.digest if existing_manifest else None

            if not registry_model.retarget_tag(repo_ref, tag,
                                               manifest_or_image, storage,
                                               docker_v2_signing_key):
                raise InvalidRequest("Could not move tag")

            username = get_authenticated_user().username

            log_action(
                "move_tag" if existing_tag else "create_tag",
                namespace,
                {
                    "username": username,
                    "repo": repository,
                    "tag": tag,
                    "namespace": namespace,
                    "image": image_id,
                    "manifest_digest": manifest_digest,
                    "original_manifest_digest": existing_manifest_digest,
                },
                repo_name=repository,
            )

        return "Updated", 201
Beispiel #16
0
    def _build_complete(self, result):
        """ Wraps up a completed build. Handles any errors and calls self._build_finished. """
        build_id = self._current_job.repo_build.uuid

        try:
            # Retrieve the result. This will raise an ApplicationError on any error that occurred.
            result_value = result.result()
            kwargs = {}

            # Note: If we are hitting an older builder that didn't return ANY map data, then the result
            # value will be a bool instead of a proper CallResult object.
            # Therefore: we have a try-except guard here to ensure we don't hit this pitfall.
            try:
                kwargs = result_value.kwresults
            except:
                pass

            try:
                yield From(self._build_status.set_phase(BUILD_PHASE.COMPLETE))
            except InvalidRepositoryBuildException:
                logger.warning(
                    'Build %s was not found; repo was probably deleted',
                    build_id)
                raise Return()

            yield From(self._build_finished(BuildJobResult.COMPLETE))

            # Label the pushed manifests with the build metadata.
            manifest_digests = kwargs.get('digests') or []
            repository = registry_model.lookup_repository(
                self._current_job.namespace, self._current_job.repo_name)
            if repository is not None:
                for digest in manifest_digests:
                    with UseThenDisconnect(app.config):
                        manifest = registry_model.lookup_manifest_by_digest(
                            repository, digest, require_available=True)
                        if manifest is None:
                            continue

                        registry_model.create_manifest_label(
                            manifest, INTERNAL_LABEL_BUILD_UUID, build_id,
                            'internal', 'text/plain')

            # Send the notification that the build has completed successfully.
            self._current_job.send_notification(
                'build_success',
                image_id=kwargs.get('image_id'),
                manifest_digests=manifest_digests)
        except ApplicationError as aex:
            worker_error = WorkerError(aex.error, aex.kwargs.get('base_error'))

            # Write the error to the log.
            yield From(
                self._build_status.set_error(
                    worker_error.public_message(),
                    worker_error.extra_data(),
                    internal_error=worker_error.is_internal_error(),
                    requeued=self._current_job.has_retries_remaining()))

            # Send the notification that the build has failed.
            self._current_job.send_notification(
                'build_failure', error_message=worker_error.public_message())

            # Mark the build as completed.
            if worker_error.is_internal_error():
                logger.exception(
                    '[BUILD INTERNAL ERROR: Remote] Build ID: %s: %s',
                    build_id, worker_error.public_message())
                yield From(self._build_finished(BuildJobResult.INCOMPLETE))
            else:
                logger.debug('Got remote failure exception for build %s: %s',
                             build_id, aex)
                yield From(self._build_finished(BuildJobResult.ERROR))

        # Remove the current job.
        self._current_job = None
Beispiel #17
0
    def put(self, namespace, repository, tag):
        """ Change which image a tag points to or create a new tag."""
        if not TAG_REGEX.match(tag):
            abort(400, TAG_ERROR)

        repo_ref = registry_model.lookup_repository(namespace, repository)
        if repo_ref is None:
            raise NotFound()

        if 'expiration' in request.get_json():
            tag_ref = registry_model.get_repo_tag(repo_ref, tag)
            if tag_ref is None:
                raise NotFound()

            expiration = request.get_json().get('expiration')
            expiration_date = None
            if expiration is not None:
                try:
                    expiration_date = datetime.utcfromtimestamp(
                        float(expiration))
                except ValueError:
                    abort(400)

                if expiration_date <= datetime.now():
                    abort(400)

            existing_end_ts, ok = registry_model.change_repository_tag_expiration(
                tag_ref, expiration_date)
            if ok:
                if not (existing_end_ts is None and expiration_date is None):
                    log_action(
                        'change_tag_expiration',
                        namespace, {
                            'username': get_authenticated_user().username,
                            'repo': repository,
                            'tag': tag,
                            'namespace': namespace,
                            'expiration_date': expiration_date,
                            'old_expiration_date': existing_end_ts
                        },
                        repo_name=repository)
            else:
                raise InvalidRequest(
                    'Could not update tag expiration; Tag has probably changed'
                )

        if 'image' in request.get_json(
        ) or 'manifest_digest' in request.get_json():
            existing_tag = registry_model.get_repo_tag(
                repo_ref, tag, include_legacy_image=True)

            manifest_or_image = None
            image_id = None
            manifest_digest = None

            if 'image' in request.get_json():
                image_id = request.get_json()['image']
                manifest_or_image = registry_model.get_legacy_image(
                    repo_ref, image_id)
            else:
                manifest_digest = request.get_json()['manifest_digest']
                manifest_or_image = registry_model.lookup_manifest_by_digest(
                    repo_ref, manifest_digest, require_available=True)

            if manifest_or_image is None:
                raise NotFound()

            # TODO: Remove this check once fully on V22
            existing_manifest_digest = None
            if existing_tag:
                existing_manifest = registry_model.get_manifest_for_tag(
                    existing_tag)
                existing_manifest_digest = existing_manifest.digest if existing_manifest else None

            if not registry_model.retarget_tag(repo_ref, tag,
                                               manifest_or_image, storage,
                                               docker_v2_signing_key):
                raise InvalidRequest('Could not move tag')

            username = get_authenticated_user().username

            log_action(
                'move_tag' if existing_tag else 'create_tag',
                namespace, {
                    'username':
                    username,
                    'repo':
                    repository,
                    'tag':
                    tag,
                    'namespace':
                    namespace,
                    'image':
                    image_id,
                    'manifest_digest':
                    manifest_digest,
                    'original_image':
                    (existing_tag.legacy_image.docker_image_id if existing_tag
                     and existing_tag.legacy_image_if_present else None),
                    'original_manifest_digest':
                    existing_manifest_digest,
                },
                repo_name=repository)

        return 'Updated', 201
Beispiel #18
0
    def post(self, namespace_name, repository_name, manifestref):
        """
        Adds a new label into the tag manifest.
        """
        label_data = request.get_json()

        # Check for any reserved prefixes.
        if label_validator.has_reserved_prefix(label_data["key"]):
            abort(400, message="Label has a reserved prefix")

        repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
        if repo_ref is None:
            raise NotFound()

        manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref)
        if manifest is None:
            raise NotFound()

        label = None
        try:
            label = registry_model.create_manifest_label(
                manifest, label_data["key"], label_data["value"], "api", label_data["media_type"]
            )
        except InvalidLabelKeyException:
            message = (
                "Label is of an invalid format or missing please "
                + "use %s format for labels" % VALID_LABEL_KEY_REGEX
            )
            abort(400, message=message)
        except InvalidMediaTypeException:
            message = (
                "Media type is invalid please use a valid media type: text/plain, application/json"
            )
            abort(400, message=message)

        if label is None:
            raise NotFound()

        metadata = {
            "id": label.uuid,
            "key": label.key,
            "value": label.value,
            "manifest_digest": manifestref,
            "media_type": label.media_type_name,
            "namespace": namespace_name,
            "repo": repository_name,
        }

        log_action("manifest_label_add", namespace_name, metadata, repo_name=repository_name)

        resp = {"label": _label_dict(label)}
        repo_string = "%s/%s" % (namespace_name, repository_name)
        headers = {
            "Location": api.url_for(
                ManageRepositoryManifestLabel,
                repository=repo_string,
                manifestref=manifestref,
                labelid=label.uuid,
            ),
        }
        return resp, 201, headers