Coder Social home page Coder Social logo

thumbor-aws's Introduction

thumbor-aws

This is a project to provide modern thumbor>7.0.0 AWS Extensions.

Coverage Status

⚙️ Installation

pip install thumbor-aws

🎯 Features

  • Asynchronous non-blocking AWS S3 support
  • Conforms with thumbor 7 new storage and results storage specs
  • Python 3 compliant
  • S3 Image Loader - Load source images from S3
  • S3 Storage - Retrieve and store source images, detector data and security keys
  • S3 Result Storage - Retrieve and store resulting images
  • Compatibility mode for users of tc_aws: currently supported loader, storage and result storage

Usage

Configuring thumbor

Configure your thumbor.conf file to point to thumbor_aws:

## The loader thumbor should use to find source images.
## This must be the full name of a python module (python must be able to import it)
LOADER = "thumbor.loaders.http_loader"

## The file storage thumbor should use to store original images.
## This must be the full name of a python module (python must be able to import it)
STORAGE = 'thumbor_aws.storage'

## The result storage thumbor should use to store generated images.
## This must be the full name of a python module (python must be able to import it)
RESULT_STORAGE = 'thumbor_aws.result_storage'

You should use only the extensions required by your use case. There's no dependency between them.

Configuration

thumbor-aws allows you to configure each extension independently:

General

Some S3 providers fail to return a valid location header when uploading a new object. For that scenario, thumbor-aws allows users to set the location template to be used.

## Default location to use if S3 does not return location header.
## Can use {bucket_name} var.
## Defaults to: 'https://{bucket_name}.s3.amazonaws.com'
AWS_DEFAULT_LOCATION = "https://{bucket_name}.s3.amazonaws.com"

Loader

thumbor-aws loader offer several configuration options:

################################## AWS Loader ##################################

## Region where thumbor's objects are going to be loaded from.
## Defaults to: 'us-east-1'
#AWS_LOADER_REGION_NAME = 'us-east-1'

## S3 Bucket where thumbor's objects are loaded from.
## Defaults to: 'thumbor'
#AWS_LOADER_BUCKET_NAME = 'thumbor'

## Secret access key for S3 Loader.
## Defaults to: None
#AWS_LOADER_S3_SECRET_ACCESS_KEY = None

## Access key ID for S3 Loader.
## Defaults to: None
#AWS_LOADER_S3_ACCESS_KEY_ID = None

## Endpoint URL for S3 API. Very useful for testing.
## Defaults to: None
#AWS_LOADER_S3_ENDPOINT_URL = None

## Loader prefix path.
## Defaults to: '/st'
#AWS_LOADER_ROOT_PATH = '/st'

################################################################################

Storage

Below you can see the result of running thumbor's config generation after importing thumbor-aws:

################################# AWS Storage ##################################

## Region where thumbor's objects are going to be stored.
## Defaults to: 'us-east-1'
#AWS_STORAGE_REGION_NAME = 'us-east-1'

## S3 Bucket where thumbor's objects are going to be stored.
## Defaults to: 'thumbor'
#AWS_STORAGE_BUCKET_NAME = 'thumbor'

## Secret access key for S3 to allow thumbor to store objects there.
## Defaults to: None
#AWS_STORAGE_S3_SECRET_ACCESS_KEY = None

## Access key ID for S3 to allow thumbor to store objects there.
## Defaults to: None
#AWS_STORAGE_S3_ACCESS_KEY_ID = None

## Endpoint URL for S3 API. Very useful for testing.
## Defaults to: None
#AWS_STORAGE_S3_ENDPOINT_URL = None

## Storage prefix path.
## Defaults to: '/st'
#AWS_STORAGE_ROOT_PATH = '/st'

## Storage ACL for files written in bucket
## Defaults to: 'public-read'
#AWS_STORAGE_S3_ACL = 'public-read'

## Default location to use if S3 does not return location header. Can use
## {bucket_name} var.
## Defaults to: 'https://{bucket_name}.s3.amazonaws.com'
#AWS_DEFAULT_LOCATION = 'https://{bucket_name}.s3.amazonaws.com'

################################################################################

Result Storage

Below you can see the result of running thumbor's config generation after importing thumbor-aws:

############################## AWS Result Storage ##############################

## Region where thumbor's objects are going to be stored.
## Defaults to: 'us-east-1'
#AWS_RESULT_STORAGE_REGION_NAME = 'us-east-1'

## S3 Bucket where thumbor's objects are going to be stored.
## Defaults to: 'thumbor'
#AWS_RESULT_STORAGE_BUCKET_NAME = 'thumbor'

## Secret access key for S3 to allow thumbor to store objects there.
## Defaults to: None
#AWS_RESULT_STORAGE_S3_SECRET_ACCESS_KEY = None

## Access key ID for S3 to allow thumbor to store objects there.
## Defaults to: None
#AWS_RESULT_STORAGE_S3_ACCESS_KEY_ID = None

## Endpoint URL for S3 API. Very useful for testing.
## Defaults to: None
#AWS_RESULT_STORAGE_S3_ENDPOINT_URL = None

## Result Storage prefix path.
## Defaults to: '/rs'
#AWS_RESULT_STORAGE_ROOT_PATH = '/rs'

## ACL to use for storing items in S3.
## Defaults to: None
#AWS_RESULT_STORAGE_S3_ACL = None

################################################################################

Configuring thumbor in compatibility mode with tc_aws

If you are a tc_aws user, thumbor-aws has a compatibility mode where you can use the same configuration you are already used to:

############################# tc_aws Compatibility #############################

## Runs in compatibility mode using the configurations for tc_aws.
## Defaults to: False
THUMBOR_AWS_RUN_IN_COMPATIBILITY_MODE = True

## AWS Region the bucket is located in.
## Defaults to: 'us-east-1'
TC_AWS_REGION = 'us-east-1'

## Max retries for get image from S3 Bucket. Default is 0
## Defaults to: 0
TC_AWS_MAX_RETRY = 0  # This is not yet supported

## S3 bucket for Loader. If given, source urls are interpreted as keys within
## this bucket. If not given, source urls are expected to containthe bucket
## name, such as 's3-bucket/keypath'.
## Defaults to: ''
TC_AWS_LOADER_BUCKET = 'my-bucket'

## S3 path prefix for Loader bucket. If given, this is prefixed to all S3 keys.
## Defaults to: ''
TC_AWS_LOADER_ROOT_PATH = 'source-files'

## S3 bucket for Storage
## Defaults to: ''
TC_AWS_STORAGE_BUCKET = 'my-bucket'

## S3 path prefix for Storage bucket
## Defaults to: ''
TC_AWS_STORAGE_ROOT_PATH = 'source-files'

