diff --git a/CHANGES/6381.bugfix b/CHANGES/6381.bugfix new file mode 100644 index 00000000000..107b8614892 --- /dev/null +++ b/CHANGES/6381.bugfix @@ -0,0 +1 @@ +Fixed duplicate chunk uploads corrupting the entire upload diff --git a/pulpcore/app/models/upload.py b/pulpcore/app/models/upload.py index c452a13a4dd..50dca8209d3 100644 --- a/pulpcore/app/models/upload.py +++ b/pulpcore/app/models/upload.py @@ -43,6 +43,7 @@ def append(self, chunk, offset, sha256=None): if sha256 != current_sha256: raise serializers.ValidationError(_("Checksum does not match chunk upload.")) + UploadChunk.objects.filter(upload=self, offset=offset).delete() upload_chunk = UploadChunk(upload=self, offset=offset, size=len(chunk)) filename = os.path.basename(upload_chunk.storage_path("")) upload_chunk.file.save(filename, ContentFile(chunk)) diff --git a/pulpcore/tests/functional/api/test_upload.py b/pulpcore/tests/functional/api/test_upload.py index 0dd8e87b0eb..219d0b42218 100644 --- a/pulpcore/tests/functional/api/test_upload.py +++ b/pulpcore/tests/functional/api/test_upload.py @@ -193,3 +193,32 @@ def test_upload_owner(pulpcore_bindings, gen_user, gen_object_with_cleanup): "core.delete_upload", "core.manage_roles_upload", } + + +@pytest.mark.parallel +def test_upload_duplicate_chunk( + pulpcore_bindings, + pulpcore_random_chunked_file_factory, + gen_object_with_cleanup, + monitor_task, +): + """Test that uploading the same chunk twice replaces rather than duplicates it.""" + file_chunks_data = pulpcore_random_chunked_file_factory(number_chunks=1) + size = file_chunks_data["size"] + chunk = file_chunks_data["chunks"][0] + sha256 = file_chunks_data["digest"] + + upload = gen_object_with_cleanup(pulpcore_bindings.UploadsApi, {"size": size}) + kwargs = {"file": chunk[0], "content_range": chunk[1], "upload_href": upload.pulp_href} + + pulpcore_bindings.UploadsApi.update(**kwargs) + pulpcore_bindings.UploadsApi.update(**kwargs) + + upload_detail = pulpcore_bindings.UploadsApi.read(upload.pulp_href) + assert len(upload_detail.chunks) == 1, "Duplicate chunk should replace the existing one" + + task = pulpcore_bindings.UploadsApi.commit(upload.pulp_href, {"sha256": sha256}).task + response = monitor_task(task) + artifact_href = response.created_resources[0] + artifact = pulpcore_bindings.ArtifactsApi.read(artifact_href) + assert artifact.sha256 == sha256