diff --git a/data/registry_model/interface.py b/data/registry_model/interface.py index 7214d8e06..6084b3395 100644 --- a/data/registry_model/interface.py +++ b/data/registry_model/interface.py @@ -225,3 +225,8 @@ class RegistryDataInterface(object): @abstractmethod def delete_blob_upload(self, blob_upload): """ Deletes a blob upload record. """ + + @abstractmethod + def commit_blob_upload(self, blob_upload, blob_digest_str, blob_expiration_seconds): + """ Commits the blob upload into a blob and sets an expiration before that blob will be GCed. + """ diff --git a/data/registry_model/registry_pre_oci_model.py b/data/registry_model/registry_pre_oci_model.py index 9f3c5c67e..f2bd6cf38 100644 --- a/data/registry_model/registry_pre_oci_model.py +++ b/data/registry_model/registry_pre_oci_model.py @@ -624,5 +624,27 @@ class PreOCIModel(RegistryDataInterface): if upload_record is not None: upload_record.delete_instance() + def commit_blob_upload(self, blob_upload, blob_digest_str, blob_expiration_seconds): + """ Commits the blob upload into a blob and sets an expiration before that blob will be GCed. + """ + upload_record = model.blob.get_blob_upload_by_uuid(blob_upload.upload_id) + if upload_record is None: + return None + + repository = upload_record.repository + namespace_name = repository.namespace_user.username + repo_name = repository.name + + # Create the blob and temporarily tag it. + location_obj = model.storage.get_image_location_for_name(blob_upload.location_name) + blob_record = model.blob.store_blob_record_and_temp_link( + namespace_name, repo_name, blob_digest_str, location_obj.id, blob_upload.byte_count, + blob_expiration_seconds, blob_upload.uncompressed_byte_count) + + # Delete the blob upload. + upload_record.delete_instance() + return Blob.for_image_storage(blob_record, + storage_path=model.storage.get_layer_path(blob_record)) + pre_oci_model = PreOCIModel() diff --git a/data/registry_model/test/test_pre_oci_model.py b/data/registry_model/test/test_pre_oci_model.py index 3c2c9adbb..cdeec9f45 100644 --- a/data/registry_model/test/test_pre_oci_model.py +++ b/data/registry_model/test/test_pre_oci_model.py @@ -1,3 +1,6 @@ +import hashlib +import uuid + from datetime import datetime, timedelta import pytest @@ -530,7 +533,8 @@ def test_torrent_info(pre_oci_model): def test_blob_uploads(pre_oci_model): repository_ref = pre_oci_model.lookup_repository('devtable', 'simple') - blob_upload = pre_oci_model.create_blob_upload(repository_ref, 'local_us', {'some': 'metadata'}) + blob_upload = pre_oci_model.create_blob_upload(repository_ref, str(uuid.uuid4()), + 'local_us', {'some': 'metadata'}) assert blob_upload assert blob_upload.storage_metadata == {'some': 'metadata'} assert blob_upload.location_name == 'local_us' @@ -557,3 +561,17 @@ def test_blob_uploads(pre_oci_model): # Ensure it can no longer be found. assert not pre_oci_model.lookup_blob_upload(repository_ref, blob_upload.upload_id) + + +def test_commit_blob_upload(pre_oci_model): + repository_ref = pre_oci_model.lookup_repository('devtable', 'simple') + blob_upload = pre_oci_model.create_blob_upload(repository_ref, str(uuid.uuid4()), + 'local_us', {'some': 'metadata'}) + + # Commit the blob upload and make sure it is written as a blob. + digest = 'sha256:' + hashlib.sha256('hello').hexdigest() + blob = pre_oci_model.commit_blob_upload(blob_upload, digest, 60) + assert blob.digest == digest + + # Ensure the upload can no longer be found. + assert not pre_oci_model.lookup_blob_upload(repository_ref, blob_upload.upload_id)