## put data into S3 using the Server Side Encryption functionality to encrypt
## data at rest in S3 https://aws.amazon.com/about-aws/whats-
## new/2011/10/04/amazon-s3-announces-server-side-encryption-support/
## Defaults to: False
TC_AWS_STORAGE_SSE = False  # This is not yet supported

## put data into S3 with Reduced Redundancy https://aws.amazon.com/about-
## aws/whats-new/2010/05/19/announcing-amazon-s3-reduced-redundancy-storage/
## Defaults to: False
TC_AWS_STORAGE_RRS = False  # This is not yet supported

## S3 bucket for result Storage
## Defaults to: ''
TC_AWS_RESULT_STORAGE_BUCKET = 'my-bucket'

## S3 path prefix for Result storage bucket
## Defaults to: ''
TC_AWS_RESULT_STORAGE_ROOT_PATH = 'result-storage'

## Store result with metadata (for instance content-type)
## Defaults to: False
# This configuration won't matter as thumbor-aws stores metadata anyway
TC_AWS_STORE_METADATA = False

################################################################################

Please notice the addition of THUMBOR_AWS_RUN_IN_COMPATIBILITY_MODE = True to tell thumbor_aws you want compatibility with tc_aws.

If you have any issues with this

Caveats

  1. thumbor-aws does not create buckets for you. If they don't exist you are getting errors.

You can easily create a bucket using aws-cli with:

$ aws s3api create-bucket --bucket <bucket name> --region <your region>

Or through your AWS Console UI.

Troubles?

If you experience any troubles, try running:

thumbor-doctor

If you still need help, please raise an issue.

👀 Thumbor

thumbor-aws stands on the shoulders of thumbor! If you are not familiar with thumbor, please check the docs or you can see a demo at http://thumborize.me/

👍 Contribute

thumbor-aws is an open-source project with many contributors. Join them contributing code or contributing documentation.

Join the chat at https://gitter.im/thumbor/thumbor

License

MIT License

Copyright (c) 2021 thumbor-aws (by @heynemann)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

thumbor-aws's People

Contributors

aaugustin avatar dependabot[bot] avatar guilhermef avatar heynemann avatar jairhenrique avatar tenzer avatar thomasf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

thumbor-aws's Issues

Location Headers was not found in response

Having just migrated from Thumbor 6 and tc-aws to Thumbor 7 and thumbor-aws, I got hit with a stream of these warning messages:

Unable to process response from AWS to <URL>: Location Headers was not found in response

I worked around it by changing the logging level of Thumbor to error (--log-level=error), but looking into this further I'm surprised everybody isn't hit by it, as I can't work out when the location header would be set on a file.

I tried to both upload a file to an S3 bucket with website hosting enabled and without, using aiobotocore like thumbor-aws does, and neither contained a location key in the HTTPHeaders dict. What is expected to set this header?

Would it perhaps be an idea to raise a warning rather than logging a warning message, so a line isn't emitted for each request Thumbor gets?
Is the warning message necessary at all?

casting None values as a string

From what I understand, AWS_RESULT_STORAGE_S3_ENDPOINT_URL is an optional variable that should be specified when using custom s3 endpoint other than AWS. When I do not specify the value, your config template defaults it to None which throws the following error:

