forked from willtrking/thumbor_aws
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* use of botornado for async requests (just like willtrking#14)
* hashed path in S3 prevent to serach by prefix in keys. Without real advantage (not a real FS) * add vows based storage tests * add .gitignore for *.pyc
- Loading branch information
Showing
9 changed files
with
267 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*.pyc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,14 @@ | ||
# coding: utf-8 | ||
# coding: utf-8 | ||
from thumbor.config import Config | ||
Config.define('STORAGE_BUCKET', 'thumbor-images','S3 bucket for Storage', 'S3 Storage') | ||
Config.define('RESULT_STORAGE_BUCKET', 'thumbor-result', 'S3 bucket for result Storage', 'S3 Result Storage') | ||
Config.define('S3_LOADER_BUCKET','thumbor-images','S3 bucket for loader', 'S3 Loader') | ||
Config.define('RESULT_STORAGE_AWS_STORAGE_ROOT_PATH','', 'S3 path prefix', 'S3 Storage') | ||
Config.define('STORAGE_EXPIRATION_SECONDS', 3600, 'S3 expiration', 'S3 Storage') | ||
Config.define('S3_STORAGE_SSE', False, 'S3 encriptipon key', 'S3 Storage') | ||
Config.define('S3_STORAGE_RRS', False, 'S3 redundency', 'S3 Storage') | ||
Config.define('S3_ALLOWED_BUCKETS', False, 'List of allowed bucket to be requeted', 'S3 Loader') | ||
|
||
Config.define('AWS_ACCESS_KEY', None, 'AWS Access key, if None use environment AWS_ACCESS_KEY_ID', 'AWS') | ||
Config.define('AWS_SECRET_KEY', None, 'AWS Secret key, if None use environment AWS_SECRET_ACCESS_KEY', 'AWS') | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#!/usr/bin/python | ||
# -*- coding: utf-8 -*- | ||
|
||
# thumbor imaging service | ||
# https://github.com/globocom/thumbor/wiki | ||
|
||
# Licensed under the MIT license: | ||
# http://www.opensource.org/licenses/mit-license | ||
# Copyright (c) 2011 globo.com [email protected] | ||
|
||
from os.path import join, abspath, dirname | ||
|
||
from thumbor.context import ServerParameters, Context | ||
from thumbor.config import Config | ||
from thumbor.importer import Importer | ||
|
||
IMAGE_URL = 's.glbimg.com/some/image_%s.jpg' | ||
IMAGE_PATH = join(abspath(dirname(__file__)), 'image.jpg') | ||
|
||
with open(IMAGE_PATH, 'r') as img: | ||
IMAGE_BYTES = img.read() | ||
|
||
def get_server(key=None): | ||
server_params = ServerParameters(8888, 'localhost', 'thumbor.conf', None, 'info', None) | ||
server_params.security_key = key | ||
return server_params | ||
|
||
def get_context(server=None, config=None, importer=None): | ||
if not server: | ||
server = get_server() | ||
|
||
if not config: | ||
config = Config() | ||
|
||
if not importer: | ||
importer = Importer(config) | ||
|
||
ctx = Context(server=server, config=config, importer=importer) | ||
return ctx |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
#se!/usr/bin/python | ||
# -*- coding: utf-8 -*- | ||
|
||
# thumbor imaging service | ||
# https://github.com/globocom/thumbor/wiki | ||
|
||
# Licensed under the MIT license: | ||
# http://www.opensource.org/licenses/mit-license | ||
# Copyright (c) 2011 globo.com [email protected] | ||
|
||
|
||
from pyvows import Vows, expect | ||
from hashlib import md5 | ||
|
||
from thumbor.app import ThumborServiceApp | ||
from thumbor.importer import Importer | ||
from thumbor.context import Context, ServerParameters | ||
from thumbor.config import Config | ||
from fixtures.storage_fixture import IMAGE_URL, IMAGE_BYTES, get_server, AWS | ||
import time | ||
|
||
from boto.s3.connection import S3Connection | ||
from boto.s3.key import Key | ||
|
||
from thumbor_aws.storages.s3_storage import Storage | ||
|
||
bucket = 'viadeo-images-test' | ||
|
||
@Vows.batch | ||
class S3StorageVows(Vows.Context): | ||
|
||
def setup(self): | ||
self.bucket = S3Connection().get_bucket(bucket) | ||
#clean bucket | ||
|
||
for x in self.bucket.list(): | ||
x.delete() | ||
|
||
class CanStoreImage(Vows.Context): | ||
def topic(self): | ||
thumborId = IMAGE_URL % '1' | ||
config=Config(STORAGE_BUCKET=bucket) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
store = storage.put(thumborId, IMAGE_BYTES ) | ||
k = Key(self.parent.bucket) | ||
k.key = thumborId | ||
result = k.get_contents_as_string() | ||
return (store , result) | ||
|
||
def should_be_in_catalog(self, topic): | ||
expect(topic[0]).to_equal(IMAGE_URL % '1') | ||
expect(topic[1]).not_to_be_null() | ||
expect(topic[1]).not_to_be_an_error() | ||
expect(topic[1]).to_equal(IMAGE_BYTES) | ||
|
||
class CanGetImage(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
storage.put(IMAGE_URL % '2', IMAGE_BYTES) | ||
return storage.get(IMAGE_URL % '2') | ||
|
||
def should_not_be_null(self, topic): | ||
expect(topic).not_to_be_null() | ||
expect(topic).not_to_be_an_error() | ||
|
||
def should_have_proper_bytes(self, topic): | ||
expect(topic).to_equal(IMAGE_BYTES) | ||
|
||
class CanGetImageExistance(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
storage.put(IMAGE_URL % '3', IMAGE_BYTES) | ||
return storage.exists(IMAGE_URL % '3') | ||
|
||
def should_exists(self, topic): | ||
expect(topic).to_equal(True) | ||
|
||
class CanGetImageInexistance(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
return storage.exists(IMAGE_URL % '9999') | ||
|
||
def should_not_exists(self, topic): | ||
expect(topic).to_equal(False) | ||
|
||
class CanRemoveImage(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
storage.put(IMAGE_URL % '4', IMAGE_BYTES) | ||
created = storage.exists(IMAGE_URL % '4') | ||
time.sleep(1) | ||
storage.remove(IMAGE_URL % '4') | ||
time.sleep(1) | ||
return storage.exists(IMAGE_URL % '4') != created | ||
|
||
def should_be_put_and_removed(self, topic): | ||
expect(topic).to_equal(True) | ||
|
||
class CanRemovethenPutImage(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
storage.put(IMAGE_URL % '5', IMAGE_BYTES) | ||
storage.remove(IMAGE_URL % '5') | ||
time.sleep(1) | ||
created = storage.exists(IMAGE_URL % '5') | ||
time.sleep(1) | ||
storage.put(IMAGE_URL % '5', IMAGE_BYTES) | ||
return storage.exists(IMAGE_URL % '5') != created | ||
|
||
def should_be_put_and_removed(self, topic): | ||
expect(topic).to_equal(True) | ||
|
||
class CanReturnPath(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
return storage.resolve_original_photo_path("toto") | ||
|
||
def should_return_the_same(self, topic): | ||
expect(topic).to_equal("toto") | ||
|
||
class CryptoVows(Vows.Context): | ||
class RaisesIfInvalidConfig(Vows.Context): | ||
@Vows.capture_error | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket, STORES_CRYPTO_KEY_FOR_EACH_IMAGE=True) | ||
storage = Storage(Context(config=config, server=get_server(''))) | ||
storage.put(IMAGE_URL % '9999', IMAGE_BYTES) | ||
storage.put_crypto(IMAGE_URL % '9999') | ||
|
||
def should_be_an_error(self, topic): | ||
expect(topic).to_be_an_error_like(RuntimeError) | ||
expect(topic).to_have_an_error_message_of("STORES_CRYPTO_KEY_FOR_EACH_IMAGE can't be True if no SECURITY_KEY specified") | ||
|
||
class GettingCryptoForANewImageReturnsNone(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket, STORES_CRYPTO_KEY_FOR_EACH_IMAGE=True) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
return storage.get_crypto(IMAGE_URL % '9999') | ||
|
||
def should_be_null(self, topic): | ||
expect(topic).to_be_null() | ||
|
||
class DoesNotStoreIfConfigSaysNotTo(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
storage.put(IMAGE_URL % '9998', IMAGE_BYTES) | ||
storage.put_crypto(IMAGE_URL % '9998') | ||
return storage.get_crypto(IMAGE_URL % '9998') | ||
|
||
def should_be_null(self, topic): | ||
expect(topic).to_be_null() | ||
|
||
class CanStoreCrypto(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket, STORES_CRYPTO_KEY_FOR_EACH_IMAGE=True) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
storage.put(IMAGE_URL % '6', IMAGE_BYTES) | ||
storage.put_crypto(IMAGE_URL % '6') | ||
return storage.get_crypto(IMAGE_URL % '6') | ||
|
||
def should_not_be_null(self, topic): | ||
expect(topic).not_to_be_null() | ||
expect(topic).not_to_be_an_error() | ||
|
||
def should_have_proper_key(self, topic): | ||
expect(topic).to_equal('ACME-SEC') | ||
|
||
class DetectorVows(Vows.Context): | ||
class CanStoreDetectorData(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
storage.put(IMAGE_URL % '7', IMAGE_BYTES) | ||
storage.put_detector_data(IMAGE_URL % '7', 'some-data') | ||
return storage.get_detector_data(IMAGE_URL % '7') | ||
|
||
def should_not_be_null(self, topic): | ||
expect(topic).not_to_be_null() | ||
expect(topic).not_to_be_an_error() | ||
|
||
def should_equal_some_data(self, topic): | ||
expect(topic).to_equal('some-data') | ||
|
||
class ReturnsNoneIfNoDetectorData(Vows.Context): | ||
def topic(self): | ||
config=Config(STORAGE_BUCKET=bucket) | ||
storage = Storage(Context(config=config, server=get_server('ACME-SEC'))) | ||
return storage.get_detector_data(IMAGE_URL % '9999') | ||
|
||
def should_not_be_null(self, topic): | ||
expect(topic).to_be_null() | ||
|