Add s3_store_bucket_url_format config option
Swift's implementation of the S3 interface doesn't support subdomain access to containers. It requires that the bucket be prepended to the request path. The option 's3_store_bucket_url_format' can be set to either 'path' or 'subdomain' (default) to control how boto forms the bucket url. Fixes bug 997658 Change-Id: Ia6e1e7356eef7ac2267f7738e2f4a7c70dc12eeb
This commit is contained in:
parent
3bfd7bd56c
commit
4de8670bcb
@ -261,6 +261,12 @@ s3_store_create_bucket_on_put = False
|
||||
# will be used. If required, an alternative directory can be specified here.
|
||||
#s3_store_object_buffer_dir = /path/to/dir
|
||||
|
||||
# When forming a bucket url, boto will either set the bucket name as the
|
||||
# subdomain or as the first token of the path. Amazon's S3 service will
|
||||
# accept it as the subdomain, but Swift's S3 middleware requires it be
|
||||
# in the path. Set this to 'path' or 'subdomain' - defaults to 'subdomain'.
|
||||
#s3_store_bucket_url_format = subdomain
|
||||
|
||||
# ============ RBD Store Options =============================
|
||||
|
||||
# Ceph configuration file path
|
||||
|
@ -40,6 +40,7 @@ s3_opts = [
|
||||
cfg.StrOpt('s3_store_bucket'),
|
||||
cfg.StrOpt('s3_store_object_buffer_dir'),
|
||||
cfg.BoolOpt('s3_store_create_bucket_on_put', default=False),
|
||||
cfg.StrOpt('s3_store_bucket_url_format', default='subdomain'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -288,7 +289,8 @@ class Store(glance.store.base.Store):
|
||||
|
||||
s3_conn = S3Connection(loc.accesskey, loc.secretkey,
|
||||
host=loc.s3serviceurl,
|
||||
is_secure=(loc.scheme == 's3+https'))
|
||||
is_secure=(loc.scheme == 's3+https'),
|
||||
calling_format=get_calling_format())
|
||||
bucket_obj = get_bucket(s3_conn, loc.bucket)
|
||||
|
||||
key = get_key(bucket_obj, loc.key)
|
||||
@ -336,7 +338,8 @@ class Store(glance.store.base.Store):
|
||||
|
||||
s3_conn = S3Connection(loc.accesskey, loc.secretkey,
|
||||
host=loc.s3serviceurl,
|
||||
is_secure=(loc.scheme == 's3+https'))
|
||||
is_secure=(loc.scheme == 's3+https'),
|
||||
calling_format=get_calling_format())
|
||||
|
||||
create_bucket_if_missing(self.bucket, s3_conn)
|
||||
|
||||
@ -415,7 +418,8 @@ class Store(glance.store.base.Store):
|
||||
from boto.s3.connection import S3Connection
|
||||
s3_conn = S3Connection(loc.accesskey, loc.secretkey,
|
||||
host=loc.s3serviceurl,
|
||||
is_secure=(loc.scheme == 's3+https'))
|
||||
is_secure=(loc.scheme == 's3+https'),
|
||||
calling_format=get_calling_format())
|
||||
bucket_obj = get_bucket(s3_conn, loc.bucket)
|
||||
|
||||
# Close the key when we're through.
|
||||
@ -510,3 +514,13 @@ def get_key(bucket, obj):
|
||||
LOG.error(msg)
|
||||
raise exception.NotFound(msg)
|
||||
return key
|
||||
|
||||
|
||||
def get_calling_format(bucket_format=None):
|
||||
import boto.s3.connection
|
||||
if bucket_format is None:
|
||||
bucket_format = CONF.s3_store_bucket_url_format
|
||||
if bucket_format.lower() == 'path':
|
||||
return boto.s3.connection.OrdinaryCallingFormat()
|
||||
else:
|
||||
return boto.s3.connection.SubdomainCallingFormat()
|
||||
|
@ -210,6 +210,7 @@ class ApiServer(Server):
|
||||
self.s3_store_access_key = ""
|
||||
self.s3_store_secret_key = ""
|
||||
self.s3_store_bucket = ""
|
||||
self.s3_store_bucket_url_format = ""
|
||||
self.swift_store_auth_address = ""
|
||||
self.swift_store_user = ""
|
||||
self.swift_store_key = ""
|
||||
@ -254,6 +255,7 @@ s3_store_host = %(s3_store_host)s
|
||||
s3_store_access_key = %(s3_store_access_key)s
|
||||
s3_store_secret_key = %(s3_store_secret_key)s
|
||||
s3_store_bucket = %(s3_store_bucket)s
|
||||
s3_store_bucket_url_format = %(s3_store_bucket_url_format)s
|
||||
swift_store_auth_address = %(swift_store_auth_address)s
|
||||
swift_store_user = %(swift_store_user)s
|
||||
swift_store_key = %(swift_store_key)s
|
||||
|
@ -27,7 +27,7 @@ import os
|
||||
import random
|
||||
import thread
|
||||
|
||||
from glance.store.s3 import get_s3_location
|
||||
from glance.store.s3 import get_s3_location, get_calling_format
|
||||
|
||||
|
||||
FIVE_KB = 5 * 1024
|
||||
@ -243,7 +243,11 @@ def setup_s3(test):
|
||||
test.disabled = True
|
||||
return
|
||||
|
||||
s3_conn = S3Connection(access_key, secret_key, host=s3_host)
|
||||
calling_format = get_calling_format(test.s3_store_bucket_url_format)
|
||||
s3_conn = S3Connection(access_key, secret_key,
|
||||
host=s3_host,
|
||||
is_secure=False,
|
||||
calling_format=calling_format)
|
||||
|
||||
test.bucket = None
|
||||
try:
|
||||
|
@ -27,6 +27,7 @@ from glance.common import exception
|
||||
from glance.common import utils
|
||||
from glance.openstack.common import cfg
|
||||
from glance.store.location import get_location_from_uri
|
||||
import glance.store.s3
|
||||
from glance.store.s3 import Store, get_s3_location
|
||||
from glance.store import UnsupportedBackend
|
||||
from glance.tests.unit import base
|
||||
@ -187,6 +188,37 @@ class TestStore(base.StoreClearingUnitTest):
|
||||
data += chunk
|
||||
self.assertEqual(expected_data, data)
|
||||
|
||||
def test_get_calling_format_path(self):
|
||||
"""Test a "normal" retrieval of an image in chunks"""
|
||||
self.config(s3_store_bucket_url_format='path')
|
||||
|
||||
def fake_S3Connection_init(*args, **kwargs):
|
||||
expected_cls = boto.s3.connection.OrdinaryCallingFormat
|
||||
self.assertTrue(isinstance(kwargs.get('calling_format'),
|
||||
expected_cls))
|
||||
|
||||
self.stubs.Set(boto.s3.connection.S3Connection, '__init__',
|
||||
fake_S3Connection_init)
|
||||
|
||||
loc = get_location_from_uri(
|
||||
"s3://user:key@auth_address/glance/%s" % FAKE_UUID)
|
||||
(image_s3, image_size) = self.store.get(loc)
|
||||
|
||||
def test_get_calling_format_default(self):
|
||||
"""Test a "normal" retrieval of an image in chunks"""
|
||||
|
||||
def fake_S3Connection_init(*args, **kwargs):
|
||||
expected_cls = boto.s3.connection.SubdomainCallingFormat
|
||||
self.assertTrue(isinstance(kwargs.get('calling_format'),
|
||||
expected_cls))
|
||||
|
||||
self.stubs.Set(boto.s3.connection.S3Connection, '__init__',
|
||||
fake_S3Connection_init)
|
||||
|
||||
loc = get_location_from_uri(
|
||||
"s3://user:key@auth_address/glance/%s" % FAKE_UUID)
|
||||
(image_s3, image_size) = self.store.get(loc)
|
||||
|
||||
def test_get_non_existing(self):
|
||||
"""
|
||||
Test that trying to retrieve a s3 that doesn't exist
|
||||
@ -373,3 +405,17 @@ class TestStore(base.StoreClearingUnitTest):
|
||||
]
|
||||
for (url, expected) in bad_locations:
|
||||
self._do_test_get_s3_location(url, expected)
|
||||
|
||||
def test_calling_format_path(self):
|
||||
self.config(s3_store_bucket_url_format='path')
|
||||
self.assertTrue(isinstance(glance.store.s3.get_calling_format(),
|
||||
boto.s3.connection.OrdinaryCallingFormat))
|
||||
|
||||
def test_calling_format_subdomain(self):
|
||||
self.config(s3_store_bucket_url_format='subdomain')
|
||||
self.assertTrue(isinstance(glance.store.s3.get_calling_format(),
|
||||
boto.s3.connection.SubdomainCallingFormat))
|
||||
|
||||
def test_calling_format_default(self):
|
||||
self.assertTrue(isinstance(glance.store.s3.get_calling_format(),
|
||||
boto.s3.connection.SubdomainCallingFormat))
|
||||
|
Loading…
Reference in New Issue
Block a user