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)
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, }, )
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
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, }, )
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)
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, })
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
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
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)
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)
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 {}
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]}
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)
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)
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
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
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
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