[thumbor-staging] [2024-03-26 02:27:47] 2024-03-26 02:27:47 thumbor:ERROR ERROR: Traceback (most recent call last):
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/tornado/web.py", line 1790, in _execute
[thumbor-staging] [2024-03-26 02:27:47]     result = await result
[thumbor-staging] [2024-03-26 02:27:47]              ^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/thumbor/handlers/upload.py", line 67, in post
[thumbor-staging] [2024-03-26 02:27:47]     await self.write_file(image_id, body)
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/thumbor/handlers/__init__.py", line 1084, in write_file
[thumbor-staging] [2024-03-26 02:27:47]     await storage.put(file_id, body)
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/thumbor_aws/storage.py", line 98, in put
[thumbor-staging] [2024-03-26 02:27:47]     path = await self.upload(
[thumbor-staging] [2024-03-26 02:27:47]            ^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/thumbor_aws/s3_client.py", line 110, in upload
[thumbor-staging] [2024-03-26 02:27:47]     async with self.get_client() as client:
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/session.py", line 25, in __aenter__
[thumbor-staging] [2024-03-26 02:27:47]     self._client = await self._coro
[thumbor-staging] [2024-03-26 02:27:47]                    ^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/session.py", line 215, in _create_client
[thumbor-staging] [2024-03-26 02:27:47]     client = await client_creator.create_client(
[thumbor-staging] [2024-03-26 02:27:47]              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/client.py", line 77, in create_client
[thumbor-staging] [2024-03-26 02:27:47]     client_args = self._get_client_args(
[thumbor-staging] [2024-03-26 02:27:47]                   ^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/client.py", line 283, in _get_client_args
[thumbor-staging] [2024-03-26 02:27:47]     return args_creator.get_client_args(
[thumbor-staging] [2024-03-26 02:27:47]            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/args.py", line 76, in get_client_args
[thumbor-staging] [2024-03-26 02:27:47]     endpoint = endpoint_creator.create_endpoint(
[thumbor-staging] [2024-03-26 02:27:47]                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:27:47]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/endpoint.py", line 308, in create_endpoint
[thumbor-staging] [2024-03-26 02:27:47]     raise ValueError("Invalid endpoint: %s" % endpoint_url)
[thumbor-staging] [2024-03-26 02:27:47] ValueError: Invalid endpoint: None
[thumbor-staging] [2024-03-26 02:27:47] 
[thumbor-sta

I've used the following environment variables:

ENV

UPLOAD_ENABLED=True
LOADER=tc_aws.loaders.s3_loader
STORAGE=tc_aws.storages.s3_storage
RESULT_STORAGE=tc_aws.result_storages.s3_storage
ALLOW_UNSAFE_URL=True
RESULT_STORAGE_STORES_UNSAFE=True
UPLOAD_PHOTO_STORAGE=tc_aws.storages.s3_storage
AWS_LOADER_REGION_NAME = 'us-east-1'
AWS_LOADER_BUCKET_NAME = 'thumbor-cdn.staging.company.com'
AWS_LOADER_S3_SECRET_ACCESS_KEY = 'qjXXXXXXXXXXXXXXXX4Sn'
AWS_LOADER_S3_ACCESS_KEY_ID = 'AKXXXXXXXXXXXXXXXELQ'
AWS_LOADER_ROOT_PATH = 'loader'
AWS_STORAGE_REGION_NAME = 'us-east-1'
AWS_STORAGE_BUCKET_NAME = 'thumbor-cdn.staging.company.com'
AWS_STORAGE_S3_SECRET_ACCESS_KEY = 'qjXXXXXXXXXXXXXXXX4Sn'
AWS_STORAGE_S3_ACCESS_KEY_ID = ''AKXXXXXXXXXXXXXXXELQ'
AWS_STORAGE_ROOT_PATH = 'storage'
AWS_RESULT_STORAGE_REGION_NAME = 'us-east-1'
AWS_RESULT_STORAGE_BUCKET_NAME = 'thumbor-cdn.staging.company.com'
AWS_RESULT_STORAGE_S3_SECRET_ACCESS_KEY = 'qjXXXXXXXXXXXXXXXX4Sn'
AWS_RESULT_STORAGE_S3_ACCESS_KEY_ID = 'AKXXXXXXXXXXXXXXXELQ'
AWS_RESULT_STORAGE_ROOT_PATH = 'result'

Error:

[thumbor-staging] [2024-03-26 02:27:47] ValueError: Invalid endpoint: None

Appending the endpoint variables to those above results in the following error:

ENV

AWS_LOADER_S3_ENDPOINT_URL = 'https://thumbor-cdn.staging.company.com'
AWS_STORAGE_S3_ENDPOINT_URL = 'https://thumbor-cdn.staging.company.com'
AWS_RESULT_STORAGE_S3_ENDPOINT_URL = 'https://thumbor-cdn.staging.company.com'

Error

[thumbor-staging] [2024-03-26 02:51:07] 2024-03-26 02:51:07 thumbor:ERROR Unable to upload image to storage/77f808481d4947e69e8432fca64f44ee: Could not connect to the endpoint URL: "https://thumbor-cdn.staging.company.com/thumbor-cdn.staging.company.com/storage/77f808481d4947e69e8432fca64f44ee" (<class 'botocore.exceptions.EndpointConnectionError'>)
[thumbor-staging] [2024-03-26 02:51:07] 2024-03-26 02:51:07 thumbor:ERROR ERROR: Traceback (most recent call last):
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 1173, in _create_direct_connection
[thumbor-staging] [2024-03-26 02:51:07]     hosts = await asyncio.shield(host_resolved)
[thumbor-staging] [2024-03-26 02:51:07]             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 884, in _resolve_host
[thumbor-staging] [2024-03-26 02:51:07]     addrs = await self._resolver.resolve(host, port, family=self._family)
[thumbor-staging] [2024-03-26 02:51:07]             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiohttp/resolver.py", line 33, in resolve
[thumbor-staging] [2024-03-26 02:51:07]     infos = await self._loop.getaddrinfo(
[thumbor-staging] [2024-03-26 02:51:07]             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/asyncio/base_events.py", line 868, in getaddrinfo
[thumbor-staging] [2024-03-26 02:51:07]     return await self.run_in_executor(
[thumbor-staging] [2024-03-26 02:51:07]            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run
[thumbor-staging] [2024-03-26 02:51:07]     result = self.fn(*self.args, **self.kwargs)
[thumbor-staging] [2024-03-26 02:51:07]              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/socket.py", line 962, in getaddrinfo
[thumbor-staging] [2024-03-26 02:51:07]     for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
[thumbor-staging] [2024-03-26 02:51:07]                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07] socket.gaierror: [Errno -2] Name does not resolve
[thumbor-staging] [2024-03-26 02:51:07] 
[thumbor-staging] [2024-03-26 02:51:07] The above exception was the direct cause of the following exception:
[thumbor-staging] [2024-03-26 02:51:07] 
[thumbor-staging] [2024-03-26 02:51:07] Traceback (most recent call last):
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/httpsession.py", line 208, in send
[thumbor-staging] [2024-03-26 02:51:07]     response = await self._session.request(
[thumbor-staging] [2024-03-26 02:51:07]                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiohttp/client.py", line 578, in _request
[thumbor-staging] [2024-03-26 02:51:07]     conn = await self._connector.connect(
[thumbor-staging] [2024-03-26 02:51:07]            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 544, in connect
[thumbor-staging] [2024-03-26 02:51:07]     proto = await self._create_connection(req, traces, timeout)
[thumbor-staging] [2024-03-26 02:51:07]             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 911, in _create_connection
[thumbor-staging] [2024-03-26 02:51:07]     _, proto = await self._create_direct_connection(req, traces, timeout)
[thumbor-staging] [2024-03-26 02:51:07]                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiohttp/connector.py", line 1187, in _create_direct_connection
[thumbor-staging] [2024-03-26 02:51:07]     raise ClientConnectorError(req.connection_key, exc) from exc
[thumbor-staging] [2024-03-26 02:51:07] aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host thumbor-cdn.staging.company.com:443 ssl:default [Name does not resolve]
[thumbor-staging] [2024-03-26 02:51:07] 
[thumbor-staging] [2024-03-26 02:51:07] During handling of the above exception, another exception occurred:
[thumbor-staging] [2024-03-26 02:51:07] 
[thumbor-staging] [2024-03-26 02:51:07] Traceback (most recent call last):
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/thumbor_aws/s3_client.py", line 122, in upload
[thumbor-staging] [2024-03-26 02:51:07]     response = await client.put_object(**settings)
[thumbor-staging] [2024-03-26 02:51:07]                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/client.py", line 388, in _make_api_call
[thumbor-staging] [2024-03-26 02:51:07]     http, parsed_response = await self._make_request(
[thumbor-staging] [2024-03-26 02:51:07]                             ^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/client.py", line 416, in _make_request
[thumbor-staging] [2024-03-26 02:51:07]     return await self._endpoint.make_request(
[thumbor-staging] [2024-03-26 02:51:07]            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/endpoint.py", line 100, in _send_request
[thumbor-staging] [2024-03-26 02:51:07]     while await self._needs_retry(
[thumbor-staging] [2024-03-26 02:51:07]           ^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/endpoint.py", line 262, in _needs_retry
[thumbor-staging] [2024-03-26 02:51:07]     responses = await self._event_emitter.emit(
[thumbor-staging] [2024-03-26 02:51:07]                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/hooks.py", line 66, in _emit
[thumbor-staging] [2024-03-26 02:51:07]     response = await resolve_awaitable(handler(**kwargs))
[thumbor-staging] [2024-03-26 02:51:07]                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/_helpers.py", line 15, in resolve_awaitable
[thumbor-staging] [2024-03-26 02:51:07]     return await obj
[thumbor-staging] [2024-03-26 02:51:07]            ^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/retryhandler.py", line 107, in _call
[thumbor-staging] [2024-03-26 02:51:07]     if await resolve_awaitable(self._checker(**checker_kwargs)):
[thumbor-staging] [2024-03-26 02:51:07]        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/_helpers.py", line 15, in resolve_awaitable
[thumbor-staging] [2024-03-26 02:51:07]     return await obj
[thumbor-staging] [2024-03-26 02:51:07]            ^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/retryhandler.py", line 126, in _call
[thumbor-staging] [2024-03-26 02:51:07]     should_retry = await self._should_retry(
[thumbor-staging] [2024-03-26 02:51:07]                    ^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/retryhandler.py", line 165, in _should_retry
[thumbor-staging] [2024-03-26 02:51:07]     return await resolve_awaitable(
[thumbor-staging] [2024-03-26 02:51:07]            ^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/_helpers.py", line 15, in resolve_awaitable
[thumbor-staging] [2024-03-26 02:51:07]     return await obj
[thumbor-staging] [2024-03-26 02:51:07]            ^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/retryhandler.py", line 174, in _call
[thumbor-staging] [2024-03-26 02:51:07]     checker(attempt_number, response, caught_exception)
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/botocore/retryhandler.py", line 247, in __call__
[thumbor-staging] [2024-03-26 02:51:07]     return self._check_caught_exception(
[thumbor-staging] [2024-03-26 02:51:07]            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/botocore/retryhandler.py", line 416, in _check_caught_exception
[thumbor-staging] [2024-03-26 02:51:07]     raise caught_exception
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/endpoint.py", line 181, in _do_get_response
[thumbor-staging] [2024-03-26 02:51:07]     http_response = await self._send(request)
[thumbor-staging] [2024-03-26 02:51:07]                     ^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/endpoint.py", line 285, in _send
[thumbor-staging] [2024-03-26 02:51:07]     return await self.http_session.send(request)
[thumbor-staging] [2024-03-26 02:51:07]            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/aiobotocore/httpsession.py", line 253, in send
[thumbor-staging] [2024-03-26 02:51:07]     raise EndpointConnectionError(endpoint_url=request.url, error=e)
[thumbor-staging] [2024-03-26 02:51:07] botocore.exceptions.EndpointConnectionError: Could not connect to the endpoint URL: "https://thumbor-cdn.staging.company.com/thumbor-cdn.staging.company.com/storage/77f808481d4947e69e8432fca64f44ee"
[thumbor-staging] [2024-03-26 02:51:07] 
[thumbor-staging] [2024-03-26 02:51:07] During handling of the above exception, another exception occurred:
[thumbor-staging] [2024-03-26 02:51:07] 
[thumbor-staging] [2024-03-26 02:51:07] Traceback (most recent call last):
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/tornado/web.py", line 1790, in _execute
[thumbor-staging] [2024-03-26 02:51:07]     result = await result
[thumbor-staging] [2024-03-26 02:51:07]              ^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/thumbor/handlers/upload.py", line 67, in post
[thumbor-staging] [2024-03-26 02:51:07]     await self.write_file(image_id, body)
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/thumbor/handlers/__init__.py", line 1084, in write_file
[thumbor-staging] [2024-03-26 02:51:07]     await storage.put(file_id, body)
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/thumbor_aws/storage.py", line 98, in put
[thumbor-staging] [2024-03-26 02:51:07]     path = await self.upload(
[thumbor-staging] [2024-03-26 02:51:07]            ^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07]   File "/usr/local/lib/python3.11/site-packages/thumbor_aws/s3_client.py", line 126, in upload
[thumbor-staging] [2024-03-26 02:51:07]     raise RuntimeError(msg)  # pylint: disable=raise-missing-from
[thumbor-staging] [2024-03-26 02:51:07]     ^^^^^^^^^^^^^^^^^^^^^^^
[thumbor-staging] [2024-03-26 02:51:07] RuntimeError: Unable to upload image to storage/77f808481d4947e69e8432fca64f44ee: Could not connect to the endpoint URL: "https://thumbor-cdn.staging.company.com/thumbor-cdn.staging.company.com/storage/77f808481d4947e69e8432fca64f44ee" (<class 'botocore.exceptions.EndpointConnectionError'>)
[thumbor-staging] [2024-03-26 02:51:07] 
[thumbor-staging] [2024-03-26 02:51:07] 2024-03-26 02:51:07 tornado.access:ERROR 500 POST /image (172.71.150.178) 8986.47ms

It's obviously appending the endpoint URL to the bucketname which is not the desired end result.

Original Issue:
beeyev/thumbor-s3-docker#32

I suspect the issue to be that the endpoint_url setters are not checked for None values in the following places:

"endpoint_url", self.config.AWS_STORAGE_S3_ENDPOINT_URL

def endpoint_url(self) -> str:

"endpoint_url": context.config.AWS_LOADER_S3_ENDPOINT_URL,

"AWS_STORAGE_S3_ENDPOINT_URL",

This should apply to any other variables expecting a possible None value.

lower performance compared to tc_aws

Hi, i switched from tc_aws [7.0.2] to thumbor-aws [0.6.0], but i had to revert cause response time increased by ~ 200 ms.
My configuration is very simple and i use loader only (Bucket is get from url).

thumbor-aws conf:

AWS_LOADER_REGION_NAME = "eu-west-1"                                      
AWS_LOADER_BUCKET_NAME = None                                                          
AWS_LOADER_ROOT_PATH = ""                                                              
AWS_STORAGE_REGION_NAME = ""                                                           
AWS_STORAGE_S3_SECRET_ACCESS_KEY = ""                                                  
AWS_STORAGE_S3_ACCESS_KEY_ID = ""                                                      
AWS_STORAGE_S3_ENDPOINT_URL = ""

tc_aws conf:

TC_AWS_REGION = "eu-west-1"
TC_AWS_ENABLE_HTTP_LOADER = True

Could be probably related to the improvements made on tc_aws by @amanagr amanagr/aws#1 ?
(For whole PR thread: thumbor-community/aws#147).
I will try to investigate further and let you know if i discover something relevant.

Regards, Alberto.

Paths with URL encoded characters

Having just migrated from Thumbor 6 and tc-aws to Thumbor 7 and thumbor-aws, I found we had an issue with requests for files with names that require URL encoded characters. They would all result in 404s when using the thumbor-aws loader.

I narrowed this down to be a missing URL decode call on the file path before loading the file from S3. You can see the equivalent decode call in tc-aws here: https://github.com/thumbor-community/aws/blob/master/tc_aws/loaders/__init__.py#L20.

I tried a small fix by creating a new loader that did the URL decoding and otherwise passed the requests onto thumbor_aws.loader:

from urllib.parse import unquote

from thumbor_aws.loader import load as upstream_load


async def load(context, path):
    """Patched version of thumbor_aws.loader.load which does URL decoding before requesting file from S3."""

    path = unquote(path)
    return await upstream_load(context, path)

That fixed the problem for me.

I then tried to see if I could contribute a fix for this in thumbor-aws, but I haven't been able to reproduce the problem in tests, which makes me wonder if there's something about our setup that means we exhibit this problem.

We've got requests arriving via CloudFront -> Nginx (does remapping of URLs) -> Thumbor. I do however not see how URLs for filenames that for instance contain spaces otherwise could be passed around, unless they are URL encoded, and thus would need to be URL decoded before requesting them from S3.

Adding something like my workaround to the thumbor_aws.loader doesn't break any of the tests, which makes me think it might be okay to add it in, but I would also like to be able to reproduce the problem in tests first.

Does paths that require URL encoded characters work fine for other people?

Unable to use storage

I'm using minio (an AWS S3 compatible storage) for testing this extension. I've bootstrapped it via docker-compose, and it usually works fine. But requesting an image, it fails with following error:

thumbor_1  | 2022-01-22 16:31:10 thumbor:ERROR ERROR: Traceback (most recent call last):
thumbor_1  |   File "/usr/local/thumbor/.local/lib/python3.10/site-packages/tornado/web.py", line 1704, in _execute
thumbor_1  |     result = await result
thumbor_1  |   File "/usr/local/thumbor/.local/lib/python3.10/site-packages/thumbor/handlers/imaging.py", line 97, in get
thumbor_1  |     return await self.check_image(kw)
thumbor_1  |   File "/usr/local/thumbor/.local/lib/python3.10/site-packages/thumbor/handlers/imaging.py", line 26, in check_image
thumbor_1  |     exists = await self.context.modules.storage.exists(
thumbor_1  |   File "/usr/local/thumbor/.local/lib/python3.10/site-packages/thumbor_aws/storage.py", line 73, in exists
thumbor_1  |     return await self.object_exists(path)
thumbor_1  |   File "/usr/local/thumbor/.local/lib/python3.10/site-packages/thumbor_aws/s3_client.py", line 234, in object_exists
thumbor_1  |     await client.get_object_acl(
thumbor_1  |   File "/usr/local/thumbor/.local/lib/python3.10/site-packages/aiobotocore/client.py", line 187, in _make_api_call
thumbor_1  |     raise error_class(parsed_response, operation_name)
thumbor_1  | botocore.exceptions.ClientError: An error occurred (XMinioInvalidObjectName) when calling the GetObjectAcl operation: Object name contains unsupported characters.
thumbor_1  | 
thumbor_1  | 2022-01-22 16:31:10 tornado.access:ERROR 500 GET /unsafe/https://raw.githubusercontent.com/thumbor/thumbor/master/docs/images/blur_before.jpg (172.18.83.1) 68.94ms

Calling with: http://localhost:8888/unsafe/https://raw.githubusercontent.com/thumbor/thumbor/master/docs/images/blur_before.jpg

Using the following configuration:

      STORAGE: thumbor_aws.storage
      AWS_STORAGE_S3_ENDPOINT_URL: http://minio:9000
      AWS_STORAGE_BUCKET_NAME: mystorage
      AWS_STORAGE_S3_ACCESS_KEY_ID: generated
      AWS_STORAGE_S3_SECRET_ACCESS_KEY: generated

The AWS bucket is accessible:

Screenshot from 2022-01-22 17-42-19

According to this issue, there was an issue in the client library. Maybe there is some escaping missing?

I need help, I can't properly process pictures from S3.

First of all, I'm sorry, I don't understand English, I usually use Google translate, if I didn't express my question clearly, please understand, thank you!
I'm using an object storage compatible with AWS S3 protocol, when I'm done configuring Thumbor, try to run the image fetched from S3, Thumbor got a 500 error and I need help.

This is my config file

LOADER = 'thumbor.loaders.http_loader'
STORAGE = 'thumbor_aws.storage'
RESULT_STORAGE = 'thumbor_aws.result_storage'
################################# AWS Storage ##################################
AWS_STORAGE_REGION_NAME = 'ap-beijing'
AWS_STORAGE_BUCKET_NAME = 'img'
AWS_STORAGE_S3_SECRET_ACCESS_KEY = 'XXXXXXXXX'
AWS_STORAGE_S3_ACCESS_KEY_ID = 'XXXXXXXXX'
AWS_STORAGE_S3_ENDPOINT_URL = 'https://cos.ap-beijing.myqcloud.com'
AWS_STORAGE_ROOT_PATH = '/storage'
AWS_STORAGE_S3_ACL = None
AWS_DEFAULT_LOCATION = 'https://img.cos.ap-beijing.myqcloud.com'

################################################################################


############################## AWS Result Storage ##############################
AWS_RESULT_STORAGE_REGION_NAME = 'ap-beijing'
AWS_RESULT_STORAGE_BUCKET_NAME = 'img'
AWS_RESULT_STORAGE_S3_SECRET_ACCESS_KEY = 'XXXXXXXX'
AWS_RESULT_STORAGE_S3_ACCESS_KEY_ID = 'XXXXXX'
AWS_RESULT_STORAGE_S3_ENDPOINT_URL = 'https://cos.ap-beijing.myqcloud.com'
AWS_RESULT_STORAGE_ROOT_PATH = '/result-storage'
AWS_RESULT_STORAGE_S3_ACL = None

################################################################################

My environment configuration

thumbor        7.0.6
thumbor-aws    0.4.0
S3             private read and write

Access

http://127.0.0.1:8689/unsafe/20221/a78a8a02-6c94-452e-9322-62824c458acd.jpg

Error

2022-02-18 03:14:09 thumbor:ERROR ERROR: Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/thumbor/handlers/__init__.py", line 195, in get_image
    result = await self._fetch(self.context.request.image_url)
  File "/usr/local/lib/python3.9/dist-packages/thumbor/handlers/__init__.py", line 732, in _fetch
    loader_result = await self.context.modules.loader.load(self.context, url)
  File "/usr/local/lib/python3.9/dist-packages/thumbor/loaders/http_loader.py", line 191, in load
    response = await client.fetch(req, raise_error=True)
TimeoutError: [Errno 110] Connection timed out

2022-02-18 03:14:09 thumbor:ERROR [BaseHandler] get_image failed for url `20221/a78a8a02-6c94-452e-9322-62824c458acd.jpg`. error: `[Errno 110] Connection timed out`
2022-02-18 03:14:09 tornado.access:ERROR 500 GET /unsafe/20221/a78a8a02-6c94-452e-9322-62824c458acd.jpg (127.0.0.1) 3231.83ms

Access

http://127.0.0.1:8689/unsafe/https://www.baidu.com/img/PC_880906d2a4ad95f5fafb2e540c5cdad7.png

Error

2022-02-18 03:12:18 thumbor:ERROR [BaseHander.finish_request] 
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/thumbor/handlers/__init__.py", line 514, in finish_request
    result = await self.context.thread_pool.queue(
  File "/usr/local/lib/python3.9/dist-packages/thumbor/threadpool.py", line 53, in queue
    return await self._execute_in_foreground(operation, *args)
  File "/usr/local/lib/python3.9/dist-packages/thumbor/threadpool.py", line 45, in _execute_in_foreground
    return operation(*args)
  File "/usr/local/lib/python3.9/dist-packages/thumbor/handlers/__init__.py", line 428, in _load_results
    image_extension, content_type = self.define_image_type(context, None)
  File "/usr/local/lib/python3.9/dist-packages/thumbor/handlers/__init__.py", line 393, in define_image_type
    elif self.can_auto_convert_png_to_jpg():
  File "/usr/local/lib/python3.9/dist-packages/thumbor/handlers/__init__.py", line 372, in can_auto_convert_png_to_jpg
    return self.context.request.engine.can_auto_convert_png_to_jpg()
  File "/usr/local/lib/python3.9/dist-packages/thumbor/engines/__init__.py", line 398, in can_auto_convert_png_to_jpg
    can_convert = self.extension == ".png" and not self.has_transparency()
  File "/usr/local/lib/python3.9/dist-packages/thumbor_vips_engine/engine.py", line 151, in has_transparency
    raise NotImplementedError()
NotImplementedError
2022-02-18 03:12:18 thumbor:WARNING Error while trying to fetch the image: 
2022-02-18 03:12:18 tornado.access:ERROR 500 GET /unsafe/https://www.baidu.com/img/PC_880906d2a4ad95f5fafb2e540c5cdad7.png (127.0.0.1) 329.15ms

I think Thumbor's configuration on AWS S3 is very simple, but I have these problems, I try to solve them and find that nothing works, I would like to get help from the developers, I would appreciate any reply.
I have been using Thumbor 6.0 for the past two years, and I have to say, Thumbor is an excellent work!

TC_AWS_LOADER_BUCKET = '' not setting bucket from request URL

Per the tc-aws compatibility section of the docs:

## this bucket. If not given, source urls are expected to containthe bucket
## name, such as 's3-bucket/keypath'.
## Defaults to: ''

I have this value un-set. I am requesting the following URL:
/redactedhash=/96x96/one_of_my_s3_buckets/some_image.jpeg

and i am getting the following error in the thumbor error output:

2022-04-24 19:02:13 thumbor:ERROR ERROR: Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/thumbor/handlers/__init__.py", line 212, in get_image
    result = await self._fetch(self.context.request.image_url)
  File "/usr/local/lib/python3.7/dist-packages/thumbor/handlers/__init__.py", line 806, in _fetch
    self.context, url
  File "/usr/local/lib/python3.7/dist-packages/thumbor_aws/loader.py", line 85, in load
    norm_path, expiration=None
  File "/usr/local/lib/python3.7/dist-packages/thumbor_aws/s3_client.py", line 154, in get_data
    Bucket=self.bucket_name, Key=path
  File "/usr/local/lib/python3.7/dist-packages/aiobotocore/client.py", line 199, in _make_api_call
    api_params, operation_model, context=request_context)
  File "/usr/local/lib/python3.7/dist-packages/aiobotocore/client.py", line 247, in _convert_to_request_dict
    api_params, operation_model, context)
  File "/usr/local/lib/python3.7/dist-packages/aiobotocore/client.py", line 279, in _emit_api_params
    params=api_params, model=operation_model, context=context)
  File "/usr/local/lib/python3.7/dist-packages/aiobotocore/hooks.py", line 29, in _emit
    response = handler(**kwargs)
  File "/usr/local/lib/python3.7/dist-packages/botocore/handlers.py", line 243, in validate_bucket_name
    raise ParamValidationError(report=error_msg)
botocore.exceptions.ParamValidationError: Parameter validation failed:
Invalid bucket name "": Bucket name must match the regex "^[a-zA-Z0-9.\-_]{1,255}$" or be an ARN matching the regex "^arn:(aws).*:(s3|s3-object-lambda):[a-z\-0-9]*:[0-9]{12}:accesspoint[/:][a-zA-Z0-9\-.]{1,63}$|^arn:(aws).*:s3-outposts:[a-z\-0-9]+:[0-9]{12}:outpost[/:][a-zA-Z0-9\-]{1,63}[/:]accesspoint[/:][a-zA-Z0-9\-]{1,63}$"

2022-04-24 19:02:13 thumbor:ERROR [BaseHandler] get_image failed for url `one_of_my_s3_buckets/some_image.jpeg`. error: `Parameter validation failed:
Invalid bucket name "": Bucket name must match the regex "^[a-zA-Z0-9.\-_]{1,255}$" or be an ARN matching the regex "^arn:(aws).*:(s3|s3-object-lambda):[a-z\-0-9]*:[0-9]{12}:accesspoint[/:][a-zA-Z0-9\-.]{1,63}$|^arn:(aws).*:s3-outposts:[a-z\-0-9]+:[0-9]{12}:outpost[/:][a-zA-Z0-9\-]{1,63}[/:]accesspoint[/:][a-zA-Z0-9\-]{1,63}$"`
2022-04-24 19:02:13 tornado.access:ERROR 500 GET /redactedhash=/96x96/one_of_my_s3_buckets/some_image.jpeg (10.0.10.206) 118.34ms

URL converts to file path in S3

Hello,

Just started to use thumbor-aws and faced a problem that when i convert an image, in my S3 storage creates a bunch of a directories based on my request. It is unwanted behaviour, so i tried to find a config parameter where i can change that. Only thing about result storage path i found is AWS_RESULT_STORAGE_ROOT_PATH which in essence just a prefix.

For example, if i use
curl 'http://localhost:8888/unsafe/filters:format(webp)/f6c8886be90e4f35a876d3b371a7bf1e/photo.jpg'
I have a path like this: {prefix}/unsafe/filters:format(webp)/

I searched in source files and found that
image

So based on this code i can assume that currently there is no way to set path like i want and not generate it from URL. Is this feature planned or there is no hope?

Thanks

ResultStorage upload failure should not break the process

Let's build a setup: thumbor 7.0.10 + thumbor-aws 0.4.3 + a minio instance.
Minio storage is full, so the ResultStorage.put currently trigger an error:

2022-08-08 16:17:23 thumbor:ERROR ERROR: Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/thumbor_aws/s3_client.py", line 122, in upload
    response = await client.put_object(**settings)
  File "/usr/local/lib/python3.10/site-packages/aiobotocore/client.py", line 265, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (XMinioStorageFull) when calling the PutObject operation: Storage backend has reached its minimum free disk threshold. Please delete a few objects to proceed.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/tornado/web.py", line 1713, in _execute
    result = await result
  File "/usr/local/lib/python3.10/site-packages/thumbor/handlers/imaging.py", line 119, in get
    return await self.check_image(kw)
  File "/usr/local/lib/python3.10/site-packages/thumbor/handlers/imaging.py", line 116, in check_image
    return await self.execute_image_operations()
  File "/usr/local/lib/python3.10/site-packages/thumbor/handlers/__init__.py", line 203, in execute_image_operations
    await self.get_image()
  File "/usr/local/lib/python3.10/site-packages/thumbor/handlers/__init__.py", line 301, in get_image
    await self.after_transform()
  File "/usr/local/lib/python3.10/site-packages/thumbor/handlers/__init__.py", line 346, in after_transform
    await self.finish_request()
  File "/usr/local/lib/python3.10/site-packages/thumbor/handlers/__init__.py", line 579, in finish_request
    await self._store_results(result_storage, metrics, results)
  File "/usr/local/lib/python3.10/site-packages/thumbor/handlers/__init__.py", line 660, in _store_results
    await result_storage.put(results)
  File "/usr/local/lib/python3.10/site-packages/thumbor_aws/result_storage.py", line 141, in put
    response = await self.upload(
  File "/usr/local/lib/python3.10/site-packages/thumbor_aws/s3_client.py", line 126, in upload
    raise RuntimeError(msg)  # pylint: disable=raise-missing-from
RuntimeError: Unable to upload image to /rs/bdehnPEqltumHiGwGNindoOr_xM=/516x516/https://files.example.com/uploaders/23779347/illustration-file/7409c4b2-0024-4c25-9759-3671e33c2bca/untitled.153.jpg: An error occurred (XMinioStorageFull) when calling the PutObject operation: Storage backend has reached its minimum free disk threshold. Please delete a few objects to proceed. (<class 'botocore.exceptions.ClientError'>)

At this level, the image got processed.
Currently thumbor_aws raise and break totally the request (500)

I suggest to log the error, but return the response image to the upstream server: no reason to break the whole system because we can no longer cache our work.

Is the pycurl dependency really necessary?

pycurl is slightly difficult to install, as it requires compilation and doesn't offer wheel.

It's an optional dependency of thumbor. I see it's a mandatory dependency of thumbor-aws but it doesn't appear to be used anywhere. Am I missing a reason why it's necessary?

Ability to use instance credentials

Hi

Is there any way to use instance profile credentials with this library?
Looking through the codebase it seems like access key / secret is the only option?

Thanks

Missing STORAGE_ROOT_PATH

Just playing around and just noticed I'm missing a configuration: STORAGE_ROOT_PATH (or similar). Didn't find evidence in code base 😅

A AWS_RESULT_STORAGE_ROOT_PATH does exist, indeed.

SSE support

I is server side encryption supported ? Can it be en/disabled ? If not, is it somewhere on the roadmap?

Python 3.11 support

I just tried to see if I could use thumbor-aws with Python 3.11, but the project specifies explicitly it's not compatible with Python 3.11:

python = ">=3.7,<3.11"

When I tried to bump that to instead be python = ">=3.7,<3.12" Poetry started complaining because the later version of SciPy that supports Python 3.11 explicitly doesn't support Python 3.7, which required the version range instead to be specified as >=3.8.<3.12.

The next problem, however, was installing frozenlist for Python 3.11. It doesn't have wheels available for Python 3.11 yet and it looks like it needs adjustments to work with the C API changes in 3.11: aio-libs/frozenlist#342.

This issue is mostly to document what the current blocker is for getting this project working with Python 3.11.

Slow response when using S3

My response time increases from 60ms to 1500ms when using RESULT_STORAGE = 'thumbor_aws.result_storage' compared to no result storage. Any idea why or how to debug this?
I am coming from thumbor==6 with python2.7 and wanted to upgrade to version thumbor v7, also switching from tc_aws to thumbor-aws. Everything works fine except for this really high response time.

Installation

FROM python:3.10-slim
...
thumbor==7.0.8
thumbor-aws==0.4.0

thumbor.conf

RESULT_STORAGE = 'thumbor_aws.result_storage'
RESULT_STORAGE_EXPIRATION_SECONDS = 0
RESULT_STORAGE_STORES_UNSAFE = True

### AWS Storage ###
AWS_RESULT_STORAGE_REGION_NAME='eu-central-1'
AWS_RESULT_STORAGE_BUCKET_NAME='thumbor-adsadaaasd'
AWS_RESULT_STORAGE_ROOT_PATH='rs'
AWS_DEFAULT_LOCATION='https://thumbor-adsadaaasd.s3.eu-central-1.amazonaws.com'

I am using /.aws/config to assume aws role for authorization.

Edit: I think it is due to the fact that the boto aws session is not being reused. It looks a new session (via AWS assumeRole) is created for every single request.

Optional ROOT_PATH parameter

First of all, thank you for the great plugin.

Could you please make it possible to store source and resulting files at the root of storage (bucket)?
Without the need to define *_STORAGE_ROOT_PATH

http://minio:9000/img-cache/rs/default/unsafe/300x400/yezhik.jpg
The `/rs` needs to be removed

And I have another question, what /default/ stands for in the path of the resulting file?

AUTO_WEBP failure with Minio

When i enables AUTO_WEBP on Thumbor I get following error:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/thumbor/handlers/__init__.py", line 146, in execute_image_operations
    result = await self.context.modules.result_storage.get()
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/thumbor_aws/result_storage.py", line 172, in get
    exists = await self.object_exists(file_abspath)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/thumbor_aws/s3_client.py", line 167, in object_exists
    await client.head_object(
  File "/usr/local/lib/python3.11/site-packages/aiobotocore/client.py", line 383, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (400) when calling the HeadObject operation: Bad Request

pip freeze results

aiobotocore==2.6.0
aiohttp==3.8.5
aioitertools==0.11.0
aiosignal==1.3.1
async-timeout==4.0.3
attrs==23.1.0
botocore==1.31.17
charset-normalizer==3.2.0
colorama==0.4.6
Deprecated==1.2.14
derpconf==0.8.3
envtpl==0.7.2
frozenlist==1.4.0
idna==3.4
Jinja2==3.1.2
jmespath==1.0.1
JpegIPTC==1.5
libthumbor==2.0.2
MarkupSafe==2.1.3
multidict==6.0.4
piexif==1.1.3
Pillow==9.5.0
pycurl==7.45.2
python-dateutil==2.8.2
pytz==2023.3
six==1.16.0
statsd==3.3.0
thumbor==7.5.2
thumbor-aws==0.6.0
thumbor-plugins-gifv==0.1.2
tornado==6.3.3
urllib3==1.26.16
webcolors==1.11.1
wrapt==1.15.0
yarl==1.9.2

Environment:

Running on docker Python 3.11 image
MinIO VERSION 2023-08-23T10:07:06Z

thumbor.conf

LOADER="thumbor.loaders.http_loader"

RESULT_STORAGE= 'thumbor_aws.result_storage'
AWS_RESULT_STORAGE_S3_ENDPOINT_URL = '{{ AWS_RESULT_STORAGE_S3_ENDPOINT_URL }}'
AWS_RESULT_STORAGE_BUCKET_NAME = '{{ AWS_RESULT_STORAGE_BUCKET_NAME }}'
AWS_RESULT_STORAGE_ROOT_PATH = '{{ AWS_RESULT_STORAGE_ROOT_PATH }}'
AWS_RESULT_STORAGE_S3_ACCESS_KEY_ID = '{{ AWS_RESULT_STORAGE_S3_ACCESS_KEY_ID }}'
AWS_RESULT_STORAGE_S3_SECRET_ACCESS_KEY = '{{ AWS_RESULT_STORAGE_S3_SECRET_ACCESS_KEY }}'
RESULT_STORAGE_STORES_UNSAFE = '{{ RESULT_STORAGE_STORES_UNSAFE | default(False) }}'

STORAGE='thumbor_aws.storage'
AWS_STORAGE_S3_ENDPOINT_URL = '{{ AWS_STORAGE_S3_ENDPOINT_URL }}'
AWS_STORAGE_BUCKET_NAME = '{{ AWS_STORAGE_BUCKET_NAME }}'
AWS_STORAGE_S3_ACCESS_KEY_ID = '{{ AWS_STORAGE_S3_ACCESS_KEY_ID }}'
AWS_STORAGE_S3_SECRET_ACCESS_KEY = '{{ AWS_STORAGE_S3_SECRET_ACCESS_KEY }}'

AUTO_WEBP = True

asyncio:ERROR Unclosed connection

Thank you for thumbor-aws. We were able to successfully serve images from S3 using this library, but discovered a side-effect is that we're seeing a lot of errors like the following:

2023-02-17 03:44:54 asyncio:ERROR Unclosed connection
client_connection: Connection<ConnectionKey(host='s3.amazonaws.com', port=443, is_ssl=True, ssl=None, proxy=None, proxy_auth=None, proxy_headers_hash=None)>
2023-02-17 03:45:12 asyncio:ERROR Unclosed connection
client_connection: Connection<ConnectionKey(host='s3.amazonaws.com', port=443, is_ssl=True, ssl=None, proxy=None, proxy_auth=None, proxy_headers_hash=None)>

This looks like connections to S3 aren't closed nor reused, any idea what would be the best way to debug or resolve?

Difficulties to configure only Loader

I'm trying to configure just the loader but I'm getting some errors.

I did the tests using 2 approaches and both without success.

my configuration:

AWS_LOADER_REGION_NAME = 'us-east-1'
AWS_LOADER_S3_SECRET_ACCESS_KEY = ''
AWS_LOADER_S3_ACCESS_KEY_ID = ''
AWS_LOADER_S3_ENDPOINT_URL = 'https://s3.us-east-1.amazonaws.com'
AWS_LOADER_ROOT_PATH = ''
AWS_LOADER_BUCKET_NAME = 'mybucket'

LOADER = "thumbor_aws.loader"

Error:

ERROR [BaseHandler] get_image failed for url . error: `AWS_STORAGE_REGION_NAME`

LOADER = thumbor.loaders.http_loader

Error:

thumbor:WARNING ERROR retrieving image "IMAGE URL" : HTTP 599: [Errno -2] Name or service not known

I tried to use the same configuration that I have in 6.7 with compatibility mode but without success.

Can you tell me if I'm doing something wrong?

aws loader prepends key path with `/` when root path is an empty string

path to object becomes invalid like /some/path.jpeg, it looks it might only be an issue when root path is empty which it is required to be in my case.

A very quick fix for testing that might not be the real solution below:

diff --git a/thumbor_aws/loader.py b/thumbor_aws/loader.py
index d67b1eb..ef6fa87 100644
--- a/thumbor_aws/loader.py
+++ b/thumbor_aws/loader.py
@@ -103,4 +103,4 @@ async def load(context, path):

 def normalize_url(prefix: str, path: str) -> str:
     """Function to normalize URLs before reaching into S3"""
-    return f"{prefix.rstrip('/')}/{path.lstrip('/')}"
+    return f"{prefix.rstrip('/')}/{path.lstrip('/')}".lstrip("/")

I guess that in some s3 compatible a leading / could possibly be a valid key so maybe a more robust solution is needed.

Remove Double Slashes From URL

There are some cases, when the resulting path contains double slashes.
For example

http://minio:9000//img-cache//rs/default/unsafe/430x100/thumbor.jpg

Could you please make an additional check when building the path ?

Access to Linode S3 compatible object storage

Hi, I tried to use thumbor-aws to access Linode S3 compatible object storage but it won't work. I set everything per documentation - loader, storage, access_keys, bucket, endopint, etc. However, I might have wrongly interpreted it. Do you have any tips I can use to connect thumbor with Linode S3? Or this is not possible at all? Many thanks in advance!

Fallback Http Loader support

As we are trying to migrate to thumbor-aws from tc-aws, one major issue we discovered is the lack of fallback loader support. In essence we need to support loading from http sources the way tc-aws did so with the TC_AWS_ENABLE_HTTP_LOADER config var. At the moment telling Thumbor we are using thumbow-aws as a loader effectively prevents us from optimizing other assets we may load from external sources.

Is there any support for this?
We found this Issue in the main project but it does not seem to have had any traction or followup anywhere thumbor/thumbor#1157

Thanks for any guidance!

aws loader checks for last modified date on files and refuses to load file

I had to comment the below out, not sure if you want a S3 client base class or something. Right now the config options of the clients are referecing some AWS_STORAGE settings and requires them to be set correctly even if only AWS_LOADER is used.

diff --git a/thumbor_aws/s3_client.py b/thumbor_aws/s3_client.py
old mode 100644
new mode 100755
index b923f4c..c9675dc
--- a/thumbor_aws/s3_client.py
+++ b/thumbor_aws/s3_client.py
@@ -161,8 +161,10 @@ class S3Client:
                 return status_code, msg, None

             last_modified = response["LastModified"]
-            if self._is_expired(last_modified, expiration):
-                return 410, b"", last_modified
+
+            # if self._is_expired(last_modified, expiration):
+            #     logger.error("expired")
+            #     return 410, b"", last_modified

             body = await self.get_body(response)

Unable to use with GCS Uniform Bucket Access

I'm trying to set this up with Google Cloud Storage's interoperability S3 endpoint but I can only load an image once. The second time it fails with the error

WARNING Error while trying to get the image from the result storage: An error occurred (InvalidArgument) when calling the GetObjectAcl operation: Invalid argument.

The root cause is s3_client.py:176 client.get_object_acl causing a 400 error per google

Is there a reason to use get_object_acl over head_object which works with GCS?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.