re-use headers_to_container_info on container GET
Currently, a container's info can be cached without cors data intact after a container GET. I made headers_to_container_info a function instead of a method and I crammed all container metadata into container_info. This is so e.g. staticweb can eventually re-use the same container info cache. Fix pep8 in swift/proxy/controllers/container.py Change-Id: I4bbb042dde79afac48395efc38bd80f0ff240e1f
This commit is contained in:
parent
c7948ec5d9
commit
af031138be
@ -94,6 +94,33 @@ def get_container_memcache_key(account, container):
|
|||||||
return 'container/%s/%s' % (account, container)
|
return 'container/%s/%s' % (account, container)
|
||||||
|
|
||||||
|
|
||||||
|
def headers_to_container_info(headers, status_int=HTTP_OK):
|
||||||
|
"""
|
||||||
|
Construct a cacheable dict of container info based on response headers.
|
||||||
|
"""
|
||||||
|
headers = dict(headers)
|
||||||
|
return {
|
||||||
|
'status': status_int,
|
||||||
|
'read_acl': headers.get('x-container-read'),
|
||||||
|
'write_acl': headers.get('x-container-write'),
|
||||||
|
'sync_key': headers.get('x-container-sync-key'),
|
||||||
|
'count': headers.get('x-container-object-count'),
|
||||||
|
'bytes': headers.get('x-container-bytes-used'),
|
||||||
|
'versions': headers.get('x-versions-location'),
|
||||||
|
'cors': {
|
||||||
|
'allow_origin': headers.get(
|
||||||
|
'x-container-meta-access-control-allow-origin'),
|
||||||
|
'allow_headers': headers.get(
|
||||||
|
'x-container-meta-access-control-allow-headers'),
|
||||||
|
'max_age': headers.get(
|
||||||
|
'x-container-meta-access-control-max-age')
|
||||||
|
},
|
||||||
|
'meta': dict((key.lower()[17:], value)
|
||||||
|
for key, value in headers.iteritems()
|
||||||
|
if key.lower().startswith('x-container-meta-'))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Controller(object):
|
class Controller(object):
|
||||||
"""Base WSGI controller class for the proxy"""
|
"""Base WSGI controller class for the proxy"""
|
||||||
server_type = 'Base'
|
server_type = 'Base'
|
||||||
@ -278,25 +305,6 @@ class Controller(object):
|
|||||||
return partition, nodes, container_count
|
return partition, nodes, container_count
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
def headers_to_container_info(self, headers):
|
|
||||||
headers = dict(headers)
|
|
||||||
return {
|
|
||||||
'read_acl': headers.get('x-container-read'),
|
|
||||||
'write_acl': headers.get('x-container-write'),
|
|
||||||
'sync_key': headers.get('x-container-sync-key'),
|
|
||||||
'count': headers.get('x-container-object-count'),
|
|
||||||
'bytes': headers.get('x-container-bytes-used'),
|
|
||||||
'versions': headers.get('x-versions-location'),
|
|
||||||
'cors': {
|
|
||||||
'allow_origin': headers.get(
|
|
||||||
'x-container-meta-access-control-allow-origin'),
|
|
||||||
'allow_headers': headers.get(
|
|
||||||
'x-container-meta-access-control-allow-headers'),
|
|
||||||
'max_age': headers.get(
|
|
||||||
'x-container-meta-access-control-max-age')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def container_info(self, account, container, account_autocreate=False):
|
def container_info(self, account, container, account_autocreate=False):
|
||||||
"""
|
"""
|
||||||
Get container information and thusly verify container existance.
|
Get container information and thusly verify container existance.
|
||||||
@ -344,8 +352,7 @@ class Controller(object):
|
|||||||
body = resp.read()
|
body = resp.read()
|
||||||
if is_success(resp.status):
|
if is_success(resp.status):
|
||||||
container_info.update(
|
container_info.update(
|
||||||
self.headers_to_container_info(resp.getheaders()))
|
headers_to_container_info(resp.getheaders()))
|
||||||
container_info['status'] = HTTP_OK
|
|
||||||
break
|
break
|
||||||
elif resp.status == HTTP_NOT_FOUND:
|
elif resp.status == HTTP_NOT_FOUND:
|
||||||
container_info['status'] = HTTP_NOT_FOUND
|
container_info['status'] = HTTP_NOT_FOUND
|
||||||
|
@ -32,7 +32,7 @@ from swift.common.utils import normalize_timestamp, public
|
|||||||
from swift.common.constraints import check_metadata, MAX_CONTAINER_NAME_LENGTH
|
from swift.common.constraints import check_metadata, MAX_CONTAINER_NAME_LENGTH
|
||||||
from swift.common.http import HTTP_ACCEPTED
|
from swift.common.http import HTTP_ACCEPTED
|
||||||
from swift.proxy.controllers.base import Controller, delay_denial, \
|
from swift.proxy.controllers.base import Controller, delay_denial, \
|
||||||
get_container_memcache_key
|
get_container_memcache_key, headers_to_container_info
|
||||||
from swift.common.swob import HTTPBadRequest, HTTPForbidden, \
|
from swift.common.swob import HTTPBadRequest, HTTPForbidden, \
|
||||||
HTTPNotFound
|
HTTPNotFound
|
||||||
|
|
||||||
@ -68,24 +68,18 @@ class ContainerController(Controller):
|
|||||||
if not self.account_info(self.account_name)[1]:
|
if not self.account_info(self.account_name)[1]:
|
||||||
return HTTPNotFound(request=req)
|
return HTTPNotFound(request=req)
|
||||||
part, nodes = self.app.container_ring.get_nodes(
|
part, nodes = self.app.container_ring.get_nodes(
|
||||||
self.account_name, self.container_name)
|
self.account_name, self.container_name)
|
||||||
shuffle(nodes)
|
shuffle(nodes)
|
||||||
resp = self.GETorHEAD_base(req, _('Container'), part, nodes,
|
resp = self.GETorHEAD_base(
|
||||||
req.path_info, len(nodes))
|
req, _('Container'), part, nodes, req.path_info, len(nodes))
|
||||||
|
|
||||||
if self.app.memcache:
|
if self.app.memcache:
|
||||||
# set the memcache container size for ratelimiting
|
# set the memcache container size for ratelimiting
|
||||||
cache_key = get_container_memcache_key(self.account_name,
|
cache_key = get_container_memcache_key(self.account_name,
|
||||||
self.container_name)
|
self.container_name)
|
||||||
self.app.memcache.set(cache_key,
|
self.app.memcache.set(
|
||||||
{'status': resp.status_int,
|
cache_key,
|
||||||
'read_acl': resp.headers.get('x-container-read'),
|
headers_to_container_info(resp.headers, resp.status_int),
|
||||||
'write_acl': resp.headers.get('x-container-write'),
|
timeout=self.app.recheck_container_existence)
|
||||||
'sync_key': resp.headers.get('x-container-sync-key'),
|
|
||||||
'count': resp.headers.get('x-container-object-count'),
|
|
||||||
'bytes': resp.headers.get('x-container-bytes-used'),
|
|
||||||
'versions': resp.headers.get('x-versions-location')},
|
|
||||||
timeout=self.app.recheck_container_existence)
|
|
||||||
|
|
||||||
if 'swift.authorize' in req.environ:
|
if 'swift.authorize' in req.environ:
|
||||||
req.acl = resp.headers.get('x-container-read')
|
req.acl = resp.headers.get('x-container-read')
|
||||||
@ -151,8 +145,9 @@ class ContainerController(Controller):
|
|||||||
cache_key = get_container_memcache_key(self.account_name,
|
cache_key = get_container_memcache_key(self.account_name,
|
||||||
self.container_name)
|
self.container_name)
|
||||||
self.app.memcache.delete(cache_key)
|
self.app.memcache.delete(cache_key)
|
||||||
resp = self.make_requests(req, self.app.container_ring,
|
resp = self.make_requests(
|
||||||
container_partition, 'PUT', req.path_info, headers)
|
req, self.app.container_ring,
|
||||||
|
container_partition, 'PUT', req.path_info, headers)
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@ -176,9 +171,9 @@ class ContainerController(Controller):
|
|||||||
if self.app.memcache:
|
if self.app.memcache:
|
||||||
self.app.memcache.delete(get_container_memcache_key(
|
self.app.memcache.delete(get_container_memcache_key(
|
||||||
self.account_name, self.container_name))
|
self.account_name, self.container_name))
|
||||||
resp = self.make_requests(req, self.app.container_ring,
|
resp = self.make_requests(
|
||||||
container_partition, 'POST', req.path_info,
|
req, self.app.container_ring, container_partition, 'POST',
|
||||||
[headers] * len(containers))
|
req.path_info, [headers] * len(containers))
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
@public
|
@public
|
||||||
@ -202,8 +197,9 @@ class ContainerController(Controller):
|
|||||||
cache_key = get_container_memcache_key(self.account_name,
|
cache_key = get_container_memcache_key(self.account_name,
|
||||||
self.container_name)
|
self.container_name)
|
||||||
self.app.memcache.delete(cache_key)
|
self.app.memcache.delete(cache_key)
|
||||||
resp = self.make_requests(req, self.app.container_ring,
|
resp = self.make_requests(
|
||||||
container_partition, 'DELETE', req.path_info, headers)
|
req, self.app.container_ring, container_partition, 'DELETE',
|
||||||
|
req.path_info, headers)
|
||||||
# Indicates no server had the container
|
# Indicates no server had the container
|
||||||
if resp.status_int == HTTP_ACCEPTED:
|
if resp.status_int == HTTP_ACCEPTED:
|
||||||
return HTTPNotFound(request=req)
|
return HTTPNotFound(request=req)
|
||||||
|
0
test/unit/proxy/controllers/__init__.py
Normal file
0
test/unit/proxy/controllers/__init__.py
Normal file
50
test/unit/proxy/controllers/test_base.py
Normal file
50
test/unit/proxy/controllers/test_base.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# Copyright (c) 2010-2012 OpenStack, LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from swift.proxy.controllers.base import headers_to_container_info
|
||||||
|
|
||||||
|
|
||||||
|
class TestFuncs(unittest.TestCase):
|
||||||
|
def test_headers_to_container_info_missing(self):
|
||||||
|
resp = headers_to_container_info({}, 404)
|
||||||
|
self.assertEquals(resp['status'], 404)
|
||||||
|
self.assertEquals(resp['read_acl'], None)
|
||||||
|
self.assertEquals(resp['write_acl'], None)
|
||||||
|
|
||||||
|
def test_headers_to_container_info_meta(self):
|
||||||
|
headers = {'X-Container-Meta-Whatevs': 14,
|
||||||
|
'x-container-meta-somethingelse': 0}
|
||||||
|
resp = headers_to_container_info(headers.items(), 200)
|
||||||
|
self.assertEquals(len(resp['meta']), 2)
|
||||||
|
self.assertEquals(resp['meta']['whatevs'], 14)
|
||||||
|
self.assertEquals(resp['meta']['somethingelse'], 0)
|
||||||
|
|
||||||
|
def test_headers_to_container_info_values(self):
|
||||||
|
headers = {
|
||||||
|
'x-container-read': 'readvalue',
|
||||||
|
'x-container-write': 'writevalue',
|
||||||
|
'x-container-sync-key': 'keyvalue',
|
||||||
|
'x-container-meta-access-control-allow-origin': 'here',
|
||||||
|
}
|
||||||
|
resp = headers_to_container_info(headers.items(), 200)
|
||||||
|
self.assertEquals(resp['read_acl'], 'readvalue')
|
||||||
|
self.assertEquals(resp['write_acl'], 'writevalue')
|
||||||
|
self.assertEquals(resp['cors']['allow_origin'], 'here')
|
||||||
|
|
||||||
|
headers['x-unused-header'] = 'blahblahblah'
|
||||||
|
self.assertEquals(
|
||||||
|
resp,
|
||||||
|
headers_to_container_info(headers.items(), 200))
|
Loading…
Reference in New Issue
Block a user