Мы генерируем предварительно заданные URL-адреса, чтобы пользователи могли загружать файлы непосредственно в сегменты S3.Выполняя интеграционные тесты, мы обнаружили неудачный тест, в котором запрос HTTP PUT на предварительно назначенный URL-адрес дал ответ SignatureDoesNotMatch
об ошибке.Удивительно, но тот же код работал нормально, используя другое ведро.Мы продолжали примерять исходное ведро, которое привело к сбою теста, и были удивлены, когда оно неожиданно начало работать без каких-либо реальных изменений кода.
Мы заметили, что прошло примерно два часа после того, как мы создали ведро, когдатест успешно пройден.Поскольку мы находимся по UTC + 0200, мы подозревали, что проблема как-то связана с этой разницей во времени и / или с какой-то проблемой синхронизации часов.Мы намереваемся подтвердить наши подозрения, что тот же предварительно назначенный URL-адрес неожиданно заработает только после того, как пройдет достаточно времени.SPOILER: Это делает!
Следующий код создает совершенно новую корзину, генерирует предварительно назначенный URL-адрес, подходящий для загрузки файла (ClientMethod='put_object'
), и пытается HTTP PUT некоторые данные, используя requests
библиотека.Мы повторяем попытку PUTting данных каждые 60 секунд до тех пор, пока они не завершатся успешно через 5419 секунд (или 90 минут) после создания корзины.
Примечание. Даже если корзина впоследствии удаляется, выполняется тот же сценарий (с использованием того же сценария).имя ведра) теперь мгновенно получается.Если вы хотите еще раз подтвердить это поведение, убедитесь, что вы используете другое имя корзины во второй раз.
import logging
import time
import boto3
import requests
from botocore.client import Config
logger = logging.getLogger(__name__)
# region = "eu-central-1"
# region = "eu-west-1"
# region = "us-west-1"
region = "us-east-1"
s3_client = boto3.client('s3', region_name=region, config=Config(signature_version='s3v4'))
if __name__ == "__main__":
bucket_name = "some-globally-unique-bucket-name"
key_for_file = "test-file.txt"
# create bucket
if region == "us-east-1":
# https://github.com/boto/boto3/issues/125
s3_client.create_bucket(Bucket=bucket_name, ACL='private')
else:
s3_client.create_bucket(Bucket=bucket_name, ACL='private',
CreateBucketConfiguration={'LocationConstraint': region})
creation_time = time.time()
# generate presigned URL
file_data = b"Hello Test World"
expires_in = 4 * 3600
url = s3_client.generate_presigned_url(ClientMethod='put_object', ExpiresIn=expires_in,
Params={'Bucket': bucket_name, 'Key': key_for_file})
time_since_bucket_creation = time.time() - creation_time
time_interval = 60
max_time_passed = expires_in
success = False
try:
while time_since_bucket_creation < max_time_passed:
response = requests.put(url, data=file_data)
if response.status_code == 200:
success = True
break
if b"<Code>SignatureDoesNotMatch</Code>" in response.content:
reason = "SignatureDoesNotMatch"
else:
reason = str(response.content)
time_since_bucket_creation = time.time() - creation_time
print("="*50)
print(f"{time_since_bucket_creation:.2f} s after bucket creation")
print(f"unable to PUT data to url: {url}")
print(f"reason: {reason}")
print(response.content)
time.sleep(time_interval)
except KeyboardInterrupt:
print("Gracefully shutting down...")
if success:
print("YAY! File Upload was successful!")
time_since_bucket_creation = time.time() - creation_time
print(f"{time_since_bucket_creation:.2f} seconds after bucket creation")
s3_client.delete_object(Bucket=bucket_name, Key=key_for_file)
# delete bucket
s3_client.delete_bucket(Bucket=bucket_name)
Мы запускаем интеграционные тесты с кластером AWS EKS, где мы создаем кластер вместе с некоторымибазы данных, корзины S3 и т. д. и все разрушают после завершения испытаний.Необходимость ждать 90 минут для назначения URL-адресов для работы невозможна.
Мои вопросы
Я что-то не так делаю?
Это ожидаемое поведение?Есть ли приемлемый обходной путь?
Может ли кто-нибудь, пожалуйста, подтвердить это поведение, используя приведенный выше код?
РЕДАКТИРОВАТЬ
Я обновил код, чтобы создать корзину в "нас"-east-1 "регион, как предложено" Michael - sqlbot "в комментариях.Странный оператор if
необходим, как задокументировано здесь .Я могу подтвердить подозрение Майкла о том, что поведение НЕ воспроизводится с помощью «us-east-1».
В случае, если это представляет интерес, возвращаемый XML в случае ошибки:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<AWSAccessKeyId>REDACTED</AWSAccessKeyId>
<StringToSign>AWS4-HMAC-SHA256
20190609T170351Z
20190609/eu-central-1/s3/aws4_request
c143cb44fa45c56e52b04e61b777ae2206e0aaeed40dafc78e036878fa91dfd6</StringToSign>
<SignatureProvided>REDACTED</SignatureProvided>
<StringToSignBytes>REDACTED</StringToSignBytes>
<CanonicalRequest>PUT
/test-file.txt
X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=REDACTED%2F20190609%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20190609T170351Z&X-Amz-Expires=14400&X-Amz-SignedHeaders=host
host:some-globally-unique-bucket-name.s3.eu-central-1.amazonaws.com
host
UNSIGNED-PAYLOAD</CanonicalRequest>
<CanonicalRequestBytes>REDACTED</CanonicalRequestBytes>
<RequestId>E6CBBC7D2E4D322E</RequestId>
<HostId>j1dM1MNaXaDhzMUXKhqdHd6+/Rl1C3GzdL9YDq0CuP8brQZQV6vbyE9Z63HBHiBWSo+hb6zHKVs=</HostId>
</Error>