diff --git a/src/sentry/api/endpoints/debug_files.py b/src/sentry/api/endpoints/debug_files.py index fb6e190fd102..4d9f736bdce7 100644 --- a/src/sentry/api/endpoints/debug_files.py +++ b/src/sentry/api/endpoints/debug_files.py @@ -858,11 +858,15 @@ def _clone_proguard_debug_file_for_reupload( } meta = build_proguard_reupload_dif_meta(debug_file, requested_debug_id) - if not debug_file.uses_objectstore_for_read(): + if debug_file.storage_path is None: assert debug_file.file is not None dif, created = create_dif_from_id(project, meta, file=debug_file.file) else: - source_fileobj = debug_file.get_file() + response = debug_file._get_objectstore_session().get(debug_file.storage_path) + if response is None: + raise FileNotFoundError("Debug file does not exist in Objectstore") + + source_fileobj = response.payload try: # Spool into a temporary file to get a seekable stream. with tempfile.TemporaryFile() as tmp: diff --git a/tests/sentry/api/endpoints/test_dif_assemble.py b/tests/sentry/api/endpoints/test_dif_assemble.py index 1865d61a0085..a1cd7c4a214d 100644 --- a/tests/sentry/api/endpoints/test_dif_assemble.py +++ b/tests/sentry/api/endpoints/test_dif_assemble.py @@ -437,6 +437,59 @@ def test_reuses_existing_proguard_file_with_new_debug_id(self) -> None: assert first_dif.file_id == second_dif.file_id assert File.objects.filter(type="project.dif", checksum=checksum).count() == 1 + def test_objectstore_assemble_clones_existing_proguard_to_new_objectstore_file(self) -> None: + file_contents = b"proguard mapping" + checksum = sha1(file_contents).hexdigest() + get_chunk_upload_session(self.organization.id).put(file_contents, key=checksum) + chunks = [checksum] + + with self.feature( + { + "organizations:objectstore-debugfiles-exclusive-write": True, + "organizations:objectstore-debugfiles-write": False, + "organizations:objectstore-debugfiles-read": True, + } + ): + assemble_dif( + project_id=self.project.id, + name="/proguard/mapping-00000000-0000-0000-0000-000000000000.txt", + checksum=checksum, + chunks=chunks, + use_objectstore=True, + ) + + first_dif = ProjectDebugFile.objects.get( + project_id=self.project.id, + debug_id="00000000-0000-0000-0000-000000000000", + ) + + response = self.client.post( + self.url, + data={ + checksum: { + "name": "/proguard/mapping-11111111-1111-1111-1111-111111111111.txt", + "chunks": chunks, + } + }, + HTTP_AUTHORIZATION=f"Bearer {self.token.token}", + ) + + assert response.status_code == 200, response.content + assert response.data[checksum]["state"] == ChunkFileState.OK + assert response.data[checksum]["dif"]["uuid"] == "11111111-1111-1111-1111-111111111111" + + second_dif = ProjectDebugFile.objects.get( + project_id=self.project.id, + debug_id="11111111-1111-1111-1111-111111111111", + ) + + assert first_dif.file_id is None + assert second_dif.file_id is None + assert first_dif.storage_path is not None + assert second_dif.storage_path is not None + assert first_dif.storage_path != second_dif.storage_path + assert File.objects.filter(type="project.dif", checksum=checksum).count() == 0 + def test_reupload_proguard_with_same_debug_id_is_idempotent(self) -> None: file_contents = b"proguard mapping" checksum = sha1(file_contents).hexdigest() @@ -590,6 +643,7 @@ def test_clone_dual_written_source_to_file(self) -> None: ) assert first_dif.file_id is not None assert first_dif.storage_path is not None + assert not first_dif.uses_objectstore_for_read() with self.feature({"organizations:objectstore-debugfiles-write": False}): response = self._clone_request(checksum, chunks) @@ -604,5 +658,37 @@ def test_clone_dual_written_source_to_file(self) -> None: ) # The source stays Objectstore-backed; the clone is written as a File. assert second_dif.file_id is not None + assert second_dif.file_id != first_dif.file_id assert second_dif.storage_path is None assert second_dif.get_file().read() == file_contents + + def test_clone_file_backed_source_to_objectstore_exclusive(self) -> None: + file_contents = b"proguard mapping" + checksum = sha1(file_contents).hexdigest() + blob = FileBlob.from_file_with_organization(ContentFile(file_contents), self.organization) + chunks = [blob.checksum] + + with self.feature({"organizations:objectstore-debugfiles-write": False}): + self._assemble_source(checksum, chunks) + + first_dif = ProjectDebugFile.objects.get( + project_id=self.project.id, + debug_id="00000000-0000-0000-0000-000000000000", + ) + assert first_dif.file_id is not None + assert first_dif.storage_path is None + + with self.feature("organizations:objectstore-debugfiles-exclusive-write"): + response = self._clone_request(checksum, chunks) + + assert response.status_code == 200, response.content + assert response.data[checksum]["state"] == ChunkFileState.OK + + second_dif = ProjectDebugFile.objects.get( + project_id=self.project.id, + debug_id="11111111-1111-1111-1111-111111111111", + ) + assert second_dif.file_id is None + assert second_dif.storage_path is not None + assert second_dif.get_file().read() == file_contents + assert File.objects.filter(type="project.dif", checksum=checksum).count() == 1