botocore を 1.31.81 以降にアップデートしたら localstack への SQS アクセスが失敗した

ユニットテストで localstack のコンテナを使って SQS を使ったテストを行なっています。
そのテストが botocore 1.31.81 から落ちるようになり調査したメモ。

# エラーメッセージ
```
Traceback (most recent call last):
（中略）
  File "/app/.venv/lib/python3.8/site-packages/（中略）/sqs_fixture.py", line 50, in _initialize_queue
    client.create_queue(QueueName=queue_name)
  File "/app/.venv/lib/python3.8/site-packages/botocore/client.py", line 535, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/app/.venv/lib/python3.8/site-packages/botocore/client.py", line 980, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (500) when calling the CreateQueue operation (reached max retries: 4): <?xml version='1.0' enco
ding='utf-8'?>
<ErrorResponse xmlns="http://queue.amazonaws.com/doc/2012-11-05/"><Error><Code>InternalError</Code><Message>exception while calling sqs with unkno
wn operation: Operation detection failed. Missing Action in request for query-protocol service ServiceModel(sqs).</Message></Error><RequestId>0e01
c318-855b-4e78-96ce-65a632631fd3</RequestId></ErrorResponse>
```

> exception while calling sqs with unknown operation: Operation detection failed. Missing Action in request for query-protocol service ServiceModel(sqs).

- クライアントのリクエストの書式がおかしい
- Action の指定が抜けている

と言っている。

# 原因
おそらく 2023/11/09 現在 localstack のイメージが botocore 1.31.81 でのリクエストフォーマットに対応していない？

# 調査
https://github.com/boto/botocore/blob/1.31.81/botocore/client.py#L946 この SQS にリクエストを投げている場所をみて各引数がどうなっているか調査

```py
        handler, event_response = self.meta.events.emit_until_response(
            'before-call.{service_id}.{operation_name}'.format(
                service_id=service_id, operation_name=operation_name
            ),
            model=operation_model,
            params=request_dict,
            request_signer=self._request_signer,
            context=request_context,
        )
```

```:botocore1.31.80（テスト通る）
request:  before-call.sqs.CreateQueue
model: OperationModel(name=CreateQueue)
params: {'url_path': '/', 'query_string': '', 'method': 'POST', 'headers': {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'User-Agent': 'Boto3/1.28.80 md/Botocore#1.31.80 ua/2.0 os/linux#6.2.0-1009-aws md/arch#x86_64 lang/python#3.8.6 md/pyimpl#CPython cfg/retry-mode#legacy Botocore/1.31.80'}, 'body': {'Action': 'CreateQueue', 'Version': '2012-11-05', 'QueueName': 'test'}, 'url': 'http://localstack:4566/', 'context': {'client_region': 'ap-northeast-1', 'client_config': <botocore.config.Config object at 0x7ff38c83f6d0>, 'has_streaming_input': False, 'auth_type': None}}
request_signer: <botocore.signers.RequestSigner object at 0x7ff38c83f220>
context: {'client_region': 'ap-northeast-1', 'client_config': <botocore.config.Config object at 0x7ff38c83f6d0>, 'has_streaming_input': False, 'auth_type': None}
```

```:botocore1.31.82（テスト落ちる）
request: before-call.sqs.CreateQueue
model: OperationModel(name=CreateQueue)
params: {'url_path': '/', 'query_string': '', 'method': 'POST', 'headers': {'X-Amz-Target': 'AmazonSQS.CreateQueue', 'Content-Type': 'application/x-amz-json-1.0', 'User-Agent': 'Boto3/1.28.82 md/Botocore#1.31.82 ua/2.0 os/linux#6.2.0-1009-aws md/arch#x86_64 lang/python#3.8.6md/pyimpl#CPython cfg/retry-mode#legacy Botocore/1.31.82'}, 'body': b'{"QueueName": "test"}', 'url': 'http://localstack:4566/', 'context': {'client_region': 'ap-northeast-1', 'client_config': <botocore.config.Config object at 0x7fa63a973850>, 'has_streaming_input': False, 'auth_type': None}}
request_signer: <botocore.signers.RequestSigner object at 0x7fa63a973820>
context: {'client_region': 'ap-northeast-1', 'client_config': <botocore.config.Config object at 0x7fa63a973850>, 'has_streaming_input': False, 'auth_type': None}
```

見比べてみると、 params の body が異なる。（そもそも ContentType とかも異なる）

- 1.31.80（テスト通る）： `'body': {'Action': 'CreateQueue', 'Version': '2012-11-05', 'QueueName': 'test'}`
- 1.31.82（テスト落ちる）： `'body': b'{"QueueName": "test"}'`

確かに Action が 1.31.82 では抜けている。
実際の AWS との通信ができるかの確認はまだできてないが、流石にそこはできると仮定して、 localstack 側がその新しいフォーマットでのリクエストに対応していないのかもしれない。