Skip to content

Commit 2feac5f

Browse files
authored
Fix 500 on delete nonexistent bundle, again (fixes #1918) (#2351)
1 parent 9fd58d3 commit 2feac5f

File tree

3 files changed

+30
-27
lines changed

3 files changed

+30
-27
lines changed

dss-api.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1993,7 +1993,7 @@ paths:
19931993
code:
19941994
type: string
19951995
description: Machine-readable error code. The types of return values should not be changed lightly.
1996-
enum: [unhandled_exception, illegal_arguments, read_only]
1996+
enum: [unhandled_exception, illegal_arguments, read_only, not_found]
19971997
required:
19981998
- code
19991999
/bundles/{uuid}/checkout:

dss/api/bundles/__init__.py

+20-23
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import io
1+
# -*- coding: utf-8 -*-
22
import os
33
import json
44
import time
@@ -7,19 +7,19 @@
77

88
import nestedcontext
99
import requests
10-
from cloud_blobstore import BlobNotFoundError, BlobStore, BlobStoreTimeoutError
10+
from cloud_blobstore import BlobNotFoundError, BlobStoreTimeoutError
1111
from flask import jsonify, redirect, request, make_response
1212
from concurrent.futures import ThreadPoolExecutor, as_completed
1313

1414
from dss import DSSException, dss_handler, DSSForbiddenException
1515
from dss.config import Config, Replica
16-
from dss.storage.blobstore import idempotent_save, test_object_exists, ObjectTest
16+
from dss.storage.blobstore import idempotent_save, ObjectTest, test_object_exists
1717
from dss.storage.bundles import get_bundle_manifest, save_bundle_manifest
1818
from dss.storage.checkout import CheckoutError, TokenError
1919
from dss.storage.checkout.bundle import get_dst_bundle_prefix, verify_checkout
20-
from dss.storage.identifiers import BundleTombstoneID, BundleFQID, FileFQID
20+
from dss.storage.identifiers import BundleTombstoneID, FileFQID
2121
from dss.storage.hcablobstore import BundleFileMetadata, BundleMetadata, FileMetadata
22-
from dss.util import UrlBuilder, security, hashabledict, multipart_parallel_upload
22+
from dss.util import UrlBuilder, security, hashabledict
2323
from dss.util.version import datetime_to_version_format
2424

2525
"""The retry-after interval in seconds. Sets up downstream libraries / users to
@@ -221,26 +221,23 @@ def delete(uuid: str, replica: str, json_request_body: dict, version: str = None
221221
)
222222

223223
handle = Config.get_blobstore_handle(Replica[replica])
224-
if test_object_exists(handle, Replica[replica].bucket, bundle_prefix, test_type=ObjectTest.PREFIX):
225-
created, idempotent = idempotent_save(
226-
handle,
227-
Replica[replica].bucket,
228-
tombstone_id.to_key(),
229-
json.dumps(tombstone_object_data).encode("utf-8")
224+
if not test_object_exists(handle, Replica[replica].bucket, bundle_prefix, test_type=ObjectTest.PREFIX):
225+
raise DSSException(404, "not_found", "Cannot find bundle!")
226+
227+
created, idempotent = idempotent_save(
228+
handle,
229+
Replica[replica].bucket,
230+
tombstone_id.to_key(),
231+
json.dumps(tombstone_object_data).encode("utf-8")
232+
)
233+
if not idempotent:
234+
raise DSSException(
235+
requests.codes.conflict,
236+
f"bundle_tombstone_already_exists",
237+
f"bundle tombstone with UUID {uuid} and version {version} already exists",
230238
)
231-
if not idempotent:
232-
raise DSSException(
233-
requests.codes.conflict,
234-
f"bundle_tombstone_already_exists",
235-
f"bundle tombstone with UUID {uuid} and version {version} already exists",
236-
)
237-
status_code = requests.codes.ok
238-
response_body = dict() # type: dict
239-
else:
240-
status_code = requests.codes.not_found
241-
response_body = dict(title="bundle not found")
242239

243-
return jsonify(response_body), status_code
240+
return dict(), requests.codes.ok
244241

245242

246243
def build_bundle_file_metadata(replica: Replica, user_supplied_files: dict):

tests/test_bundle.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,10 @@ def _test_bundle_delete(self, replica: Replica, fixtures_bucket: str, authorized
714714
tombstone_exists = test_object_exists(handle, bucket, f"bundles/{bundle_uuid}.{bundle_version}.dead")
715715
self.assertEquals(tombstone_exists, authorized)
716716

717+
def test_delete_nonexistent(self):
718+
nonexistent_uuid = str(uuid.uuid4())
719+
self.delete_bundle(Replica.aws, nonexistent_uuid, authorized=True, expected_code=404)
720+
717721
def test_no_replica(self):
718722
"""
719723
Verify we raise the correct error code when we provide no replica.
@@ -846,7 +850,8 @@ def delete_bundle(
846850
replica: Replica,
847851
bundle_uuid: str,
848852
bundle_version: typing.Optional[str] = None,
849-
authorized: bool = True):
853+
authorized: bool = True,
854+
expected_code: typing.Optional[int] = None):
850855
# make delete request
851856
url_builder = UrlBuilder().set(path="/v1/bundles/" + bundle_uuid).add_query('replica', replica.name)
852857
if bundle_version:
@@ -857,14 +862,15 @@ def delete_bundle(
857862
if bundle_version:
858863
json_request_body['version'] = bundle_version
859864

860-
expected_code = requests.codes.ok if authorized else requests.codes.forbidden
865+
if not expected_code:
866+
expected_code = requests.codes.ok if authorized else requests.codes.forbidden
861867

862868
# delete and check results
863869
return self.assertDeleteResponse(
864870
url,
865871
expected_code,
866872
json_request_body=json_request_body,
867-
headers=get_auth_header(authorized=authorized),
873+
headers=get_auth_header(authorized=authorized)
868874
)
869875

870876
@lru_cache()

0 commit comments

Comments
 (0)