)]}'
{"/PATCHSET_LEVEL":[{"author":{"_account_id":6968,"name":"Christian Schwede","email":"cschwede@nvidia.com","username":"cschwede"},"change_message_id":"6dab9fe77a4f91c652704ab97346de0fd074a7f5","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":5,"id":"b18a4959_9ca4d7f0","updated":"2026-08-24 08:31:18.000000000","message":"recheck","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":5,"id":"fe420850_4b55775e","updated":"2026-08-22 02:04:47.000000000","message":"ummm... I think I like this change!  idk, maybe we should 🚢 - my brain is fried tho","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":6,"id":"f03d9b20_20ea967d","updated":"2026-08-24 21:33:36.000000000","message":"I\u0027ll work on rebasing the slo copy hook follow-up:\n\n1001905: WIP: let SLO own copy sysmeta cleanup | https://review.opendev.org/c/openstack/swift/+/1001905\n\n... ontop of the expanded probe tests:\n\n1002201: probe tests: extend SLO/MPU copy probes | https://review.opendev.org/c/openstack/swift/+/1002201","commit_id":"a43a5f4edc99ececfb903da6665c0abe9a564ea1"},{"author":{"_account_id":6968,"name":"Christian Schwede","email":"cschwede@nvidia.com","username":"cschwede"},"change_message_id":"f2388977d334ff98e9aa16b4fe94c79665c004ea","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":6,"id":"0bf5348d_4d9bded5","updated":"2026-08-24 18:45:07.000000000","message":"LGTM now.\n\nThe change from PS5 is mostly around `register_copy_source_hook`, Clay already added his +2 earlier on - so I think this is ready to be merged.","commit_id":"a43a5f4edc99ececfb903da6665c0abe9a564ea1"}],"swift/common/middleware/copy.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":140,"context_line":""},{"line_number":141,"context_line":"More than one middleware may wish to register a callback, so a middleware"},{"line_number":142,"context_line":"that registers a callback must first stash any callback already found in the"},{"line_number":143,"context_line":"environ, and then call that callback from its own callback, for example::"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":"    previous_hook \u003d env.get(\u0027swift.callback.copy_source_hook\u0027)"},{"line_number":146,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"2d3c7fc4_edf35230","line":143,"updated":"2026-08-22 02:04:47.000000000","message":"as an alternative to documenting this requirement we could provide a helper:\n\n```\n  def register_copy_source_hook(env, hook):\n      previous \u003d env.get(\u0027swift.callback.copy_source_hook\u0027)\n\n      def chained(req, source_resp, sink_req):\n          if previous:\n              previous(req, source_resp, sink_req)\n          hook(req, source_resp, sink_req)\n\n      env[\u0027swift.callback.copy_source_hook\u0027] \u003d chained\n```\n\nand consumers just:\n\n```\n  register_copy_source_hook(environ, s3api_copy_hook)\n```","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":140,"context_line":""},{"line_number":141,"context_line":"More than one middleware may wish to register a callback, so a middleware"},{"line_number":142,"context_line":"that registers a callback must first stash any callback already found in the"},{"line_number":143,"context_line":"environ, and then call that callback from its own callback, for example::"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":"    previous_hook \u003d env.get(\u0027swift.callback.copy_source_hook\u0027)"},{"line_number":146,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"3247c243_c3c13530","line":143,"in_reply_to":"2d3c7fc4_edf35230","updated":"2026-08-24 15:16:49.000000000","message":"I like this pattern: repeated common behaviour and naming encapsulated in a helper.\n\nI\u0027d advocate doing it for other callbacks too e.g. footers callback is getting used again in upcoming checksum persistence, and we\u0027re adding a manifest hook too.","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":140,"context_line":""},{"line_number":141,"context_line":"More than one middleware may wish to register a callback, so a middleware"},{"line_number":142,"context_line":"that registers a callback must first stash any callback already found in the"},{"line_number":143,"context_line":"environ, and then call that callback from its own callback, for example::"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":"    previous_hook \u003d env.get(\u0027swift.callback.copy_source_hook\u0027)"},{"line_number":146,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"079c15b9_c5fa01ff","line":143,"in_reply_to":"3247c243_c3c13530","updated":"2026-08-24 21:33:36.000000000","message":"Done","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":142,"context_line":"that registers a callback must first stash any callback already found in the"},{"line_number":143,"context_line":"environ, and then call that callback from its own callback, for example::"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":"    previous_hook \u003d env.get(\u0027swift.callback.copy_source_hook\u0027)"},{"line_number":146,"context_line":""},{"line_number":147,"context_line":"    def my_hook(req, source_resp, sink_req):"},{"line_number":148,"context_line":"        if previous_hook:"}],"source_content_type":"text/x-python","patch_set":5,"id":"fcb452c0_78689697","line":145,"updated":"2026-08-22 02:04:47.000000000","message":"FWIW swift/common/middleware/crypto/encrypter.py’s install_footers_callback() calls it \"inner\":\n\n```\n  inner_callback \u003d req.environ.get(\u0027swift.callback.update_footers\u0027)\n```\n\n... but it seems pretty academic - I couldn\u0027t find an EXAMPLE of crypto actually needing to compose another mw\u0027s footers out-side of tests","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":142,"context_line":"that registers a callback must first stash any callback already found in the"},{"line_number":143,"context_line":"environ, and then call that callback from its own callback, for example::"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":"    previous_hook \u003d env.get(\u0027swift.callback.copy_source_hook\u0027)"},{"line_number":146,"context_line":""},{"line_number":147,"context_line":"    def my_hook(req, source_resp, sink_req):"},{"line_number":148,"context_line":"        if previous_hook:"}],"source_content_type":"text/x-python","patch_set":5,"id":"baa90784_ef2b2e18","line":145,"in_reply_to":"72249739_c13e6278","updated":"2026-08-24 21:33:36.000000000","message":"Acknowledged","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":142,"context_line":"that registers a callback must first stash any callback already found in the"},{"line_number":143,"context_line":"environ, and then call that callback from its own callback, for example::"},{"line_number":144,"context_line":""},{"line_number":145,"context_line":"    previous_hook \u003d env.get(\u0027swift.callback.copy_source_hook\u0027)"},{"line_number":146,"context_line":""},{"line_number":147,"context_line":"    def my_hook(req, source_resp, sink_req):"},{"line_number":148,"context_line":"        if previous_hook:"}],"source_content_type":"text/x-python","patch_set":5,"id":"72249739_c13e6278","line":145,"in_reply_to":"fcb452c0_78689697","updated":"2026-08-24 15:16:49.000000000","message":"...and over here https://review.opendev.org/c/openstack/swift/+/991516/79/swift/common/middleware/s3api/s3request.py in ``_install_checksum_footers_callback`` we have ``original_callback`` :)\n\n...more reason to enshrine in helpers","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":242,"context_line":"        if not out_content_type:"},{"line_number":243,"context_line":"            out_content_type \u003d \u0027text/plain\u0027"},{"line_number":244,"context_line":""},{"line_number":245,"context_line":"        if heartbeat:"},{"line_number":246,"context_line":"            gt \u003d eventlet.spawn(self._app_call,"},{"line_number":247,"context_line":"                                req.environ)"},{"line_number":248,"context_line":"            start_response(\u0027202 Accepted\u0027,"}],"source_content_type":"text/x-python","patch_set":5,"id":"8e08ef8a_e67e9588","line":245,"updated":"2026-08-22 02:04:47.000000000","message":"the coupling with the heartbeat handling in the caller `handle_PUT` is mind-bending\n\nsource_resp.app_iter ends up jointly “sort of owned” by handle_PUT(), a returned response generator, and a background green thread.","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":242,"context_line":"        if not out_content_type:"},{"line_number":243,"context_line":"            out_content_type \u003d \u0027text/plain\u0027"},{"line_number":244,"context_line":""},{"line_number":245,"context_line":"        if heartbeat:"},{"line_number":246,"context_line":"            gt \u003d eventlet.spawn(self._app_call,"},{"line_number":247,"context_line":"                                req.environ)"},{"line_number":248,"context_line":"            start_response(\u0027202 Accepted\u0027,"}],"source_content_type":"text/x-python","patch_set":5,"id":"10daa2d6_9cf5e0f2","line":245,"in_reply_to":"6b9fe007_60ef8ac7","updated":"2026-08-24 21:33:36.000000000","message":"Acknowledged","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":242,"context_line":"        if not out_content_type:"},{"line_number":243,"context_line":"            out_content_type \u003d \u0027text/plain\u0027"},{"line_number":244,"context_line":""},{"line_number":245,"context_line":"        if heartbeat:"},{"line_number":246,"context_line":"            gt \u003d eventlet.spawn(self._app_call,"},{"line_number":247,"context_line":"                                req.environ)"},{"line_number":248,"context_line":"            start_response(\u0027202 Accepted\u0027,"}],"source_content_type":"text/x-python","patch_set":5,"id":"6b9fe007_60ef8ac7","line":245,"in_reply_to":"8e08ef8a_e67e9588","updated":"2026-08-24 15:16:49.000000000","message":"indeed","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":467,"context_line":"                                           self.yield_frequency)"},{"line_number":468,"context_line":"        source_resp \u003d self._get_source_object(ssc_ctx, source_path, req)"},{"line_number":469,"context_line":"        if source_resp.status_int \u003e\u003d HTTP_MULTIPLE_CHOICES:"},{"line_number":470,"context_line":"            return source_resp(source_resp.environ, start_response)"},{"line_number":471,"context_line":""},{"line_number":472,"context_line":"        # Create a new Request object based on the original request instance."},{"line_number":473,"context_line":"        # This will preserve original request environ including headers."}],"source_content_type":"text/x-python","patch_set":5,"id":"8aa2961d_bbc43164","line":470,"updated":"2026-08-22 02:04:47.000000000","message":"really I think outside of this early return the rest of this whole method should:\n\n```\n...\nfinally:\n    close_if_possible(source_resp.app_iter)\n```\n\nbut it\u0027s tricky:\n\n\n  Conceptually:\n\n```\n  def run_destination_put(sink_req, source_resp):\n      with closing_if_possible(source_resp.app_iter):\n          return self._app_call(sink_req.environ)\n```\n\n  Then the two modes only differ in how they observe that operation:\n```\n  # ordinary copy\n  put_response \u003d run_destination_put(sink_req, source_resp)\n  return make_client_response(put_response)\n\n  # heartbeat copy\n  task \u003d spawn(run_destination_put, sink_req, source_resp)\n  return heartbeat_response(task)\n```\n  The heartbeat response generator should own the task lifecycle, not the source iterator:\n```\n  def heartbeat_response(task):\n      try:\n          yield b\u0027 \u0027\n          while not task.done():\n              yield heartbeat_or_result(task)\n      finally:\n          task.cancel_and_join()  # if client disconnects\n```","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":555,"context_line":"                copy_source_hook(req, source_resp, sink_req)"},{"line_number":556,"context_line":"            except Exception:"},{"line_number":557,"context_line":"                close_if_possible(source_resp.app_iter)"},{"line_number":558,"context_line":"                raise"},{"line_number":559,"context_line":""},{"line_number":560,"context_line":"        # Create response headers for PUT response"},{"line_number":561,"context_line":"        resp_headers \u003d self._create_response_headers(source_path,"}],"source_content_type":"text/x-python","patch_set":5,"id":"6b942ed8_a3177372","line":558,"updated":"2026-08-22 02:04:47.000000000","message":"N.B. you can\u0027t use ctx-mgr finally cause that would close it before we\u0027re done with it","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":573,"context_line":"                    close_if_possible(source_resp.app_iter)"},{"line_number":574,"context_line":"            return clean_iter(put_resp)"},{"line_number":575,"context_line":""},{"line_number":576,"context_line":"        close_if_possible(source_resp.app_iter)"},{"line_number":577,"context_line":"        return put_resp"},{"line_number":578,"context_line":""},{"line_number":579,"context_line":"    def handle_OPTIONS(self, req, start_response):"}],"source_content_type":"text/x-python","patch_set":5,"id":"7d0face0_9adf1d21","line":576,"updated":"2026-08-22 02:04:47.000000000","message":"the duplicate `close_if_possible` in this return path is begging for a `finally`\n\nthe problem is the heartbeat case","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"}],"swift/common/middleware/s3api/controllers/multi_upload.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":774,"context_line":"        headers[s3_etag_header] \u003d s3_etag"},{"line_number":775,"context_line":"        # Leave base header value blank; SLO will populate"},{"line_number":776,"context_line":"        headers[get_container_update_override_key(\u0027etag\u0027)] \u003d \\"},{"line_number":777,"context_line":"            serialize_header(\u0027\u0027, {\u0027s3_etag\u0027: s3_etag})"},{"line_number":778,"context_line":""},{"line_number":779,"context_line":"        too_small_message \u003d (\u0027s3api requires that each segment be at least \u0027"},{"line_number":780,"context_line":"                             \u0027%d bytes\u0027 % self.conf.min_segment_size)"}],"source_content_type":"text/x-python","patch_set":5,"id":"2723228d_275f53be","line":777,"updated":"2026-08-22 02:04:47.000000000","message":"it\u0027s not obvious to me why this new helper gets created in *this* change?","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":774,"context_line":"        headers[s3_etag_header] \u003d s3_etag"},{"line_number":775,"context_line":"        # Leave base header value blank; SLO will populate"},{"line_number":776,"context_line":"        headers[get_container_update_override_key(\u0027etag\u0027)] \u003d \\"},{"line_number":777,"context_line":"            serialize_header(\u0027\u0027, {\u0027s3_etag\u0027: s3_etag})"},{"line_number":778,"context_line":""},{"line_number":779,"context_line":"        too_small_message \u003d (\u0027s3api requires that each segment be at least \u0027"},{"line_number":780,"context_line":"                             \u0027%d bytes\u0027 % self.conf.min_segment_size)"}],"source_content_type":"text/x-python","patch_set":5,"id":"fc86cbcd_5c36b238","line":777,"in_reply_to":"2723228d_275f53be","updated":"2026-08-24 21:33:36.000000000","message":"Acknowledged","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"}],"swift/common/middleware/s3api/s3api.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":456,"context_line":"            resp \u003d self.handle_request(req)"},{"line_number":457,"context_line":"        except NotS3Request:"},{"line_number":458,"context_line":"            # prevent swift api copying s3api specific sysmeta..."},{"line_number":459,"context_line":"            install_copy_hook(env, s3_request\u003dFalse)"},{"line_number":460,"context_line":"            return self.app(env, start_response)"},{"line_number":461,"context_line":"        except InvalidSubresource as e:"},{"line_number":462,"context_line":"            self.logger.debug(e.cause)"}],"source_content_type":"text/x-python","patch_set":5,"id":"ae9b55bb_280d4184","line":459,"updated":"2026-08-22 02:04:47.000000000","message":"that is SUPA sneaky smart!?\n\n  - Unit: test_non_s3_request_passthrough_copy in test/unit/common/middleware/s3api/test_s3api.py builds a native Swift PUT copy request through S3API, gives its source an x-object-sysmeta-s3api-etag, and asserts the destination PUT does not receive that header. It also verifies non-S3API sysmeta remains.\n\n  - Functional: test_mpu_object_copy_via_swift_api in test/functional/s3api/test_multi_upload.py creates the source through S3 multipart upload, then calls src_file_swift.copy(...) using the native Swift API. It asserts the copy has the content-MD5 ETag—not the S3 multipart ETag—and has no x_manifest_etag.","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":456,"context_line":"            resp \u003d self.handle_request(req)"},{"line_number":457,"context_line":"        except NotS3Request:"},{"line_number":458,"context_line":"            # prevent swift api copying s3api specific sysmeta..."},{"line_number":459,"context_line":"            install_copy_hook(env, s3_request\u003dFalse)"},{"line_number":460,"context_line":"            return self.app(env, start_response)"},{"line_number":461,"context_line":"        except InvalidSubresource as e:"},{"line_number":462,"context_line":"            self.logger.debug(e.cause)"}],"source_content_type":"text/x-python","patch_set":5,"id":"be1f9acf_0e4283ef","line":459,"in_reply_to":"4e48614e_800579d3","updated":"2026-08-24 21:33:36.000000000","message":"Done","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":456,"context_line":"            resp \u003d self.handle_request(req)"},{"line_number":457,"context_line":"        except NotS3Request:"},{"line_number":458,"context_line":"            # prevent swift api copying s3api specific sysmeta..."},{"line_number":459,"context_line":"            install_copy_hook(env, s3_request\u003dFalse)"},{"line_number":460,"context_line":"            return self.app(env, start_response)"},{"line_number":461,"context_line":"        except InvalidSubresource as e:"},{"line_number":462,"context_line":"            self.logger.debug(e.cause)"}],"source_content_type":"text/x-python","patch_set":5,"id":"4e48614e_800579d3","line":459,"in_reply_to":"ae9b55bb_280d4184","updated":"2026-08-24 15:16:49.000000000","message":"I think I can move this up before the try i.e. remove the s3_request flag and just install the hook always","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"}],"swift/common/middleware/s3api/utils.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":40,"context_line":"    if resource.lower() \u003d\u003d \u0027object\u0027:"},{"line_number":41,"context_line":"        return \u0027x-object-sysmeta-s3api-\u0027"},{"line_number":42,"context_line":"    else:"},{"line_number":43,"context_line":"        return \u0027x-container-sysmeta-s3api-\u0027"},{"line_number":44,"context_line":""},{"line_number":45,"context_line":""},{"line_number":46,"context_line":"def sysmeta_header(resource, name):"}],"source_content_type":"text/x-python","patch_set":5,"id":"46a08f6c_c67101d5","line":43,"updated":"2026-08-22 02:04:47.000000000","message":"I thought I hated all the *new* little \"is_*_sysmeta\" helpers - turns out I hated the old pre-existing badly named helpers!\n\n1001906: sq? clarify s3api sysmeta helper names | https://review.opendev.org/c/openstack/swift/+/1001906","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":56,"context_line":"    Returns the ``s3api`` namespace object system metadata header for the given"},{"line_number":57,"context_line":"    name."},{"line_number":58,"context_line":"    \"\"\""},{"line_number":59,"context_line":"    return get_sys_meta_prefix(\u0027object\u0027) + \u0027s3api-\u0027 + name"},{"line_number":60,"context_line":""},{"line_number":61,"context_line":""},{"line_number":62,"context_line":"def is_s3api_sysmeta(server_type, name):"}],"source_content_type":"text/x-python","patch_set":5,"id":"285286b8_a77a9c1a","line":59,"updated":"2026-08-24 15:16:49.000000000","message":"remove this","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":56,"context_line":"    Returns the ``s3api`` namespace object system metadata header for the given"},{"line_number":57,"context_line":"    name."},{"line_number":58,"context_line":"    \"\"\""},{"line_number":59,"context_line":"    return get_sys_meta_prefix(\u0027object\u0027) + \u0027s3api-\u0027 + name"},{"line_number":60,"context_line":""},{"line_number":61,"context_line":""},{"line_number":62,"context_line":"def is_s3api_sysmeta(server_type, name):"}],"source_content_type":"text/x-python","patch_set":5,"id":"673947fb_e3f2c1ef","line":59,"in_reply_to":"285286b8_a77a9c1a","updated":"2026-08-24 21:33:36.000000000","message":"Done","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":68,"context_line":"    return is_s3api_sysmeta(\u0027object\u0027, name)"},{"line_number":69,"context_line":""},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"def swift3_object_sysmeta_header(name):"},{"line_number":72,"context_line":"    \"\"\""},{"line_number":73,"context_line":"    Returns the legacy ``swift3`` namespace object system metadata header for"},{"line_number":74,"context_line":"    the given name."}],"source_content_type":"text/x-python","patch_set":5,"id":"f67666cd_56f2e0a3","line":71,"updated":"2026-08-22 02:04:47.000000000","message":"this is an anomoly - `swift3_sysmeta_header(resource, name)` would have been better, but we only ever needed object and so here it stays.\n\nDoesn\u0027t mean that `s3api_object_sysmeta_header` is a good idea or better than `s3api_sysmeta_header(\u0027object\u0027, ...)`\n\n1001906: sq? clarify s3api sysmeta helper names | https://review.opendev.org/c/openstack/swift/+/1001906","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":444,"context_line":"        if previous_copy_source_hook:"},{"line_number":445,"context_line":"            previous_copy_source_hook(req, source_resp, sink_req)"},{"line_number":446,"context_line":""},{"line_number":447,"context_line":"        # copy middleware has already copied most sysmeta fom the source resp"},{"line_number":448,"context_line":"        # to the sink req, so first clear *all* s3api sysmeta, and then set"},{"line_number":449,"context_line":"        # only the s3api sysmeta that is explicitly wanted in sink_req..."},{"line_number":450,"context_line":"        etag_override_key \u003d get_container_update_override_key(\u0027etag\u0027).lower()"}],"source_content_type":"text/x-python","patch_set":5,"id":"c766971c_614dd01b","line":447,"updated":"2026-08-22 02:04:47.000000000","message":"my agent spotted fom!","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":false,"context_lines":[{"line_number":444,"context_line":"        if previous_copy_source_hook:"},{"line_number":445,"context_line":"            previous_copy_source_hook(req, source_resp, sink_req)"},{"line_number":446,"context_line":""},{"line_number":447,"context_line":"        # copy middleware has already copied most sysmeta fom the source resp"},{"line_number":448,"context_line":"        # to the sink req, so first clear *all* s3api sysmeta, and then set"},{"line_number":449,"context_line":"        # only the s3api sysmeta that is explicitly wanted in sink_req..."},{"line_number":450,"context_line":"        etag_override_key \u003d get_container_update_override_key(\u0027etag\u0027).lower()"}],"source_content_type":"text/x-python","patch_set":5,"id":"d7ca4481_67247f6e","line":447,"in_reply_to":"c766971c_614dd01b","updated":"2026-08-24 15:16:49.000000000","message":"Done","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":449,"context_line":"        # only the s3api sysmeta that is explicitly wanted in sink_req..."},{"line_number":450,"context_line":"        etag_override_key \u003d get_container_update_override_key(\u0027etag\u0027).lower()"},{"line_number":451,"context_line":"        for key, value in dict(sink_req.headers).items():"},{"line_number":452,"context_line":"            lower_key \u003d key.lower()"},{"line_number":453,"context_line":"            if (is_s3api_object_sysmeta(lower_key)"},{"line_number":454,"context_line":"                    or is_swift3_object_sysmeta(lower_key)):"},{"line_number":455,"context_line":"                del sink_req.headers[key]"}],"source_content_type":"text/x-python","patch_set":5,"id":"900f961e_983036dd","line":452,"updated":"2026-08-22 02:04:47.000000000","message":"oic, this normalization isn\u0027t for the benifit of the is_*_object_sysmeta helpers (which will redo their own normalization) - it\u0027s for the other conditional tests","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":449,"context_line":"        # only the s3api sysmeta that is explicitly wanted in sink_req..."},{"line_number":450,"context_line":"        etag_override_key \u003d get_container_update_override_key(\u0027etag\u0027).lower()"},{"line_number":451,"context_line":"        for key, value in dict(sink_req.headers).items():"},{"line_number":452,"context_line":"            lower_key \u003d key.lower()"},{"line_number":453,"context_line":"            if (is_s3api_object_sysmeta(lower_key)"},{"line_number":454,"context_line":"                    or is_swift3_object_sysmeta(lower_key)):"},{"line_number":455,"context_line":"                del sink_req.headers[key]"}],"source_content_type":"text/x-python","patch_set":5,"id":"a400f3ba_e4f4c0ab","line":452,"in_reply_to":"900f961e_983036dd","updated":"2026-08-24 21:33:36.000000000","message":"Acknowledged","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":455,"context_line":"                del sink_req.headers[key]"},{"line_number":456,"context_line":"            elif s3_request and lower_key in (\u0027x-object-sysmeta-slo-etag\u0027,"},{"line_number":457,"context_line":"                                              \u0027x-object-sysmeta-slo-size\u0027):"},{"line_number":458,"context_line":"                del sink_req.headers[key]"},{"line_number":459,"context_line":"            elif lower_key \u003d\u003d etag_override_key:"},{"line_number":460,"context_line":"                etag, params \u003d parse_header(value)"},{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"}],"source_content_type":"text/x-python","patch_set":5,"id":"050ef1a1_46d676e5","line":458,"updated":"2026-08-22 02:04:47.000000000","message":"this SEEMS reasonable, but mostly because the whole idea of blindly copying sysmeta seems insane!\n\nBut why should s3api have to go out of it\u0027s way to deal with slo\u0027s sysmeta (and no-one-elses\u0027?!?) - maybe slo should be doing this if it\u0027s so important?\n\n1001905: WIP: let SLO own copy sysmeta cleanup | https://review.opendev.org/c/openstack/swift/+/1001905\n\nI don\u0027t think there\u0027s any functional tests that prove this is necessary (there ARE some unittests, but they\u0027re kind of on-the-nose) - I think if we really want a test failure to demonstrate \"see this sysmeta don\u0027t belong here!?\" (despite being functionally benign) we may need to drop into probetests:\n\n1001904: sq? add s3api copy sysmeta probe | https://review.opendev.org/c/openstack/swift/+/1001904","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":455,"context_line":"                del sink_req.headers[key]"},{"line_number":456,"context_line":"            elif s3_request and lower_key in (\u0027x-object-sysmeta-slo-etag\u0027,"},{"line_number":457,"context_line":"                                              \u0027x-object-sysmeta-slo-size\u0027):"},{"line_number":458,"context_line":"                del sink_req.headers[key]"},{"line_number":459,"context_line":"            elif lower_key \u003d\u003d etag_override_key:"},{"line_number":460,"context_line":"                etag, params \u003d parse_header(value)"},{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"}],"source_content_type":"text/x-python","patch_set":5,"id":"124f0f59_76e583d7","line":458,"in_reply_to":"050ef1a1_46d676e5","updated":"2026-08-24 15:16:49.000000000","message":"I like the idea of slo doing its own housekeeping. I ran out of energy for it, thanks for the follow-up","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":455,"context_line":"                del sink_req.headers[key]"},{"line_number":456,"context_line":"            elif s3_request and lower_key in (\u0027x-object-sysmeta-slo-etag\u0027,"},{"line_number":457,"context_line":"                                              \u0027x-object-sysmeta-slo-size\u0027):"},{"line_number":458,"context_line":"                del sink_req.headers[key]"},{"line_number":459,"context_line":"            elif lower_key \u003d\u003d etag_override_key:"},{"line_number":460,"context_line":"                etag, params \u003d parse_header(value)"},{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"}],"source_content_type":"text/x-python","patch_set":5,"id":"ed76bfa2_29cda048","line":458,"in_reply_to":"124f0f59_76e583d7","updated":"2026-08-24 21:33:36.000000000","message":"Acknowledged","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":458,"context_line":"                del sink_req.headers[key]"},{"line_number":459,"context_line":"            elif lower_key \u003d\u003d etag_override_key:"},{"line_number":460,"context_line":"                etag, params \u003d parse_header(value)"},{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"},{"line_number":462,"context_line":"                value \u003d serialize_header(etag, params)"},{"line_number":463,"context_line":"                sink_req.headers[key] \u003d value or None"},{"line_number":464,"context_line":"            # else: not relevant to s3api"}],"source_content_type":"text/x-python","patch_set":5,"id":"76946932_97005bf1","line":461,"updated":"2026-08-22 02:04:47.000000000","message":"oh dude - that is SO sneaky!?","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":458,"context_line":"                del sink_req.headers[key]"},{"line_number":459,"context_line":"            elif lower_key \u003d\u003d etag_override_key:"},{"line_number":460,"context_line":"                etag, params \u003d parse_header(value)"},{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"},{"line_number":462,"context_line":"                value \u003d serialize_header(etag, params)"},{"line_number":463,"context_line":"                sink_req.headers[key] \u003d value or None"},{"line_number":464,"context_line":"            # else: not relevant to s3api"}],"source_content_type":"text/x-python","patch_set":5,"id":"a0972c72_ffaca859","line":461,"in_reply_to":"37b9c0ce_305027b2","updated":"2026-08-24 21:33:36.000000000","message":"I convinced myself this is ok\n\n... actully it\u0027d be \"ok\" if we didn\u0027t pop the s3_etag - turns out that copy.py already has some heavy handed rules for the etag override (too bad it doesn\u0027t cleanup the other unrelated sysmeta while it\u0027s at it!)\n\n1002201: probe tests: extend SLO/MPU copy probes | https://review.opendev.org/c/openstack/swift/+/1002201\n\nI think it will be better if all the mw handled their own etag override systags tho\n\nN.B. swift_bytes goes in content-type (sadly?) and always gets stripped in the proxy on source READ and re-written (if needed) during slo-to-slo `?multipart-manifest\u003d(get\u003d\u003eput)`","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":458,"context_line":"                del sink_req.headers[key]"},{"line_number":459,"context_line":"            elif lower_key \u003d\u003d etag_override_key:"},{"line_number":460,"context_line":"                etag, params \u003d parse_header(value)"},{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"},{"line_number":462,"context_line":"                value \u003d serialize_header(etag, params)"},{"line_number":463,"context_line":"                sink_req.headers[key] \u003d value or None"},{"line_number":464,"context_line":"            # else: not relevant to s3api"}],"source_content_type":"text/x-python","patch_set":5,"id":"37b9c0ce_305027b2","line":461,"in_reply_to":"76946932_97005bf1","updated":"2026-08-24 15:16:49.000000000","message":"hmmm, I haven\u0027t cleaned up slo_etag here though :/","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":459,"context_line":"            elif lower_key \u003d\u003d etag_override_key:"},{"line_number":460,"context_line":"                etag, params \u003d parse_header(value)"},{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"},{"line_number":462,"context_line":"                value \u003d serialize_header(etag, params)"},{"line_number":463,"context_line":"                sink_req.headers[key] \u003d value or None"},{"line_number":464,"context_line":"            # else: not relevant to s3api"},{"line_number":465,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"5235bffe_4c9efb7d","line":462,"range":{"start_line":462,"start_character":24,"end_line":462,"end_character":40},"updated":"2026-08-24 15:16:49.000000000","message":"here\u0027s why I added the ``serialize_header`` helper; I couldn\u0027t bear spelling out the ``join \u0027; %s\u003d%s\u0027 blah blah`` again!","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":459,"context_line":"            elif lower_key \u003d\u003d etag_override_key:"},{"line_number":460,"context_line":"                etag, params \u003d parse_header(value)"},{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"},{"line_number":462,"context_line":"                value \u003d serialize_header(etag, params)"},{"line_number":463,"context_line":"                sink_req.headers[key] \u003d value or None"},{"line_number":464,"context_line":"            # else: not relevant to s3api"},{"line_number":465,"context_line":""}],"source_content_type":"text/x-python","patch_set":5,"id":"56b7592c_97c241ae","line":462,"range":{"start_line":462,"start_character":24,"end_line":462,"end_character":40},"in_reply_to":"5235bffe_4c9efb7d","updated":"2026-08-24 21:33:36.000000000","message":"Acknowledged","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"},{"line_number":462,"context_line":"                value \u003d serialize_header(etag, params)"},{"line_number":463,"context_line":"                sink_req.headers[key] \u003d value or None"},{"line_number":464,"context_line":"            # else: not relevant to s3api"},{"line_number":465,"context_line":""},{"line_number":466,"context_line":"        acl_header \u003d s3api_object_sysmeta_header(\u0027acl\u0027)"},{"line_number":467,"context_line":"        sink_req.headers[acl_header] \u003d (req.headers.get(acl_header)"}],"source_content_type":"text/x-python","patch_set":5,"id":"09a0758e_d978d97f","line":464,"updated":"2026-08-22 02:04:47.000000000","message":"ok, so the s3api copy hook is all about stripping *s3api* sysmeta (and maybe strangely also slo sysmeta, but only IF it\u0027s a s3request ??)\n\nthis change does NOT \"fix\" the copy mw (or even s3api copy) to prevent the blind carry of source object sysmeta in general.","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"},{"line_number":462,"context_line":"                value \u003d serialize_header(etag, params)"},{"line_number":463,"context_line":"                sink_req.headers[key] \u003d value or None"},{"line_number":464,"context_line":"            # else: not relevant to s3api"},{"line_number":465,"context_line":""},{"line_number":466,"context_line":"        acl_header \u003d s3api_object_sysmeta_header(\u0027acl\u0027)"},{"line_number":467,"context_line":"        sink_req.headers[acl_header] \u003d (req.headers.get(acl_header)"}],"source_content_type":"text/x-python","patch_set":5,"id":"4d6cc589_db100002","line":464,"in_reply_to":"09a0758e_d978d97f","updated":"2026-08-24 15:16:49.000000000","message":"TL;DR I think the hook might need a little refining\n\nI deliberately avoided \"fixing\" copy middleware because of fears about third-party middleware relying on its behaviour.\n\nEach middleware taking care of its metadata seems like a good principle. Encryption is perhaps the only good example: it purges its sysmeta in the read path https://github.com/openstack/swift/blob/c7748ac9287eab81c319a574937d153387f64e0b/swift/common/middleware/crypto/decrypter.py#L37.\n\nBut it gets blurred because s3api MPU is tightly coupled with SLO. I can\u0027t really justify why this current hook implementation only removes slo sysmeta when the copy request came via the s3api; the source could still be an s3 MPU if the copy comes via the swift api. So I was thinking of changing the condition to be \"only if we find s3 sysmeta then also remove slo metadata, because the two are known to be coupled\".\n\nBut then I saw 1001905: WIP: let SLO own copy sysmeta cleanup | https://review.opendev.org/c/openstack/swift/+/1001905 which I warm to, but also got me thinking \"when DO we want SLO to leak its sysmeta?\"","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":461,"context_line":"                params.pop(\u0027s3_etag\u0027, None)"},{"line_number":462,"context_line":"                value \u003d serialize_header(etag, params)"},{"line_number":463,"context_line":"                sink_req.headers[key] \u003d value or None"},{"line_number":464,"context_line":"            # else: not relevant to s3api"},{"line_number":465,"context_line":""},{"line_number":466,"context_line":"        acl_header \u003d s3api_object_sysmeta_header(\u0027acl\u0027)"},{"line_number":467,"context_line":"        sink_req.headers[acl_header] \u003d (req.headers.get(acl_header)"}],"source_content_type":"text/x-python","patch_set":5,"id":"e7f2e743_ed02f381","line":464,"in_reply_to":"4d6cc589_db100002","updated":"2026-08-24 21:33:36.000000000","message":"Acknowledged","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":466,"context_line":"        acl_header \u003d s3api_object_sysmeta_header(\u0027acl\u0027)"},{"line_number":467,"context_line":"        sink_req.headers[acl_header] \u003d (req.headers.get(acl_header)"},{"line_number":468,"context_line":"                                        or source_resp.headers.get(acl_header))"},{"line_number":469,"context_line":""},{"line_number":470,"context_line":"    environ[copy_hook_key] \u003d copy_hook"}],"source_content_type":"text/x-python","patch_set":5,"id":"320ee03f_811c3060","line":469,"updated":"2026-08-22 02:04:47.000000000","message":"oh crazy!  so the one piece of s3api metadata that we DO copy from the source object is the acl_header!  this is per-object acl stuff, we don\u0027t use this in prod.  I\u0027m guessing there\u0027s something that shows this is how aws s3 works tho?\n\nand just to be super explicit about it - this white list *is* adding back in a header we just del\u0027d?  or maybe setting it to None?","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":466,"context_line":"        acl_header \u003d s3api_object_sysmeta_header(\u0027acl\u0027)"},{"line_number":467,"context_line":"        sink_req.headers[acl_header] \u003d (req.headers.get(acl_header)"},{"line_number":468,"context_line":"                                        or source_resp.headers.get(acl_header))"},{"line_number":469,"context_line":""},{"line_number":470,"context_line":"    environ[copy_hook_key] \u003d copy_hook"}],"source_content_type":"text/x-python","patch_set":5,"id":"6f2dfacd_71522c8a","line":469,"in_reply_to":"320ee03f_811c3060","updated":"2026-08-24 15:16:49.000000000","message":"func tests break without this (with s3_acl \u003d True)\n\nIIUC this mimics what is happening on master: if the copy request has acl sysmeta then use that, otherwise copy whatever is found in the source response.\n\nOff-topic (but not insignificant) is whether that is the correct thing to do. S3 docs says:\n\n\"\nWhen you copy an object, the ACL metadata is not preserved and is set to private by default. Only the owner has full access control. To override the default ACL setting, specify a new ACL when you generate a copy request. For more information, see Using ACLs.\n\"\n\nhttps://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html#API_CopyObject_RequestSyntax","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4c387ba3c0d3809ba30b0baf15d22c4dc70fae88","unresolved":true,"context_lines":[{"line_number":437,"context_line":"            if (is_s3api_sysmeta(\u0027object\u0027, lower_key)"},{"line_number":438,"context_line":"                    or is_swift3_object_sysmeta(lower_key)):"},{"line_number":439,"context_line":"                del sink_req.headers[key]"},{"line_number":440,"context_line":"            elif s3_mpu and lower_key in (\u0027x-object-sysmeta-slo-etag\u0027,"},{"line_number":441,"context_line":"                                          \u0027x-object-sysmeta-slo-size\u0027):"},{"line_number":442,"context_line":"                # s3api assumes responsibility for the SLO sysmeta for an MPU"},{"line_number":443,"context_line":"                del sink_req.headers[key]"}],"source_content_type":"text/x-python","patch_set":6,"id":"bfda5dd6_e875cc86","line":440,"range":{"start_line":440,"start_character":14,"end_line":440,"end_character":23},"updated":"2026-08-24 18:01:04.000000000","message":"this is the significant change in this latest patch set: the same hook is installed for all requests (s3api and swift api) and slo sysmeta is erased IFF it\u0027s an s3 mpu being copied","commit_id":"a43a5f4edc99ececfb903da6665c0abe9a564ea1"}],"swift/common/utils/__init__.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":3204,"context_line":"    :param params: a dict of params to append to the ``token``."},{"line_number":3205,"context_line":"    :return: the header value as a string."},{"line_number":3206,"context_line":"    \"\"\""},{"line_number":3207,"context_line":"    return token + \u0027\u0027.join(\u0027; %s\u003d%s\u0027 % kv for kv in params.items())"},{"line_number":3208,"context_line":""},{"line_number":3209,"context_line":""},{"line_number":3210,"context_line":"def extract_swift_bytes(content_type):"}],"source_content_type":"text/x-python","patch_set":4,"id":"5aadb983_b148d9bd","line":3207,"updated":"2026-08-22 02:04:47.000000000","message":"this seems nice too!\n\nI like `for kv in params.items()` - i get so used to `for k, v in params.items()` I didn\u0027t really think of just NOT unpacking the tuple!","commit_id":"3ef6bfb0635092296a2439c724d48a2d60dea07c"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":3204,"context_line":"    :param params: a dict of params to append to the ``token``."},{"line_number":3205,"context_line":"    :return: the header value as a string."},{"line_number":3206,"context_line":"    \"\"\""},{"line_number":3207,"context_line":"    return token + \u0027\u0027.join(\u0027; %s\u003d%s\u0027 % kv for kv in params.items())"},{"line_number":3208,"context_line":""},{"line_number":3209,"context_line":""},{"line_number":3210,"context_line":"def extract_swift_bytes(content_type):"}],"source_content_type":"text/x-python","patch_set":4,"id":"e1abc807_cc95a479","line":3207,"in_reply_to":"5aadb983_b148d9bd","updated":"2026-08-24 15:16:49.000000000","message":"TBH I learnt that trick from reading somebody else\u0027s cleverness","commit_id":"3ef6bfb0635092296a2439c724d48a2d60dea07c"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":3204,"context_line":"    :param params: a dict of params to append to the ``token``."},{"line_number":3205,"context_line":"    :return: the header value as a string."},{"line_number":3206,"context_line":"    \"\"\""},{"line_number":3207,"context_line":"    return token + \u0027\u0027.join(\u0027; %s\u003d%s\u0027 % kv for kv in params.items())"},{"line_number":3208,"context_line":""},{"line_number":3209,"context_line":""},{"line_number":3210,"context_line":"def extract_swift_bytes(content_type):"}],"source_content_type":"text/x-python","patch_set":4,"id":"14ea1225_9d587a34","line":3207,"in_reply_to":"e1abc807_cc95a479","updated":"2026-08-24 21:33:36.000000000","message":"Acknowledged","commit_id":"3ef6bfb0635092296a2439c724d48a2d60dea07c"}],"test/functional/__init__.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":1342,"context_line":"def md5hex(s):"},{"line_number":1343,"context_line":"    if not isinstance(s, bytes):"},{"line_number":1344,"context_line":"        s \u003d s.encode(\u0027ascii\u0027)"},{"line_number":1345,"context_line":"    return md5(s, usedforsecurity\u003dFalse).hexdigest()"}],"source_content_type":"text/x-python","patch_set":4,"id":"75b741b1_10258cf7","line":1345,"updated":"2026-08-22 02:04:47.000000000","message":"this is moved, not new.\n\nthere\u0027s another couple few in test.unit as well:\n\n  - https://github.com/NVIDIA/swift/blob/master/test/unit/common/middleware/test_slo.py#L58 has an exact semantic match: strings are ASCII-encoded, then hashed with usedforsecurity\u003dFalse.\n  - https://github.com/NVIDIA/swift/blob/master/test/unit/common/middleware/crypto/crypto_helpers.py#L43 has a bytes-only md5hex.\n  - https://github.com/NVIDIA/swift/blob/master/test/unit/common/middleware/test_dlo.py#L35 has a variant that UTF-8-encodes strings.","commit_id":"3ef6bfb0635092296a2439c724d48a2d60dea07c"}],"test/functional/s3api/__init__.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":96,"context_line":"        self.assertEqual(normalize_etag(etag1), normalize_etag(etag2))"},{"line_number":97,"context_line":""},{"line_number":98,"context_line":"    def assertEtagsNotEqual(self, etag1, etag2):"},{"line_number":99,"context_line":"        self.assertNotEqual(normalize_etag(etag1), normalize_etag(etag2))"},{"line_number":100,"context_line":""},{"line_number":101,"context_line":""},{"line_number":102,"context_line":"class S3ApiBaseBoto3(S3ApiBase):"}],"source_content_type":"text/x-python","patch_set":3,"id":"64730ada_c44babb4","line":99,"updated":"2026-08-22 02:04:47.000000000","message":"i like these!","commit_id":"94ac31e6ce7fd53cac66b0d22861c306173f6b45"}],"test/functional/s3api/test_multi_upload.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":1054,"context_line":"                return o.find(\u0027ETag\u0027).text"},{"line_number":1055,"context_line":"        self.fail(\u0027key %r not found in listing of bucket %r\u0027 % (key, bucket))"},{"line_number":1056,"context_line":""},{"line_number":1057,"context_line":"    def test_mpu_object_copy(self):"},{"line_number":1058,"context_line":"        # verify copying from an mpu to a regular object"},{"line_number":1059,"context_line":"        bucket \u003d \u0027bucket\u0027"},{"line_number":1060,"context_line":"        obj \u003d \u0027mpu-object\u0027"}],"source_content_type":"text/x-python","patch_set":5,"id":"aaf382db_96af2032","line":1057,"updated":"2026-08-22 02:04:47.000000000","message":"all these tests are boto2 based - how are you even running them 😅","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":1054,"context_line":"                return o.find(\u0027ETag\u0027).text"},{"line_number":1055,"context_line":"        self.fail(\u0027key %r not found in listing of bucket %r\u0027 % (key, bucket))"},{"line_number":1056,"context_line":""},{"line_number":1057,"context_line":"    def test_mpu_object_copy(self):"},{"line_number":1058,"context_line":"        # verify copying from an mpu to a regular object"},{"line_number":1059,"context_line":"        bucket \u003d \u0027bucket\u0027"},{"line_number":1060,"context_line":"        obj \u003d \u0027mpu-object\u0027"}],"source_content_type":"text/x-python","patch_set":5,"id":"9fcf3c6e_807a68cf","line":1057,"in_reply_to":"aaf382db_96af2032","updated":"2026-08-24 15:16:49.000000000","message":"I\u0027m so behind the times ;-)","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"}],"test/functional/s3api/test_object.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":763,"context_line":"        obj \u003d \u0027object\u0027"},{"line_number":764,"context_line":"        dst_obj \u003d \u0027object-copy\u0027"},{"line_number":765,"context_line":"        content \u003d b\u0027abcdefghij\u0027"},{"line_number":766,"context_line":"        etag \u003d md5(content, usedforsecurity\u003dFalse).hexdigest()"},{"line_number":767,"context_line":"        req_headers \u003d {\u0027X-Amz-Meta-Foo\u0027: \u0027bar\u0027}"},{"line_number":768,"context_line":""},{"line_number":769,"context_line":"        # PUT the source object with custom user metadata"}],"source_content_type":"text/x-python","patch_set":5,"id":"f8959f94_84532223","line":766,"updated":"2026-08-22 02:04:47.000000000","message":"you really can\u0027t win for trying\n\n\u003e the patch also adds direct md5(..., usedforsecurity\u003dFalse).hexdigest() calls in test/functional/s3api/test_object.py, and hundreds remain elsewhere. So there is no indication of a broader conversion plan.","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":763,"context_line":"        obj \u003d \u0027object\u0027"},{"line_number":764,"context_line":"        dst_obj \u003d \u0027object-copy\u0027"},{"line_number":765,"context_line":"        content \u003d b\u0027abcdefghij\u0027"},{"line_number":766,"context_line":"        etag \u003d md5(content, usedforsecurity\u003dFalse).hexdigest()"},{"line_number":767,"context_line":"        req_headers \u003d {\u0027X-Amz-Meta-Foo\u0027: \u0027bar\u0027}"},{"line_number":768,"context_line":""},{"line_number":769,"context_line":"        # PUT the source object with custom user metadata"}],"source_content_type":"text/x-python","patch_set":5,"id":"13ac7061_90f14324","line":766,"in_reply_to":"4e10d787_8b6132cc","updated":"2026-08-24 21:33:36.000000000","message":"Acknowledged","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":763,"context_line":"        obj \u003d \u0027object\u0027"},{"line_number":764,"context_line":"        dst_obj \u003d \u0027object-copy\u0027"},{"line_number":765,"context_line":"        content \u003d b\u0027abcdefghij\u0027"},{"line_number":766,"context_line":"        etag \u003d md5(content, usedforsecurity\u003dFalse).hexdigest()"},{"line_number":767,"context_line":"        req_headers \u003d {\u0027X-Amz-Meta-Foo\u0027: \u0027bar\u0027}"},{"line_number":768,"context_line":""},{"line_number":769,"context_line":"        # PUT the source object with custom user metadata"}],"source_content_type":"text/x-python","patch_set":5,"id":"4e10d787_8b6132cc","line":766,"in_reply_to":"f8959f94_84532223","updated":"2026-08-24 15:16:49.000000000","message":"sigh. we thought it was cool to type it out so many times, or we freely cut-paste-modify tests, or we did a global replace once upon a time.\n\nthis one is on me though :/","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"}],"test/functional/test_slo.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":54,"context_line":"def md5hex(s):"},{"line_number":55,"context_line":"    if not isinstance(s, bytes):"},{"line_number":56,"context_line":"        s \u003d s.encode(\u0027ascii\u0027)"},{"line_number":57,"context_line":"    return md5(s, usedforsecurity\u003dFalse).hexdigest()"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"class TestSloEnv(BaseEnv):"}],"source_content_type":"text/x-python","patch_set":5,"id":"3f1e4aa6_8984528c","side":"PARENT","line":57,"updated":"2026-08-22 02:04:47.000000000","message":"oic, this already existed","commit_id":"e19c896da0eeaf09419726106c46acc3e8bdce39"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":false,"context_lines":[{"line_number":54,"context_line":"def md5hex(s):"},{"line_number":55,"context_line":"    if not isinstance(s, bytes):"},{"line_number":56,"context_line":"        s \u003d s.encode(\u0027ascii\u0027)"},{"line_number":57,"context_line":"    return md5(s, usedforsecurity\u003dFalse).hexdigest()"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":""},{"line_number":60,"context_line":"class TestSloEnv(BaseEnv):"}],"source_content_type":"text/x-python","patch_set":5,"id":"f73b617c_14f3318a","side":"PARENT","line":57,"in_reply_to":"3f1e4aa6_8984528c","updated":"2026-08-24 15:16:49.000000000","message":"Acknowledged","commit_id":"e19c896da0eeaf09419726106c46acc3e8bdce39"}],"test/s3api/test_mpu.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":748,"context_line":"            }], objs)"},{"line_number":749,"context_line":"        dest_etag \u003d etag_from_resp(dest_get_resp)"},{"line_number":750,"context_line":"        self.assertEqual(exp_dest_etag, dest_etag)"},{"line_number":751,"context_line":"        self.assertNotEqual(dest_etag, src_etag)"},{"line_number":752,"context_line":""},{"line_number":753,"context_line":"    def test_create_list_abort_multipart_uploads(self):"},{"line_number":754,"context_line":"        key_name \u003d self.create_name(\u0027key\u0027)"}],"source_content_type":"text/x-python","patch_set":4,"id":"6d7f64eb_713b2c5e","line":751,"updated":"2026-08-22 02:04:47.000000000","message":"getting crazy with the drive-by!  ain\u0027t no body got time for no prefactor to cleanup all those un-normalized etags!","commit_id":"3ef6bfb0635092296a2439c724d48a2d60dea07c"}],"test/unit/common/middleware/s3api/test_utils.py":[{"author":{"_account_id":6968,"name":"Christian Schwede","email":"cschwede@nvidia.com","username":"cschwede"},"change_message_id":"d81aa67fe12cd4e278247f3f5cf420fd322defa3","unresolved":true,"context_lines":[{"line_number":382,"context_line":""},{"line_number":383,"context_line":"    def test_install_copy_hook_removes_sysmeta_not_s3_request(self):"},{"line_number":384,"context_line":"        acl \u003d \u0027{\"Owner\":\"test:tester\",\u0027"},{"line_number":385,"context_line":"        \u0027\"Grant\":[{\"Permission\":\"FULL_CONTROL\",\"Grantee\":\"test:tester\"}]}\u0027"},{"line_number":386,"context_line":"        req \u003d Request.blank(\u0027/bucket/dest\u0027, method\u003d\u0027PUT\u0027)"},{"line_number":387,"context_line":"        src_headers \u003d {"},{"line_number":388,"context_line":"            \u0027X-Object-Sysmeta-S3api-Acl\u0027: acl,"}],"source_content_type":"text/x-python","patch_set":5,"id":"6b84a325_51bab3b6","line":385,"updated":"2026-08-24 15:06:25.000000000","message":"There is a hidden bug here - acl is simply a string `{\"Owner\":\"test:tester\",` - the second line is a Python statement. This is never evaluated properly. This only affects the test and could be fixed later - wdyt?","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"4c387ba3c0d3809ba30b0baf15d22c4dc70fae88","unresolved":false,"context_lines":[{"line_number":382,"context_line":""},{"line_number":383,"context_line":"    def test_install_copy_hook_removes_sysmeta_not_s3_request(self):"},{"line_number":384,"context_line":"        acl \u003d \u0027{\"Owner\":\"test:tester\",\u0027"},{"line_number":385,"context_line":"        \u0027\"Grant\":[{\"Permission\":\"FULL_CONTROL\",\"Grantee\":\"test:tester\"}]}\u0027"},{"line_number":386,"context_line":"        req \u003d Request.blank(\u0027/bucket/dest\u0027, method\u003d\u0027PUT\u0027)"},{"line_number":387,"context_line":"        src_headers \u003d {"},{"line_number":388,"context_line":"            \u0027X-Object-Sysmeta-S3api-Acl\u0027: acl,"}],"source_content_type":"text/x-python","patch_set":5,"id":"563322ea_f0a69b96","line":385,"in_reply_to":"02b79630_615b2a5c","updated":"2026-08-24 18:01:04.000000000","message":"Done","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":382,"context_line":""},{"line_number":383,"context_line":"    def test_install_copy_hook_removes_sysmeta_not_s3_request(self):"},{"line_number":384,"context_line":"        acl \u003d \u0027{\"Owner\":\"test:tester\",\u0027"},{"line_number":385,"context_line":"        \u0027\"Grant\":[{\"Permission\":\"FULL_CONTROL\",\"Grantee\":\"test:tester\"}]}\u0027"},{"line_number":386,"context_line":"        req \u003d Request.blank(\u0027/bucket/dest\u0027, method\u003d\u0027PUT\u0027)"},{"line_number":387,"context_line":"        src_headers \u003d {"},{"line_number":388,"context_line":"            \u0027X-Object-Sysmeta-S3api-Acl\u0027: acl,"}],"source_content_type":"text/x-python","patch_set":5,"id":"02b79630_615b2a5c","line":385,"in_reply_to":"6b84a325_51bab3b6","updated":"2026-08-24 15:16:49.000000000","message":"will fix!\n\nmy agent pointed out the same but I apparently only fixed it in the previous two tests :(","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":391,"context_line":"            \u0027X-Object-Sysmeta-S3api-Other\u0027: \u0027other\u0027,"},{"line_number":392,"context_line":"            \u0027X-Object-Sysmeta-Slo-Etag\u0027: \u0027slo-etag\u0027,"},{"line_number":393,"context_line":"            \u0027X-Object-Sysmeta-Slo-Size\u0027: \u0027slo-size\u0027,"},{"line_number":394,"context_line":"            \u0027X-Object-Sysmeta-Future\u0027: \u0027Unknown\u0027,"},{"line_number":395,"context_line":"            \u0027X-Object-Meta-Fruit\u0027: \u0027Banana\u0027,"},{"line_number":396,"context_line":"            \u0027X-Object-Sysmeta-Container-Update-Override-Etag\u0027:"},{"line_number":397,"context_line":"            \u0027etag; s3_etag\u003dabcd; slo_etag\u003d1234\u0027,"}],"source_content_type":"text/x-python","patch_set":5,"id":"14d1a49a_52d1e95f","line":394,"updated":"2026-08-22 02:04:47.000000000","message":"I don\u0027t like this - we\u0027re enshrining the responsibility for Slo sysmeta into the s3api copy hook at the same time as calling out it\u0027s NOT responsible for sysmeta future\n\nseems wishy washy\n\nhttps://review.opendev.org/c/openstack/swift/+/1001905/1/test/unit/common/middleware/s3api/test_utils.py#b383","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"0b0bd3cabf2918f9bbc06e6d76c27a13c3d8c575","unresolved":true,"context_lines":[{"line_number":391,"context_line":"            \u0027X-Object-Sysmeta-S3api-Other\u0027: \u0027other\u0027,"},{"line_number":392,"context_line":"            \u0027X-Object-Sysmeta-Slo-Etag\u0027: \u0027slo-etag\u0027,"},{"line_number":393,"context_line":"            \u0027X-Object-Sysmeta-Slo-Size\u0027: \u0027slo-size\u0027,"},{"line_number":394,"context_line":"            \u0027X-Object-Sysmeta-Future\u0027: \u0027Unknown\u0027,"},{"line_number":395,"context_line":"            \u0027X-Object-Meta-Fruit\u0027: \u0027Banana\u0027,"},{"line_number":396,"context_line":"            \u0027X-Object-Sysmeta-Container-Update-Override-Etag\u0027:"},{"line_number":397,"context_line":"            \u0027etag; s3_etag\u003dabcd; slo_etag\u003d1234\u0027,"}],"source_content_type":"text/x-python","patch_set":5,"id":"bcb734ce_08f94c30","line":394,"in_reply_to":"14d1a49a_52d1e95f","updated":"2026-08-24 15:16:49.000000000","message":"I\u0027m not happy with s3api having to tidy up slo, but I think it is justifiable given the coupling of MPUs with SLO. Better though if SLO can take care of it.","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"be24322bc6cca3922bff73751b67b1c1bb05701c","unresolved":false,"context_lines":[{"line_number":391,"context_line":"            \u0027X-Object-Sysmeta-S3api-Other\u0027: \u0027other\u0027,"},{"line_number":392,"context_line":"            \u0027X-Object-Sysmeta-Slo-Etag\u0027: \u0027slo-etag\u0027,"},{"line_number":393,"context_line":"            \u0027X-Object-Sysmeta-Slo-Size\u0027: \u0027slo-size\u0027,"},{"line_number":394,"context_line":"            \u0027X-Object-Sysmeta-Future\u0027: \u0027Unknown\u0027,"},{"line_number":395,"context_line":"            \u0027X-Object-Meta-Fruit\u0027: \u0027Banana\u0027,"},{"line_number":396,"context_line":"            \u0027X-Object-Sysmeta-Container-Update-Override-Etag\u0027:"},{"line_number":397,"context_line":"            \u0027etag; s3_etag\u003dabcd; slo_etag\u003d1234\u0027,"}],"source_content_type":"text/x-python","patch_set":5,"id":"ef57b310_2262e1cc","line":394,"in_reply_to":"bcb734ce_08f94c30","updated":"2026-08-24 21:33:36.000000000","message":"Acknowledged","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"}],"test/unit/common/middleware/test_copy.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"552b6b95767ea655442a7bc5495956a85a12381c","unresolved":true,"context_lines":[{"line_number":271,"context_line":""},{"line_number":272,"context_line":"            env[\u0027swift.callback.copy_source_hook\u0027] \u003d \\"},{"line_number":273,"context_line":"                second_copy_source_hook"},{"line_number":274,"context_line":"            return self.ssc(env, start_response)"},{"line_number":275,"context_line":""},{"line_number":276,"context_line":"        self.app.register("},{"line_number":277,"context_line":"            \u0027GET\u0027, \u0027/v1/a/c/source\u0027, swob.HTTPOk, {}, source_body)"}],"source_content_type":"text/x-python","patch_set":5,"id":"c5e47b87_93398c7b","line":274,"updated":"2026-08-22 02:04:47.000000000","message":"FWIW the ordering pipeline of\n\n`first second app`\n\nmakes sense to me; and I don\u0027t really know if `outer inner app` would have been any more obvious.","commit_id":"8c3f615c9f9b3d361e7e6f14552f7717376f1081"}]}
