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:
Michael Barton 2012-11-01 16:14:58 -07:00
parent c7948ec5d9
commit af031138be
4 changed files with 95 additions and 42 deletions

View File

@ -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

View File

@ -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)

View File

View 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))