)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"13963d6c0eaf3054353085f8f3c20f82e13d409d","unresolved":true,"context_lines":[{"line_number":13,"context_line":"- https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-s3-functionality-conditional-writes/"},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"Drive-By: Fix retry of a CompleteMultipartUpload with changed parts; it"},{"line_number":16,"context_line":"should 404 rather than succeed in writing the new manifest."},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"Change-Id: I2e57dacb342b5758f16b502bb91372a2443d0182"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":9,"id":"393495e7_95f2436c","line":16,"updated":"2025-05-19 17:22:58.000000000","message":"I\u0027d prefer to see this have its own commit message rather than a drive-by, it seems reasonably significant in itself. Or did this fix enable the conditional PUT fix?","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"f68ba1b019e1c75c4e22d685d0cc58189a8b2477","unresolved":false,"context_lines":[{"line_number":13,"context_line":"- https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-s3-functionality-conditional-writes/"},{"line_number":14,"context_line":""},{"line_number":15,"context_line":"Drive-By: Fix retry of a CompleteMultipartUpload with changed parts; it"},{"line_number":16,"context_line":"should 404 rather than succeed in writing the new manifest."},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"Change-Id: I2e57dacb342b5758f16b502bb91372a2443d0182"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":9,"id":"627c06b7_036afb0c","line":16,"in_reply_to":"393495e7_95f2436c","updated":"2025-05-19 23:44:51.000000000","message":"makes sense, I will make sure to request this too next time if I see similar drive-by fix in the review.","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"4ef38d8b27f3e8aa88be68cdb4131f670b672348","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"f3101aa0_86dce5d9","updated":"2025-05-15 15:43:52.000000000","message":"Simple but solid patch, now I realized swift support ``if-none-match`` long ago, how come AWS S3 uses same header name, did AWS S3 copy this feature from swift? ;-) \n\nI tested this patch on my local vasio, works as expected, could add more tests though.\n\n```\nvagrant@vagrant:~$ aws s3api put-object --bucket test --key test2 --body test --if-none-match \u0027*\u0027\n{\n    \"ETag\": \"\\\"ab51e0383317d5160c6072d4ff57e860\\\"\"\n}\nvagrant@vagrant:~$ aws s3api put-object --bucket test --key test2 --body test --if-none-match \u0027*\u0027\n\nAn error occurred (PreconditionFailed) when calling the PutObject operation: At least one of the preconditions you specified did not hold.\nvagrant@vagrant:~$ aws s3api put-object --bucket test --key test2 --body test --if-none-match \u0027ab51e0383317d5160c6072d4ff57e860\u0027\n\nAn error occurred (NotImplemented) when calling the PutObject operation: Conditional object PUTs are not supported.\n```","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"6b354e9a3718985d95fd69f45e5fc9612fd5452b","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":3,"id":"6e4f7961_07657589","in_reply_to":"f3101aa0_86dce5d9","updated":"2025-05-15 16:06:17.000000000","message":"\u003e how come AWS S3 uses same header name, did AWS S3 copy this feature from swift? ;-)\n\nIt\u0027s in the HTTP RFCs! https://www.rfc-editor.org/rfc/rfc2616#section-14.26","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"de82693ee837a3665edd3d34297abfdf1e95af93","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":6,"id":"823a9115_ed474d5b","updated":"2025-05-16 05:16:54.000000000","message":"with the changes in the ``POST`` of MPU controller, \"if-none-match \u003d etag\" returns ``NotImplemented`` which is consistent with normal object conditional write.\n\n```\nvagrant@vagrant:~$ aws s3api complete-multipart-upload --bucket test --key mputest2  --upload-id YjJmMWI2NzktNWVjOC00YmM1LThkOTgtMmFiYjUxOGUzYzlj --multipart-upload file://parts.json --if-none-match \"*\"\n\nAn error occurred (PreconditionFailed) when calling the CompleteMultipartUpload operation: At least one of the preconditions you specified did not hold.\nvagrant@vagrant:~$ aws s3api complete-multipart-upload --bucket test --key mputest2  --upload-id YjJmMWI2NzktNWVjOC00YmM1LThkOTgtMmFiYjUxOGUzYzlj --multipart-upload file://parts.json --if-none-match \"07f593208f8ac1c016ccd9e3fac2650f\"\n\nAn error occurred (NotImplemented) when calling the CompleteMultipartUpload operation: Conditional uploads are not supported.\n```\n\nhowever, I could use the same upload ID and repeatedly run ``complete-multipart-upload`` with ``--if-none-match \"*\"``, and they all returned success, I feel this is against the semantics of S3 conditional writes?\n\n```\nvagrant@vagrant:~$ aws s3api complete-multipart-upload --bucket test --key mputest2  --upload-id NDBiOTc0MzMtZWJhMy00ZTI1LTliY2YtZTU0OGQ0NDEyNjVi --multipart-upload file://parts.json --if-none-match \"*\"\n{\n    \"Location\": \"http://saio:8080/test/mputest2\",\n    \"Bucket\": \"test\",\n    \"Key\": \"mputest2\",\n    \"ETag\": \"\\\"07e923c3158d4415d44e36521dcf6953-2\\\"\"\n}\nvagrant@vagrant:~$ aws s3api complete-multipart-upload --bucket test --key mputest2  --upload-id NDBiOTc0MzMtZWJhMy00ZTI1LTliY2YtZTU0OGQ0NDEyNjVi --multipart-upload file://parts.json --if-none-match \"*\"\n{\n    \"Location\": \"http://saio:8080/test/mputest2\",\n    \"Bucket\": \"test\",\n    \"Key\": \"mputest2\",\n    \"ETag\": \"\\\"07e923c3158d4415d44e36521dcf6953-2\\\"\"\n}\nvagrant@vagrant:~$ aws s3api complete-multipart-upload --bucket test --key mputest2  --upload-id NDBiOTc0MzMtZWJhMy00ZTI1LTliY2YtZTU0OGQ0NDEyNjVi --multipart-upload file://parts.json --if-none-match \"*\"\n{\n    \"Location\": \"http://saio:8080/test/mputest2\",\n    \"Bucket\": \"test\",\n    \"Key\": \"mputest2\",\n    \"ETag\": \"\\\"07e923c3158d4415d44e36521dcf6953-2\\\"\"\n}\n```","commit_id":"ed17921f935383e2f8fc9726f88e0dd2f0ad2d6c"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"ab539e7e962907da70f6d9ddbf4e1ff21cfe0dd2","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":6,"id":"af6ec607_1945b538","in_reply_to":"022f77e4_f6fc003a","updated":"2025-05-16 18:55:07.000000000","message":"Good thought to test that scenario! I\u0027m of two minds -- on the one hand, clearly the object *is there already*, so we ought to 412; on the other, we\u0027re \"just\" retrying the request and should 200 if reasonable so the client fall into some error state unnecessarily.\n\nPoking at AWS, they seem to take the latter view; will include a cross-compat test to cover it.","commit_id":"ed17921f935383e2f8fc9726f88e0dd2f0ad2d6c"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"06ebca0874feec849fc428a16705e29f8d433b20","unresolved":true,"context_lines":[],"source_content_type":"","patch_set":6,"id":"022f77e4_f6fc003a","in_reply_to":"823a9115_ed474d5b","updated":"2025-05-16 15:33:55.000000000","message":"I tried this fix, works for me: https://review.opendev.org/c/openstack/swift/+/950198\n\n```\nvagrant@vagrant:~$ aws s3api complete-multipart-upload --bucket test --key mputest2  --upload-id NDBiOTc0MzMtZWJhMy00ZTI1LTliY2YtZTU0OGQ0NDEyNjVi --multipart-upload file://parts.json --if-none-match \"*\"\n\nAn error occurred (PreconditionFailed) when calling the CompleteMultipartUpload operation: At least one of the preconditions you specified did not hold.\n```","commit_id":"ed17921f935383e2f8fc9726f88e0dd2f0ad2d6c"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"3e1b487f034cd2f8873d43141442c8989eaa70bd","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":6,"id":"85bac7ff_cf8da0d9","in_reply_to":"af6ec607_1945b538","updated":"2025-05-19 17:15:02.000000000","message":"Tim and Al confirmed that aws s3 returns 200 in this case.","commit_id":"ed17921f935383e2f8fc9726f88e0dd2f0ad2d6c"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"13963d6c0eaf3054353085f8f3c20f82e13d409d","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":9,"id":"a78ce4cc_41301e90","updated":"2025-05-19 17:22:58.000000000","message":"LGTM but I think unit test coverage would be good.\n\nI question the necessity for the drive-by rather than a separate patch, but maybe I am missing the dependency?\n\nThe s3 compat tests and functional tests are already a bit bifurcated, but between them I think we have functional coverage.","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"3e1b487f034cd2f8873d43141442c8989eaa70bd","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":9,"id":"becedc4d_dd45274f","updated":"2025-05-19 17:15:02.000000000","message":"Very useful patch, swift supports conditional write already, let\u0027s enable it with s3api!","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"}],"doc/s3api/conf/ceph-known-failures-keystone.yaml":[{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"4ef38d8b27f3e8aa88be68cdb4131f670b672348","unresolved":false,"context_lines":[{"line_number":87,"context_line":"  s3tests.functional.test_s3.test_put_object_ifmatch_overwrite_existed_good: {status: KNOWN}"},{"line_number":88,"context_line":"  s3tests.functional.test_s3.test_put_object_ifnonmatch_failed: {status: KNOWN}"},{"line_number":89,"context_line":"  s3tests.functional.test_s3.test_put_object_ifnonmatch_good: {status: KNOWN}"},{"line_number":90,"context_line":"  s3tests.functional.test_s3.test_put_object_ifnonmatch_nonexisted_good: {status: KNOWN}"},{"line_number":91,"context_line":"  s3tests.functional.test_s3.test_put_object_ifnonmatch_overwrite_existed_failed: {status: KNOWN}"},{"line_number":92,"context_line":"  s3tests.functional.test_s3.test_set_cors: {status: KNOWN}"},{"line_number":93,"context_line":"  s3tests.functional.test_s3.test_stress_bucket_acls_changes: {status: KNOWN}"}],"source_content_type":"text/x-yaml","patch_set":3,"id":"2399240e_77a1f102","side":"PARENT","line":90,"updated":"2025-05-15 15:43:52.000000000","message":"okay, we are addding those tests back, and they passed Zuul pipeline!","commit_id":"6212869399d943305c8b1f2e0a20d9cd2cd71b75"}],"swift/common/middleware/s3api/controllers/multi_upload.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"13963d6c0eaf3054353085f8f3c20f82e13d409d","unresolved":true,"context_lines":[{"line_number":653,"context_line":"                h in req.headers for h in ("},{"line_number":654,"context_line":"                    \u0027If-Match\u0027, \u0027If-Modified-Since\u0027, \u0027If-Unmodified-Since\u0027)):"},{"line_number":655,"context_line":"            raise S3NotImplemented("},{"line_number":656,"context_line":"                \u0027Conditional uploads are not supported.\u0027)"},{"line_number":657,"context_line":""},{"line_number":658,"context_line":"        resp, is_marker \u003d _get_upload_info(req, self.app, upload_id)"},{"line_number":659,"context_line":"        if (is_marker and"}],"source_content_type":"text/x-python","patch_set":9,"id":"1ce9a62f_8b8b1996","line":656,"updated":"2025-05-19 17:22:58.000000000","message":"ok, and the marker is left intact so an abort can clean up unwanted parts (or the client can drop the If-None-Match header)","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"a89fae7712e5e5ce1bf99af29baebcd4337671ea","unresolved":true,"context_lines":[{"line_number":754,"context_line":"        elif already_uploaded_s3_etag:"},{"line_number":755,"context_line":"            # If the header\u0027s present but *doesn\u0027t* match, upload-id is"},{"line_number":756,"context_line":"            # no longer valid"},{"line_number":757,"context_line":"            raise NoSuchUpload(upload_id\u003dupload_id)"},{"line_number":758,"context_line":"        headers[s3_etag_header] \u003d s3_etag"},{"line_number":759,"context_line":"        # Leave base header value blank; SLO will populate"},{"line_number":760,"context_line":"        c_etag \u003d \u0027; s3_etag\u003d%s\u0027 % s3_etag"}],"source_content_type":"text/x-python","patch_set":9,"id":"eeb72b44_39239519","line":757,"updated":"2025-05-19 15:35:20.000000000","message":"there is ``upload_id`` along with NoSuchUpload error, but the test case probably is not able to test ``upload_id`` existing in the error, since the response body has no upload_id either.\n\n```\nb\"\u003c?xml version\u003d\u00271.0\u0027 encoding\u003d\u0027UTF-8\u0027?\u003e\\n\u003cError\u003e\u003cCode\u003eNoSuchUpload\u003c/Code\u003e\u003cMessage\u003eThe specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.\u003c/Message\u003e\u003cUploadId\u003eX\u003c/UploadId\u003e\u003c/Error\u003e\"\n```","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"}],"swift/common/middleware/s3api/s3request.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"13963d6c0eaf3054353085f8f3c20f82e13d409d","unresolved":true,"context_lines":[{"line_number":921,"context_line":"        if self.method \u003d\u003d \u0027PUT\u0027 and ("},{"line_number":922,"context_line":"                any(h in self.headers for h in ("},{"line_number":923,"context_line":"                    \u0027If-Match\u0027, \u0027If-Modified-Since\u0027, \u0027If-Unmodified-Since\u0027))"},{"line_number":924,"context_line":"                or self.headers.get(\u0027If-None-Match\u0027, \u0027*\u0027) !\u003d \u0027*\u0027):"},{"line_number":925,"context_line":"            raise S3NotImplemented("},{"line_number":926,"context_line":"                \u0027Conditional object PUTs are not supported.\u0027)"},{"line_number":927,"context_line":""}],"source_content_type":"text/x-python","patch_set":9,"id":"818a9355_7d3c7546","line":924,"updated":"2025-05-19 17:22:58.000000000","message":"wow, apparently no existing unit tests cover this change. I think it would be good to add one.","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"f68ba1b019e1c75c4e22d685d0cc58189a8b2477","unresolved":false,"context_lines":[{"line_number":921,"context_line":"        if self.method \u003d\u003d \u0027PUT\u0027 and ("},{"line_number":922,"context_line":"                any(h in self.headers for h in ("},{"line_number":923,"context_line":"                    \u0027If-Match\u0027, \u0027If-Modified-Since\u0027, \u0027If-Unmodified-Since\u0027))"},{"line_number":924,"context_line":"                or self.headers.get(\u0027If-None-Match\u0027, \u0027*\u0027) !\u003d \u0027*\u0027):"},{"line_number":925,"context_line":"            raise S3NotImplemented("},{"line_number":926,"context_line":"                \u0027Conditional object PUTs are not supported.\u0027)"},{"line_number":927,"context_line":""}],"source_content_type":"text/x-python","patch_set":9,"id":"7c9e93ef_1902e70b","line":924,"in_reply_to":"818a9355_7d3c7546","updated":"2025-05-19 23:44:51.000000000","message":"ACK, even though the functional test case ``test_put_object_conditional_requests`` covers here, but functional test is for end-to-end, we only can assume those ``501 Not Implemented`` are raised by the target changes but can\u0027t be 100% sure. added unit test cases to cover those cases to make sure those ``501 Not Implemented`` are raised by the changes within s3request.py.\n\nhttps://review.opendev.org/c/openstack/swift/+/950371/1/test/unit/common/middleware/s3api/test_s3request.py","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"}],"test/functional/s3api/test_multi_upload.py":[{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"de82693ee837a3665edd3d34297abfdf1e95af93","unresolved":false,"context_lines":[{"line_number":748,"context_line":"        for headers in ["},{"line_number":749,"context_line":"            {\u0027If-Match\u0027: part_etag},"},{"line_number":750,"context_line":"            {\u0027If-Match\u0027: \u0027*\u0027},"},{"line_number":751,"context_line":"            {\u0027If-None-Match\u0027: part_etag},"},{"line_number":752,"context_line":"            {\u0027If-Modified-Since\u0027: \u0027Wed, 21 Oct 2015 07:28:00 GMT\u0027},"},{"line_number":753,"context_line":"            {\u0027If-Unmodified-Since\u0027: \u0027Wed, 21 Oct 2015 07:28:00 GMT\u0027},"},{"line_number":754,"context_line":"        ]:"}],"source_content_type":"text/x-python","patch_set":6,"id":"83b23b5f_8df40dda","line":751,"updated":"2025-05-16 05:16:54.000000000","message":"Nice! those requests all will return ``501 NotImplemented`` now.","commit_id":"ed17921f935383e2f8fc9726f88e0dd2f0ad2d6c"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"13963d6c0eaf3054353085f8f3c20f82e13d409d","unresolved":true,"context_lines":[{"line_number":729,"context_line":"                                   query\u003dquery)"},{"line_number":730,"context_line":"        self.assertEqual(get_error_code(body), \u0027InvalidPart\u0027)"},{"line_number":731,"context_line":""},{"line_number":732,"context_line":"    def test_complete_multi_upload_conditional(self):"},{"line_number":733,"context_line":"        bucket \u003d \u0027bucket\u0027"},{"line_number":734,"context_line":"        key \u003d \u0027obj\u0027"},{"line_number":735,"context_line":"        self.conn.make_request(\u0027PUT\u0027, bucket)"}],"source_content_type":"text/x-python","patch_set":9,"id":"e09056c3_a7724867","line":732,"updated":"2025-05-19 17:22:58.000000000","message":"does this cover anything different than the compat test in test/s3api? The compat test DOES cover completing with different etag and SAME uploadId which I don\u0027t think this does, so I wonder if it is a superset?","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"f68ba1b019e1c75c4e22d685d0cc58189a8b2477","unresolved":false,"context_lines":[{"line_number":729,"context_line":"                                   query\u003dquery)"},{"line_number":730,"context_line":"        self.assertEqual(get_error_code(body), \u0027InvalidPart\u0027)"},{"line_number":731,"context_line":""},{"line_number":732,"context_line":"    def test_complete_multi_upload_conditional(self):"},{"line_number":733,"context_line":"        bucket \u003d \u0027bucket\u0027"},{"line_number":734,"context_line":"        key \u003d \u0027obj\u0027"},{"line_number":735,"context_line":"        self.conn.make_request(\u0027PUT\u0027, bucket)"}],"source_content_type":"text/x-python","patch_set":9,"id":"76b9ca95_a3815bf0","line":732,"in_reply_to":"e09056c3_a7724867","updated":"2025-05-19 23:44:51.000000000","message":"I think we can replace this test with unit tests.","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"}],"test/functional/s3api/test_object.py":[{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"4ef38d8b27f3e8aa88be68cdb4131f670b672348","unresolved":true,"context_lines":[{"line_number":339,"context_line":"        headers \u003d {\u0027If-None-Match\u0027: \u0027*\u0027}"},{"line_number":340,"context_line":"        status, headers, body \u003d \\"},{"line_number":341,"context_line":"            self.conn.make_request(\u0027PUT\u0027, self.bucket, obj, headers, content)"},{"line_number":342,"context_line":"        self.assertEqual(status, 200)"},{"line_number":343,"context_line":""},{"line_number":344,"context_line":"    def test_put_object_expect(self):"},{"line_number":345,"context_line":"        obj \u003d \u0027object\u0027"}],"source_content_type":"text/x-python","patch_set":3,"id":"62cb9672_d1017c30","line":342,"updated":"2025-05-15 15:43:52.000000000","message":"we could also add a rewrite test into functional test case to verify it will return ``412 PreconditionFailed``","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"de82693ee837a3665edd3d34297abfdf1e95af93","unresolved":false,"context_lines":[{"line_number":339,"context_line":"        headers \u003d {\u0027If-None-Match\u0027: \u0027*\u0027}"},{"line_number":340,"context_line":"        status, headers, body \u003d \\"},{"line_number":341,"context_line":"            self.conn.make_request(\u0027PUT\u0027, self.bucket, obj, headers, content)"},{"line_number":342,"context_line":"        self.assertEqual(status, 200)"},{"line_number":343,"context_line":""},{"line_number":344,"context_line":"    def test_put_object_expect(self):"},{"line_number":345,"context_line":"        obj \u003d \u0027object\u0027"}],"source_content_type":"text/x-python","patch_set":3,"id":"4975147d_23903457","line":342,"in_reply_to":"1473a884_c649d615","updated":"2025-05-16 05:16:54.000000000","message":"Done","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"6b354e9a3718985d95fd69f45e5fc9612fd5452b","unresolved":true,"context_lines":[{"line_number":339,"context_line":"        headers \u003d {\u0027If-None-Match\u0027: \u0027*\u0027}"},{"line_number":340,"context_line":"        status, headers, body \u003d \\"},{"line_number":341,"context_line":"            self.conn.make_request(\u0027PUT\u0027, self.bucket, obj, headers, content)"},{"line_number":342,"context_line":"        self.assertEqual(status, 200)"},{"line_number":343,"context_line":""},{"line_number":344,"context_line":"    def test_put_object_expect(self):"},{"line_number":345,"context_line":"        obj \u003d \u0027object\u0027"}],"source_content_type":"text/x-python","patch_set":3,"id":"1473a884_c649d615","line":342,"in_reply_to":"62cb9672_d1017c30","updated":"2025-05-15 16:06:17.000000000","message":"Good call. Will add.","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"424fc4c14fba0f74cdb62c4f08a2656f79c582bc","unresolved":true,"context_lines":[{"line_number":343,"context_line":""},{"line_number":344,"context_line":"        # And the if-none-match prevents overwrites"},{"line_number":345,"context_line":"        status, headers, body \u003d \\"},{"line_number":346,"context_line":"            self.conn.make_request(\u0027PUT\u0027, self.bucket, obj, headers, content)"},{"line_number":347,"context_line":"        self.assertEqual(status, 412)"},{"line_number":348,"context_line":""},{"line_number":349,"context_line":"    def test_put_object_expect(self):"}],"source_content_type":"text/x-python","patch_set":4,"id":"5dc63d0d_76afc957","line":346,"range":{"start_line":346,"start_character":60,"end_line":346,"end_character":67},"updated":"2025-05-15 20:08:24.000000000","message":"Sigh. These are now the previous response headers, not the `If-None-Match` header I was expecting...","commit_id":"1c93d9cdee054b475a49cd10975451cea5a80b9d"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"de82693ee837a3665edd3d34297abfdf1e95af93","unresolved":false,"context_lines":[{"line_number":343,"context_line":""},{"line_number":344,"context_line":"        # And the if-none-match prevents overwrites"},{"line_number":345,"context_line":"        status, headers, body \u003d \\"},{"line_number":346,"context_line":"            self.conn.make_request(\u0027PUT\u0027, self.bucket, obj, headers, content)"},{"line_number":347,"context_line":"        self.assertEqual(status, 412)"},{"line_number":348,"context_line":""},{"line_number":349,"context_line":"    def test_put_object_expect(self):"}],"source_content_type":"text/x-python","patch_set":4,"id":"3e254f82_6a873703","line":346,"range":{"start_line":346,"start_character":60,"end_line":346,"end_character":67},"in_reply_to":"5dc63d0d_76afc957","updated":"2025-05-16 05:16:54.000000000","message":"Done","commit_id":"1c93d9cdee054b475a49cd10975451cea5a80b9d"}],"test/s3api/test_conditional_writes.py":[{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"4ef38d8b27f3e8aa88be68cdb4131f670b672348","unresolved":true,"context_lines":[{"line_number":31,"context_line":"            Body\u003db\u0027\u0027,"},{"line_number":32,"context_line":"        )"},{"line_number":33,"context_line":"        self.assertEqual(200, resp[\u0027ResponseMetadata\u0027][\u0027HTTPStatusCode\u0027])"},{"line_number":34,"context_line":"        # But overwrite is blocked"},{"line_number":35,"context_line":"        with self.assertRaises(ClientError) as caught:"},{"line_number":36,"context_line":"            client.put_object("},{"line_number":37,"context_line":"                Bucket\u003dself.bucket_name,"}],"source_content_type":"text/x-python","patch_set":3,"id":"7d2c149f_cbd25c3b","line":34,"updated":"2025-05-15 15:43:52.000000000","message":"we could add test case of ``If-None-Match: \u003cetag\u003e`` to explicitly show it is not supported.","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"6b354e9a3718985d95fd69f45e5fc9612fd5452b","unresolved":true,"context_lines":[{"line_number":31,"context_line":"            Body\u003db\u0027\u0027,"},{"line_number":32,"context_line":"        )"},{"line_number":33,"context_line":"        self.assertEqual(200, resp[\u0027ResponseMetadata\u0027][\u0027HTTPStatusCode\u0027])"},{"line_number":34,"context_line":"        # But overwrite is blocked"},{"line_number":35,"context_line":"        with self.assertRaises(ClientError) as caught:"},{"line_number":36,"context_line":"            client.put_object("},{"line_number":37,"context_line":"                Bucket\u003dself.bucket_name,"}],"source_content_type":"text/x-python","patch_set":3,"id":"cd28b741_11cb2966","line":34,"in_reply_to":"7d2c149f_cbd25c3b","updated":"2025-05-15 16:06:17.000000000","message":"We\u0027d want to do that under `test/functional` though, as AWS *does* support it (IIUC).","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"36d699cc265e8b115a278c9bb1a0539eb6076a32","unresolved":true,"context_lines":[{"line_number":31,"context_line":"            Body\u003db\u0027\u0027,"},{"line_number":32,"context_line":"        )"},{"line_number":33,"context_line":"        self.assertEqual(200, resp[\u0027ResponseMetadata\u0027][\u0027HTTPStatusCode\u0027])"},{"line_number":34,"context_line":"        # But overwrite is blocked"},{"line_number":35,"context_line":"        with self.assertRaises(ClientError) as caught:"},{"line_number":36,"context_line":"            client.put_object("},{"line_number":37,"context_line":"                Bucket\u003dself.bucket_name,"}],"source_content_type":"text/x-python","patch_set":3,"id":"a84547a9_d7a3f9fc","line":34,"in_reply_to":"7d2c149f_cbd25c3b","updated":"2025-05-15 20:37:47.000000000","message":"put-object with ``if-none-match \u003d etag`` returns ``NotImplemented``\n```\nvagrant@vagrant:~$ aws s3api put-object --bucket test --key test2 --body test --if-none-match \u0027*\u0027\n{\n    \"ETag\": \"\\\"ab51e0383317d5160c6072d4ff57e860\\\"\"\n}\nvagrant@vagrant:~$ aws s3api put-object --bucket test --key test2 --body test --if-none-match \u0027*\u0027\n\nAn error occurred (PreconditionFailed) when calling the PutObject operation: At least one of the preconditions you specified did not hold.\nvagrant@vagrant:~$ aws s3api put-object --bucket test --key test2 --body test --if-none-match \u0027ab51e0383317d5160c6072d4ff57e860\u0027\n\nAn error occurred (NotImplemented) when calling the PutObject operation: Conditional object PUTs are not supported.\n```","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"de82693ee837a3665edd3d34297abfdf1e95af93","unresolved":false,"context_lines":[{"line_number":31,"context_line":"            Body\u003db\u0027\u0027,"},{"line_number":32,"context_line":"        )"},{"line_number":33,"context_line":"        self.assertEqual(200, resp[\u0027ResponseMetadata\u0027][\u0027HTTPStatusCode\u0027])"},{"line_number":34,"context_line":"        # But overwrite is blocked"},{"line_number":35,"context_line":"        with self.assertRaises(ClientError) as caught:"},{"line_number":36,"context_line":"            client.put_object("},{"line_number":37,"context_line":"                Bucket\u003dself.bucket_name,"}],"source_content_type":"text/x-python","patch_set":3,"id":"7ca90cb1_c7dab696","line":34,"in_reply_to":"a84547a9_d7a3f9fc","updated":"2025-05-16 05:16:54.000000000","message":"Done","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"4ef38d8b27f3e8aa88be68cdb4131f670b672348","unresolved":true,"context_lines":[{"line_number":90,"context_line":"            \u0027ETag\u0027: part_resp[\u0027ETag\u0027],"},{"line_number":91,"context_line":"            \u0027PartNumber\u0027: 1,"},{"line_number":92,"context_line":"        }]"},{"line_number":93,"context_line":"        # But completion will be blocked"},{"line_number":94,"context_line":"        with self.assertRaises(ClientError) as caught:"},{"line_number":95,"context_line":"            client.complete_multipart_upload("},{"line_number":96,"context_line":"                Bucket\u003dself.bucket_name,"}],"source_content_type":"text/x-python","patch_set":3,"id":"bc9cea36_a03af9bd","line":93,"updated":"2025-05-15 15:43:52.000000000","message":"test case of ``If-None-Match: \u003cetag\u003e`` for MPU too","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"de82693ee837a3665edd3d34297abfdf1e95af93","unresolved":false,"context_lines":[{"line_number":90,"context_line":"            \u0027ETag\u0027: part_resp[\u0027ETag\u0027],"},{"line_number":91,"context_line":"            \u0027PartNumber\u0027: 1,"},{"line_number":92,"context_line":"        }]"},{"line_number":93,"context_line":"        # But completion will be blocked"},{"line_number":94,"context_line":"        with self.assertRaises(ClientError) as caught:"},{"line_number":95,"context_line":"            client.complete_multipart_upload("},{"line_number":96,"context_line":"                Bucket\u003dself.bucket_name,"}],"source_content_type":"text/x-python","patch_set":3,"id":"a01b359a_0322349e","line":93,"in_reply_to":"01326868_7b721b82","updated":"2025-05-16 05:16:54.000000000","message":"Done","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"36d699cc265e8b115a278c9bb1a0539eb6076a32","unresolved":true,"context_lines":[{"line_number":90,"context_line":"            \u0027ETag\u0027: part_resp[\u0027ETag\u0027],"},{"line_number":91,"context_line":"            \u0027PartNumber\u0027: 1,"},{"line_number":92,"context_line":"        }]"},{"line_number":93,"context_line":"        # But completion will be blocked"},{"line_number":94,"context_line":"        with self.assertRaises(ClientError) as caught:"},{"line_number":95,"context_line":"            client.complete_multipart_upload("},{"line_number":96,"context_line":"                Bucket\u003dself.bucket_name,"}],"source_content_type":"text/x-python","patch_set":3,"id":"01326868_7b721b82","line":93,"in_reply_to":"bc9cea36_a03af9bd","updated":"2025-05-15 20:37:47.000000000","message":"while complete-multipart-upload with ``if-none-match \u003d etag`` returns ``InvalidRequest``\n\n```\nvagrant@vagrant:~$ aws s3api complete-multipart-upload --bucket test --key mputest --upload-id YzA1MGEzN2ItMjU3Yi00NzQ1LTkwYmQtMjAwNDQwMDkwMzlm --multipart-upload file://parts.json --if-none-match \"07f593208f8ac1c016ccd9e3fac2650f\"\n\nAn error occurred (InvalidRequest) when calling the CompleteMultipartUpload operation: Invalid Request.\n```\nthis inconsistency pre-existed in the master branch:\n\n```\nvagrant@vagrant:~$ aws s3api put-object --bucket test --key test2 --body test --if-none-match \u0027*\u0027\n\nAn error occurred (NotImplemented) when calling the PutObject operation: Conditional object PUTs are not supported.\nwhile complete-multipart-upload returns InvalidRequest\nvagrant@vagrant:~$ aws s3api complete-multipart-upload --bucket test --key mputest --upload-id YzA1MGEzN2ItMjU3Yi00NzQ1LTkwYmQtMjAwNDQwMDkwMzlm --multipart-upload file://parts.json --if-none-match \"*\"\n\nAn error occurred (InvalidRequest) when calling the CompleteMultipartUpload operation: Invalid Request.\nvagrant@vagrant:~$ aws s3api complete-multipart-upload --bucket test --key mputest --upload-id YzA1MGEzN2ItMjU3Yi00NzQ1LTkwYmQtMjAwNDQwMDkwMzlm --multipart-upload file://parts.json --if-none-match \"07f593208f8ac1c016ccd9e3fac2650f\"\n\nAn error occurred (InvalidRequest) when calling the CompleteMultipartUpload operation: Invalid Request.\n```\n\nSo this patch keeps those return as before, I think it\u0027s fine given those two return code are kind of similar too. if we want to fix this inconsistency, that can be a follow-on patch.","commit_id":"667ee3f26edeb47864b1fc270ee63043fcc8683b"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"ab539e7e962907da70f6d9ddbf4e1ff21cfe0dd2","unresolved":true,"context_lines":[{"line_number":26,"context_line":"        resp \u003d client.put_object("},{"line_number":27,"context_line":"            Bucket\u003dself.bucket_name,"},{"line_number":28,"context_line":"            Key\u003dkey_name,"},{"line_number":29,"context_line":"            ChecksumCRC32\u003d\u0027AAAAAA\u003d\u003d\u0027,"},{"line_number":30,"context_line":"            IfNoneMatch\u003d\u0027*\u0027,"},{"line_number":31,"context_line":"            Body\u003db\u0027\u0027,"},{"line_number":32,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":6,"id":"fa274840_60bc7426","line":29,"updated":"2025-05-16 18:55:07.000000000","message":"Why\u0027d I include checksums? I think I must\u0027ve just had checksums on my mind...","commit_id":"ed17921f935383e2f8fc9726f88e0dd2f0ad2d6c"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"ae2fa2fb8dccf26d1bf21d149235cd8493755288","unresolved":false,"context_lines":[{"line_number":26,"context_line":"        resp \u003d client.put_object("},{"line_number":27,"context_line":"            Bucket\u003dself.bucket_name,"},{"line_number":28,"context_line":"            Key\u003dkey_name,"},{"line_number":29,"context_line":"            ChecksumCRC32\u003d\u0027AAAAAA\u003d\u003d\u0027,"},{"line_number":30,"context_line":"            IfNoneMatch\u003d\u0027*\u0027,"},{"line_number":31,"context_line":"            Body\u003db\u0027\u0027,"},{"line_number":32,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":6,"id":"2b691032_707654fe","line":29,"in_reply_to":"fa274840_60bc7426","updated":"2025-05-16 22:34:56.000000000","message":"Done","commit_id":"ed17921f935383e2f8fc9726f88e0dd2f0ad2d6c"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"ec8bc4377554acceb32f2e9de65cf95d620b9979","unresolved":true,"context_lines":[{"line_number":85,"context_line":"                IfNoneMatch\u003d\u0027*\u0027,"},{"line_number":86,"context_line":"            )"},{"line_number":87,"context_line":"        self.assertEqual(404, status_from_error(caught.exception))"},{"line_number":88,"context_line":"        self.assertEqual(\u0027NoSuchUpload\u0027,"},{"line_number":89,"context_line":"                         code_from_error(caught.exception))"},{"line_number":90,"context_line":""},{"line_number":91,"context_line":"        # Ditto fewer"}],"source_content_type":"text/x-python","patch_set":7,"id":"1f57cf18_43220da5","line":88,"updated":"2025-05-16 18:58:39.000000000","message":"😬 I\u0027m not actually sure we handle this case correctly yet...","commit_id":"837617e66dec2c48269a9e662b6a3b5853a04e51"},{"author":{"_account_id":15343,"name":"Tim Burke","email":"tburke@nvidia.com","username":"tburke"},"change_message_id":"ae2fa2fb8dccf26d1bf21d149235cd8493755288","unresolved":true,"context_lines":[{"line_number":85,"context_line":"                IfNoneMatch\u003d\u0027*\u0027,"},{"line_number":86,"context_line":"            )"},{"line_number":87,"context_line":"        self.assertEqual(404, status_from_error(caught.exception))"},{"line_number":88,"context_line":"        self.assertEqual(\u0027NoSuchUpload\u0027,"},{"line_number":89,"context_line":"                         code_from_error(caught.exception))"},{"line_number":90,"context_line":""},{"line_number":91,"context_line":"        # Ditto fewer"}],"source_content_type":"text/x-python","patch_set":7,"id":"9110d4aa_287b1a22","line":88,"in_reply_to":"1ece781b_35391f32","updated":"2025-05-16 22:34:56.000000000","message":"\u003e probably we will have run this test against s3.\n\nThat\u0027s how I discovered it. 😉\n\nThis should all pass against AWS.","commit_id":"837617e66dec2c48269a9e662b6a3b5853a04e51"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"d499ddc9b610a6b69529fb0d759cc7e1b653f9ee","unresolved":true,"context_lines":[{"line_number":85,"context_line":"                IfNoneMatch\u003d\u0027*\u0027,"},{"line_number":86,"context_line":"            )"},{"line_number":87,"context_line":"        self.assertEqual(404, status_from_error(caught.exception))"},{"line_number":88,"context_line":"        self.assertEqual(\u0027NoSuchUpload\u0027,"},{"line_number":89,"context_line":"                         code_from_error(caught.exception))"},{"line_number":90,"context_line":""},{"line_number":91,"context_line":"        # Ditto fewer"}],"source_content_type":"text/x-python","patch_set":7,"id":"1ece781b_35391f32","line":88,"in_reply_to":"1f57cf18_43220da5","updated":"2025-05-16 19:39:21.000000000","message":"maybe the current behavior is correct to continue a new complete-multipart-upload? if user has re-uploaded one part with different content, user would want the new ``upload`` with same upload id to succeed. probably we will have run this test against s3.","commit_id":"837617e66dec2c48269a9e662b6a3b5853a04e51"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"a89fae7712e5e5ce1bf99af29baebcd4337671ea","unresolved":false,"context_lines":[{"line_number":85,"context_line":"                IfNoneMatch\u003d\u0027*\u0027,"},{"line_number":86,"context_line":"            )"},{"line_number":87,"context_line":"        self.assertEqual(404, status_from_error(caught.exception))"},{"line_number":88,"context_line":"        self.assertEqual(\u0027NoSuchUpload\u0027,"},{"line_number":89,"context_line":"                         code_from_error(caught.exception))"},{"line_number":90,"context_line":""},{"line_number":91,"context_line":"        # Ditto fewer"}],"source_content_type":"text/x-python","patch_set":7,"id":"bbb29e8f_fc210029","line":88,"in_reply_to":"9110d4aa_287b1a22","updated":"2025-05-19 15:35:20.000000000","message":"Done","commit_id":"837617e66dec2c48269a9e662b6a3b5853a04e51"}],"test/unit/common/middleware/s3api/test_multi_upload.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"13963d6c0eaf3054353085f8f3c20f82e13d409d","unresolved":true,"context_lines":[{"line_number":1601,"context_line":"            (\u0027HEAD\u0027, \u0027/v1/AUTH_test/bucket+segments/object/X\u0027),"},{"line_number":1602,"context_line":"            # But the object does, and with the same upload ID"},{"line_number":1603,"context_line":"            (\u0027HEAD\u0027, \u0027/v1/AUTH_test/bucket/object\u0027),"},{"line_number":1604,"context_line":"            # And then we bail"},{"line_number":1605,"context_line":"        ])"},{"line_number":1606,"context_line":"        self.assertEqual(req.environ[\u0027swift.backend_path\u0027],"},{"line_number":1607,"context_line":"                         \u0027/v1/AUTH_test/bucket+segments/object/X\u0027)"}],"source_content_type":"text/x-python","patch_set":9,"id":"e7fdec71_f018ccca","line":1604,"updated":"2025-05-19 17:22:58.000000000","message":"is there no unit tests coverage for the conditional mpu complete?","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"},{"author":{"_account_id":34930,"name":"Jianjian Huo","email":"jhuo@nvidia.com","username":"jhuo"},"change_message_id":"f68ba1b019e1c75c4e22d685d0cc58189a8b2477","unresolved":false,"context_lines":[{"line_number":1601,"context_line":"            (\u0027HEAD\u0027, \u0027/v1/AUTH_test/bucket+segments/object/X\u0027),"},{"line_number":1602,"context_line":"            # But the object does, and with the same upload ID"},{"line_number":1603,"context_line":"            (\u0027HEAD\u0027, \u0027/v1/AUTH_test/bucket/object\u0027),"},{"line_number":1604,"context_line":"            # And then we bail"},{"line_number":1605,"context_line":"        ])"},{"line_number":1606,"context_line":"        self.assertEqual(req.environ[\u0027swift.backend_path\u0027],"},{"line_number":1607,"context_line":"                         \u0027/v1/AUTH_test/bucket+segments/object/X\u0027)"}],"source_content_type":"text/x-python","patch_set":9,"id":"de11b71d_e92abfa0","line":1604,"in_reply_to":"e7fdec71_f018ccca","updated":"2025-05-19 23:44:51.000000000","message":"yeah, the new ``test/s3api/test_conditional_writes.py`` uses boto3 for end-to-end testing, we would need unit test to verify this as well; also added more test combinations:\n\nhttps://review.opendev.org/c/openstack/swift/+/950371/1/test/unit/common/middleware/s3api/test_multi_upload.py","commit_id":"edd5eb29d7d6041ae0b16b78cbcb89f9dd95309f"}]}
