)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2954672d3374f6ca35bfd0e6423caba79a92e3b0","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"69c17673_4fbf719e","updated":"2024-01-02 15:42:22.000000000","message":"@Clay thanks for tidying this up. There was one last piece I hadn\u0027t finished on the original patch chain, now a follow on to this patch: https://review.opendev.org/c/openstack/swift/+/904497","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a3671221782f6105c8c249f9972068ba27b18d54","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":1,"id":"a9befd91_5538f4ff","updated":"2023-12-21 18:09:35.000000000","message":"this is a squash of all of the follow on and some drive bys\n\n904131: sq: cleanup test_obj.py | https://review.opendev.org/c/openstack/swift/+/904131\n904127: s3api: unify partNumber validation | https://review.opendev.org/c/openstack/swift/+/904127\n904137: WIP s3api: enforce configured max part num | https://review.opendev.org/c/openstack/swift/+/904137\n\nit\u0027s pretty much all Alistairs work, but I ended up with a stand-alone change-id because of a hicup with `git review --no-thin`\n\nMy goal is to get all of this squashed into:\n\n894580: s3api: Support GET/HEAD request with ?partNumber | https://review.opendev.org/c/openstack/swift/+/894580","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"}],"swift/common/middleware/s3api/s3request.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2954672d3374f6ca35bfd0e6423caba79a92e3b0","unresolved":true,"context_lines":[{"line_number":1457,"context_line":"            elif \u0027Part number must be an integer\u0027 in err_str:"},{"line_number":1458,"context_line":"                self.validate_part_number("},{"line_number":1459,"context_line":"                    max_parts\u003dself.conf.max_upload_part_num)"},{"line_number":1460,"context_line":"            raise InvalidRequest(msg\u003derr_str)"},{"line_number":1461,"context_line":"        if status \u003d\u003d HTTP_UNAUTHORIZED:"},{"line_number":1462,"context_line":"            raise SignatureDoesNotMatch("},{"line_number":1463,"context_line":"                **self.signature_does_not_match_kwargs())"}],"source_content_type":"text/x-python","patch_set":1,"id":"abcabf42_a7005126","line":1460,"updated":"2024-01-02 15:42:22.000000000","message":"ok, so this dedent takes care of the possibility that SLO thinks it\u0027s a bad request but validate_part_number doesn\u0027t (for some reason). Seems reasonably defensive, since we always want to raise a 400 out of this clause.","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"}],"test/s3api/__init__.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a3671221782f6105c8c249f9972068ba27b18d54","unresolved":true,"context_lines":[{"line_number":226,"context_line":"            if not bucket[\u0027Name\u0027].startswith(TEST_PREFIX):"},{"line_number":227,"context_line":"                # these tests run against real s3 accounts"},{"line_number":228,"context_line":"                continue"},{"line_number":229,"context_line":"            self.clear_bucket(client, bucket[\u0027Name\u0027])"},{"line_number":230,"context_line":""},{"line_number":231,"context_line":"    def tearDown(self):"},{"line_number":232,"context_line":"        client \u003d self.get_s3_client(1)"}],"source_content_type":"text/x-python","patch_set":1,"id":"3b52c884_c52f1cca","line":229,"updated":"2023-12-21 18:09:35.000000000","message":"this crap is all drive-by - I have a vendetta against over-use of classmethod/staticmethod","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2954672d3374f6ca35bfd0e6423caba79a92e3b0","unresolved":true,"context_lines":[{"line_number":226,"context_line":"            if not bucket[\u0027Name\u0027].startswith(TEST_PREFIX):"},{"line_number":227,"context_line":"                # these tests run against real s3 accounts"},{"line_number":228,"context_line":"                continue"},{"line_number":229,"context_line":"            self.clear_bucket(client, bucket[\u0027Name\u0027])"},{"line_number":230,"context_line":""},{"line_number":231,"context_line":"    def tearDown(self):"},{"line_number":232,"context_line":"        client \u003d self.get_s3_client(1)"}],"source_content_type":"text/x-python","patch_set":1,"id":"95c4817e_2e79f7c3","line":229,"in_reply_to":"3b52c884_c52f1cca","updated":"2024-01-02 15:42:22.000000000","message":"why do you consider it over-use? what\u0027s wrong with class methods? in this context it seems like it\u0027s correct and reasonable use. It\u0027s no big deal to me, but my IDE has a competing opposite vendetta and loves to warn me that methods could/should be static.","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"}],"test/unit/common/middleware/s3api/test_multi_get.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a3671221782f6105c8c249f9972068ba27b18d54","unresolved":true,"context_lines":[{"line_number":212,"context_line":"        with mock.patch.object(self.s3api.conf,"},{"line_number":213,"context_line":"                               \u0027max_upload_part_num\u0027, max_parts):"},{"line_number":214,"context_line":"            status, headers, body \u003d self.call_s3api(req)"},{"line_number":215,"context_line":"            self.assertEqual(status.split()[0], \u0027206\u0027)"},{"line_number":216,"context_line":"            # sanity"},{"line_number":217,"context_line":"            req.params \u003d {\u0027partNumber\u0027: str(part_number + 1)}"},{"line_number":218,"context_line":"            status, headers, body \u003d self.call_s3api(req)"}],"source_content_type":"text/x-python","patch_set":1,"id":"a5c36804_a333371c","line":215,"updated":"2023-12-21 18:09:35.000000000","message":"IIUC this essentially new behavior.  On current s3api-part-num you could have configured max_upload_part_num to 20K an created an 11K segment MPU; but ?partNumber\u003d10001 would have still failed because we rejected any partNumber \u003e 10000 (even on successfull slo responses)","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2954672d3374f6ca35bfd0e6423caba79a92e3b0","unresolved":true,"context_lines":[{"line_number":216,"context_line":"            # sanity"},{"line_number":217,"context_line":"            req.params \u003d {\u0027partNumber\u0027: str(part_number + 1)}"},{"line_number":218,"context_line":"            status, headers, body \u003d self.call_s3api(req)"},{"line_number":219,"context_line":"            self.assertIn(\u0027must be an integer between 1 and 2, inclusive\u0027,"},{"line_number":220,"context_line":"                          self._get_error_message(body))"},{"line_number":221,"context_line":""},{"line_number":222,"context_line":"    def test_mpu_GET_huge_part_num(self):"}],"source_content_type":"text/x-python","patch_set":1,"id":"4c69a212_225db20e","line":219,"range":{"start_line":219,"start_character":60,"end_line":219,"end_character":61},"updated":"2024-01-02 15:42:22.000000000","message":"we just verified that partNumber\u003d3 works so this message doesn\u0027t make sense - I\u0027d intended to push https://review.opendev.org/c/openstack/swift/+/904137 a little further to cap at max(configured max parts, actual number of parts)\n\nsee https://review.opendev.org/c/openstack/swift/+/904497","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a3671221782f6105c8c249f9972068ba27b18d54","unresolved":true,"context_lines":[{"line_number":217,"context_line":"            req.params \u003d {\u0027partNumber\u0027: str(part_number + 1)}"},{"line_number":218,"context_line":"            status, headers, body \u003d self.call_s3api(req)"},{"line_number":219,"context_line":"            self.assertIn(\u0027must be an integer between 1 and 2, inclusive\u0027,"},{"line_number":220,"context_line":"                          self._get_error_message(body))"},{"line_number":221,"context_line":""},{"line_number":222,"context_line":"    def test_mpu_GET_huge_part_num(self):"},{"line_number":223,"context_line":"        req \u003d swob.Request.blank(\u0027/bucket/mpu\u0027, params\u003d{"}],"source_content_type":"text/x-python","patch_set":1,"id":"ddb59007_8af185fc","line":220,"updated":"2023-12-21 18:09:35.000000000","message":"of course this is the only part that would have broke since the stubbed MPU bypasses the upload max_upload_part_num validation, so this would just 416","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"}],"test/unit/common/middleware/s3api/test_multi_upload.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a3671221782f6105c8c249f9972068ba27b18d54","unresolved":true,"context_lines":[{"line_number":687,"context_line":"        self.assertEqual(self._get_error_code(body), \u0027InvalidArgument\u0027)"},{"line_number":688,"context_line":"        self.assertEqual(self._get_error_message(body),"},{"line_number":689,"context_line":"                         \u0027Part number must be an integer between 1 and 10000, \u0027"},{"line_number":690,"context_line":"                         \u0027inclusive\u0027)"},{"line_number":691,"context_line":""},{"line_number":692,"context_line":"        # part number must be \u003e 0"},{"line_number":693,"context_line":"        req \u003d Request.blank(\u0027/bucket/object?partNumber\u003d0\u0026uploadId\u003dX\u0027,"}],"source_content_type":"text/x-python","patch_set":1,"id":"ed1bc6e3_2e4fb872","line":690,"updated":"2023-12-21 18:09:35.000000000","message":"maybe one of the reasons I was confused about the default value for max_upload_part_num is that our unittests are configured with a non-standard default\n\nI150cf53b07e7d2d8de1d6e8c1fb08c07b9afe842","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2954672d3374f6ca35bfd0e6423caba79a92e3b0","unresolved":true,"context_lines":[{"line_number":687,"context_line":"        self.assertEqual(self._get_error_code(body), \u0027InvalidArgument\u0027)"},{"line_number":688,"context_line":"        self.assertEqual(self._get_error_message(body),"},{"line_number":689,"context_line":"                         \u0027Part number must be an integer between 1 and 10000, \u0027"},{"line_number":690,"context_line":"                         \u0027inclusive\u0027)"},{"line_number":691,"context_line":""},{"line_number":692,"context_line":"        # part number must be \u003e 0"},{"line_number":693,"context_line":"        req \u003d Request.blank(\u0027/bucket/object?partNumber\u003d0\u0026uploadId\u003dX\u0027,"}],"source_content_type":"text/x-python","patch_set":1,"id":"df51dfed_e8eccf89","line":690,"in_reply_to":"ed1bc6e3_2e4fb872","updated":"2024-01-02 15:42:22.000000000","message":"yep, it\u0027s a little confusing!","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"}],"test/unit/common/middleware/s3api/test_obj.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"a3671221782f6105c8c249f9972068ba27b18d54","unresolved":true,"context_lines":[{"line_number":123,"context_line":"        self._test_object_GETorHEAD(\u0027GET\u0027)"},{"line_number":124,"context_line":""},{"line_number":125,"context_line":"    def test_object_HEAD(self):"},{"line_number":126,"context_line":"        self._test_object_GETorHEAD(\u0027HEAD\u0027)"},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"    def test_object_HEAD_error(self):"},{"line_number":129,"context_line":"        # HEAD does not return the body even an error response in the"}],"source_content_type":"text/x-python","patch_set":1,"id":"c7c67fc0_46b723d2","line":126,"updated":"2023-12-21 18:09:35.000000000","message":"pulling this in the s3acl/base coverage is also kind of a drive-by - but it works and provides useful coverage","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2954672d3374f6ca35bfd0e6423caba79a92e3b0","unresolved":true,"context_lines":[{"line_number":123,"context_line":"        self._test_object_GETorHEAD(\u0027GET\u0027)"},{"line_number":124,"context_line":""},{"line_number":125,"context_line":"    def test_object_HEAD(self):"},{"line_number":126,"context_line":"        self._test_object_GETorHEAD(\u0027HEAD\u0027)"},{"line_number":127,"context_line":""},{"line_number":128,"context_line":"    def test_object_HEAD_error(self):"},{"line_number":129,"context_line":"        # HEAD does not return the body even an error response in the"}],"source_content_type":"text/x-python","patch_set":1,"id":"8b65061a_d90aff51","line":126,"in_reply_to":"c7c67fc0_46b723d2","updated":"2024-01-02 15:42:22.000000000","message":"+1","commit_id":"47ba17fc7836ea5193ea7bef05e442833ea1490e"}]}
