Copy OpenStack Swift (Grizzly) Functional tests
Copy the functional tests to our tree so that we can edit and skip any tests we know we are not going to support for this release Change-Id: I93a76550aaaa58de49ec9a7178a34e081b7b7cf0 Signed-off-by: Luis Pabon <lpabon@redhat.com> Reviewed-on: http://review.gluster.org/5211
This commit is contained in:
parent
b00e479637
commit
41b9106143
0
test/functional/__init__.py
Normal file
0
test/functional/__init__.py
Normal file
736
test/functional/swift_test_client.py
Normal file
736
test/functional/swift_test_client.py
Normal file
@ -0,0 +1,736 @@
|
|||||||
|
# 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 hashlib
|
||||||
|
import httplib
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import socket
|
||||||
|
import StringIO
|
||||||
|
import time
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
import simplejson as json
|
||||||
|
|
||||||
|
from nose import SkipTest
|
||||||
|
from xml.dom import minidom
|
||||||
|
from swiftclient import get_auth
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationFailed(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RequestError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseError(Exception):
|
||||||
|
def __init__(self, response):
|
||||||
|
self.status = response.status
|
||||||
|
self.reason = response.reason
|
||||||
|
Exception.__init__(self)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '%d: %s' % (self.status, self.reason)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%d: %s' % (self.status, self.reason)
|
||||||
|
|
||||||
|
|
||||||
|
def listing_empty(method):
|
||||||
|
for i in xrange(0, 6):
|
||||||
|
if len(method()) == 0:
|
||||||
|
return True
|
||||||
|
|
||||||
|
time.sleep(2 ** i)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def listing_items(method):
|
||||||
|
marker = None
|
||||||
|
once = True
|
||||||
|
items = []
|
||||||
|
|
||||||
|
while once or items:
|
||||||
|
for i in items:
|
||||||
|
yield i
|
||||||
|
|
||||||
|
if once or marker:
|
||||||
|
if marker:
|
||||||
|
items = method(parms={'marker': marker})
|
||||||
|
else:
|
||||||
|
items = method()
|
||||||
|
|
||||||
|
if len(items) == 10000:
|
||||||
|
marker = items[-1]
|
||||||
|
else:
|
||||||
|
marker = None
|
||||||
|
|
||||||
|
once = False
|
||||||
|
else:
|
||||||
|
items = []
|
||||||
|
|
||||||
|
|
||||||
|
class Connection(object):
|
||||||
|
def __init__(self, config):
|
||||||
|
for key in 'auth_host auth_port auth_ssl username password'.split():
|
||||||
|
if key not in config:
|
||||||
|
raise SkipTest
|
||||||
|
|
||||||
|
self.auth_host = config['auth_host']
|
||||||
|
self.auth_port = int(config['auth_port'])
|
||||||
|
self.auth_ssl = config['auth_ssl'] in ('on', 'true', 'yes', '1')
|
||||||
|
self.auth_prefix = config.get('auth_prefix', '/')
|
||||||
|
self.auth_version = str(config.get('auth_version', '1'))
|
||||||
|
|
||||||
|
self.account = config.get('account')
|
||||||
|
self.username = config['username']
|
||||||
|
self.password = config['password']
|
||||||
|
|
||||||
|
self.storage_host = None
|
||||||
|
self.storage_port = None
|
||||||
|
|
||||||
|
self.conn_class = None
|
||||||
|
|
||||||
|
def get_account(self):
|
||||||
|
return Account(self, self.account)
|
||||||
|
|
||||||
|
def authenticate(self, clone_conn=None):
|
||||||
|
if clone_conn:
|
||||||
|
self.conn_class = clone_conn.conn_class
|
||||||
|
self.storage_host = clone_conn.storage_host
|
||||||
|
self.storage_url = clone_conn.storage_url
|
||||||
|
self.storage_port = clone_conn.storage_port
|
||||||
|
self.storage_token = clone_conn.storage_token
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.auth_version == "1":
|
||||||
|
auth_path = '%sv1.0' % (self.auth_prefix)
|
||||||
|
if self.account:
|
||||||
|
auth_user = '%s:%s' % (self.account, self.username)
|
||||||
|
else:
|
||||||
|
auth_user = self.username
|
||||||
|
else:
|
||||||
|
auth_user = self.username
|
||||||
|
auth_path = self.auth_prefix
|
||||||
|
auth_scheme = 'https://' if self.auth_ssl else 'http://'
|
||||||
|
auth_netloc = "%s:%d" % (self.auth_host, self.auth_port)
|
||||||
|
auth_url = auth_scheme + auth_netloc + auth_path
|
||||||
|
|
||||||
|
(storage_url, storage_token) = get_auth(auth_url,
|
||||||
|
auth_user, self.password,
|
||||||
|
snet=False,
|
||||||
|
tenant_name=self.account,
|
||||||
|
auth_version=self.auth_version,
|
||||||
|
os_options={})
|
||||||
|
|
||||||
|
if not (storage_url and storage_token):
|
||||||
|
raise AuthenticationFailed()
|
||||||
|
|
||||||
|
x = storage_url.split('/')
|
||||||
|
|
||||||
|
if x[0] == 'http:':
|
||||||
|
self.conn_class = httplib.HTTPConnection
|
||||||
|
self.storage_port = 80
|
||||||
|
elif x[0] == 'https:':
|
||||||
|
self.conn_class = httplib.HTTPSConnection
|
||||||
|
self.storage_port = 443
|
||||||
|
else:
|
||||||
|
raise ValueError('unexpected protocol %s' % (x[0]))
|
||||||
|
|
||||||
|
self.storage_host = x[2].split(':')[0]
|
||||||
|
if ':' in x[2]:
|
||||||
|
self.storage_port = int(x[2].split(':')[1])
|
||||||
|
self.storage_url = '/%s/%s' % (x[3], x[4])
|
||||||
|
|
||||||
|
self.storage_token = storage_token
|
||||||
|
|
||||||
|
self.http_connect()
|
||||||
|
return self.storage_url, self.storage_token
|
||||||
|
|
||||||
|
def http_connect(self):
|
||||||
|
self.connection = self.conn_class(self.storage_host,
|
||||||
|
port=self.storage_port)
|
||||||
|
#self.connection.set_debuglevel(3)
|
||||||
|
|
||||||
|
def make_path(self, path=[], cfg={}):
|
||||||
|
if cfg.get('version_only_path'):
|
||||||
|
return '/' + self.storage_url.split('/')[1]
|
||||||
|
|
||||||
|
if path:
|
||||||
|
quote = urllib.quote
|
||||||
|
if cfg.get('no_quote') or cfg.get('no_path_quote'):
|
||||||
|
quote = lambda x: x
|
||||||
|
return '%s/%s' % (self.storage_url,
|
||||||
|
'/'.join([quote(i) for i in path]))
|
||||||
|
else:
|
||||||
|
return self.storage_url
|
||||||
|
|
||||||
|
def make_headers(self, hdrs, cfg={}):
|
||||||
|
headers = {}
|
||||||
|
|
||||||
|
if not cfg.get('no_auth_token'):
|
||||||
|
headers['X-Auth-Token'] = self.storage_token
|
||||||
|
|
||||||
|
if isinstance(hdrs, dict):
|
||||||
|
headers.update(hdrs)
|
||||||
|
return headers
|
||||||
|
|
||||||
|
def make_request(self, method, path=[], data='', hdrs={}, parms={},
|
||||||
|
cfg={}):
|
||||||
|
path = self.make_path(path, cfg=cfg)
|
||||||
|
headers = self.make_headers(hdrs, cfg=cfg)
|
||||||
|
if isinstance(parms, dict) and parms:
|
||||||
|
quote = urllib.quote
|
||||||
|
if cfg.get('no_quote') or cfg.get('no_parms_quote'):
|
||||||
|
quote = lambda x: x
|
||||||
|
query_args = ['%s=%s' % (quote(x), quote(str(y)))
|
||||||
|
for (x, y) in parms.items()]
|
||||||
|
path = '%s?%s' % (path, '&'.join(query_args))
|
||||||
|
if not cfg.get('no_content_length'):
|
||||||
|
if cfg.get('set_content_length'):
|
||||||
|
headers['Content-Length'] = cfg.get('set_content_length')
|
||||||
|
else:
|
||||||
|
headers['Content-Length'] = len(data)
|
||||||
|
|
||||||
|
def try_request():
|
||||||
|
self.http_connect()
|
||||||
|
self.connection.request(method, path, data, headers)
|
||||||
|
return self.connection.getresponse()
|
||||||
|
|
||||||
|
self.response = None
|
||||||
|
try_count = 0
|
||||||
|
while try_count < 5:
|
||||||
|
try_count += 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.response = try_request()
|
||||||
|
except httplib.HTTPException:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.response.status == 401:
|
||||||
|
self.authenticate()
|
||||||
|
continue
|
||||||
|
elif self.response.status == 503:
|
||||||
|
if try_count != 5:
|
||||||
|
time.sleep(5)
|
||||||
|
continue
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
if self.response:
|
||||||
|
return self.response.status
|
||||||
|
|
||||||
|
raise RequestError('Unable to complete http request')
|
||||||
|
|
||||||
|
def put_start(self, path, hdrs={}, parms={}, cfg={}, chunked=False):
|
||||||
|
self.http_connect()
|
||||||
|
|
||||||
|
path = self.make_path(path, cfg)
|
||||||
|
headers = self.make_headers(hdrs, cfg=cfg)
|
||||||
|
|
||||||
|
if chunked:
|
||||||
|
headers['Transfer-Encoding'] = 'chunked'
|
||||||
|
headers.pop('Content-Length', None)
|
||||||
|
|
||||||
|
if isinstance(parms, dict) and parms:
|
||||||
|
quote = urllib.quote
|
||||||
|
if cfg.get('no_quote') or cfg.get('no_parms_quote'):
|
||||||
|
quote = lambda x: x
|
||||||
|
query_args = ['%s=%s' % (quote(x), quote(str(y)))
|
||||||
|
for (x, y) in parms.items()]
|
||||||
|
path = '%s?%s' % (path, '&'.join(query_args))
|
||||||
|
|
||||||
|
query_args = ['%s=%s' % (urllib.quote(x),
|
||||||
|
urllib.quote(str(y))) for (x, y) in parms.items()]
|
||||||
|
path = '%s?%s' % (path, '&'.join(query_args))
|
||||||
|
|
||||||
|
self.connection = self.conn_class(self.storage_host,
|
||||||
|
port=self.storage_port)
|
||||||
|
#self.connection.set_debuglevel(3)
|
||||||
|
self.connection.putrequest('PUT', path)
|
||||||
|
for key, value in headers.iteritems():
|
||||||
|
self.connection.putheader(key, value)
|
||||||
|
self.connection.endheaders()
|
||||||
|
|
||||||
|
def put_data(self, data, chunked=False):
|
||||||
|
if chunked:
|
||||||
|
self.connection.send('%x\r\n%s\r\n' % (len(data), data))
|
||||||
|
else:
|
||||||
|
self.connection.send(data)
|
||||||
|
|
||||||
|
def put_end(self, chunked=False):
|
||||||
|
if chunked:
|
||||||
|
self.connection.send('0\r\n\r\n')
|
||||||
|
|
||||||
|
self.response = self.connection.getresponse()
|
||||||
|
self.connection.close()
|
||||||
|
return self.response.status
|
||||||
|
|
||||||
|
|
||||||
|
class Base:
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def header_fields(self, fields):
|
||||||
|
headers = dict(self.conn.response.getheaders())
|
||||||
|
ret = {}
|
||||||
|
for field in fields:
|
||||||
|
if field[1] not in headers:
|
||||||
|
raise ValueError("%s was not found in response header" %
|
||||||
|
(field[1]))
|
||||||
|
|
||||||
|
try:
|
||||||
|
ret[field[0]] = int(headers[field[1]])
|
||||||
|
except ValueError:
|
||||||
|
ret[field[0]] = headers[field[1]]
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class Account(Base):
|
||||||
|
def __init__(self, conn, name):
|
||||||
|
self.conn = conn
|
||||||
|
self.name = str(name)
|
||||||
|
|
||||||
|
def container(self, container_name):
|
||||||
|
return Container(self.conn, self.name, container_name)
|
||||||
|
|
||||||
|
def containers(self, hdrs={}, parms={}, cfg={}):
|
||||||
|
format = parms.get('format', None)
|
||||||
|
if format not in [None, 'json', 'xml']:
|
||||||
|
raise RequestError('Invalid format: %s' % format)
|
||||||
|
if format is None and 'format' in parms:
|
||||||
|
del parms['format']
|
||||||
|
|
||||||
|
status = self.conn.make_request('GET', self.path, hdrs=hdrs,
|
||||||
|
parms=parms, cfg=cfg)
|
||||||
|
if status == 200:
|
||||||
|
if format == 'json':
|
||||||
|
conts = json.loads(self.conn.response.read())
|
||||||
|
for cont in conts:
|
||||||
|
cont['name'] = cont['name'].encode('utf-8')
|
||||||
|
return conts
|
||||||
|
elif format == 'xml':
|
||||||
|
conts = []
|
||||||
|
tree = minidom.parseString(self.conn.response.read())
|
||||||
|
for x in tree.getElementsByTagName('container'):
|
||||||
|
cont = {}
|
||||||
|
for key in ['name', 'count', 'bytes']:
|
||||||
|
cont[key] = x.getElementsByTagName(key)[0].\
|
||||||
|
childNodes[0].nodeValue
|
||||||
|
conts.append(cont)
|
||||||
|
for cont in conts:
|
||||||
|
cont['name'] = cont['name'].encode('utf-8')
|
||||||
|
return conts
|
||||||
|
else:
|
||||||
|
lines = self.conn.response.read().split('\n')
|
||||||
|
if lines and not lines[-1]:
|
||||||
|
lines = lines[:-1]
|
||||||
|
return lines
|
||||||
|
elif status == 204:
|
||||||
|
return []
|
||||||
|
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
def delete_containers(self):
|
||||||
|
for c in listing_items(self.containers):
|
||||||
|
cont = self.container(c)
|
||||||
|
if not cont.delete_recursive():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return listing_empty(self.containers)
|
||||||
|
|
||||||
|
def info(self, hdrs={}, parms={}, cfg={}):
|
||||||
|
if self.conn.make_request('HEAD', self.path, hdrs=hdrs,
|
||||||
|
parms=parms, cfg=cfg) != 204:
|
||||||
|
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
fields = [['object_count', 'x-account-object-count'],
|
||||||
|
['container_count', 'x-account-container-count'],
|
||||||
|
['bytes_used', 'x-account-bytes-used']]
|
||||||
|
|
||||||
|
return self.header_fields(fields)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class Container(Base):
|
||||||
|
def __init__(self, conn, account, name):
|
||||||
|
self.conn = conn
|
||||||
|
self.account = str(account)
|
||||||
|
self.name = str(name)
|
||||||
|
|
||||||
|
def create(self, hdrs={}, parms={}, cfg={}):
|
||||||
|
return self.conn.make_request('PUT', self.path, hdrs=hdrs,
|
||||||
|
parms=parms, cfg=cfg) in (201, 202)
|
||||||
|
|
||||||
|
def delete(self, hdrs={}, parms={}):
|
||||||
|
return self.conn.make_request('DELETE', self.path, hdrs=hdrs,
|
||||||
|
parms=parms) == 204
|
||||||
|
|
||||||
|
def delete_files(self):
|
||||||
|
for f in listing_items(self.files):
|
||||||
|
file = self.file(f)
|
||||||
|
if not file.delete():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return listing_empty(self.files)
|
||||||
|
|
||||||
|
def delete_recursive(self):
|
||||||
|
return self.delete_files() and self.delete()
|
||||||
|
|
||||||
|
def file(self, file_name):
|
||||||
|
return File(self.conn, self.account, self.name, file_name)
|
||||||
|
|
||||||
|
def files(self, hdrs={}, parms={}, cfg={}):
|
||||||
|
format = parms.get('format', None)
|
||||||
|
if format not in [None, 'json', 'xml']:
|
||||||
|
raise RequestError('Invalid format: %s' % format)
|
||||||
|
if format is None and 'format' in parms:
|
||||||
|
del parms['format']
|
||||||
|
|
||||||
|
status = self.conn.make_request('GET', self.path, hdrs=hdrs,
|
||||||
|
parms=parms, cfg=cfg)
|
||||||
|
if status == 200:
|
||||||
|
if format == 'json':
|
||||||
|
files = json.loads(self.conn.response.read())
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
file['name'] = file['name'].encode('utf-8')
|
||||||
|
file['content_type'] = file['content_type'].encode('utf-8')
|
||||||
|
return files
|
||||||
|
elif format == 'xml':
|
||||||
|
files = []
|
||||||
|
tree = minidom.parseString(self.conn.response.read())
|
||||||
|
for x in tree.getElementsByTagName('object'):
|
||||||
|
file = {}
|
||||||
|
for key in ['name', 'hash', 'bytes', 'content_type',
|
||||||
|
'last_modified']:
|
||||||
|
|
||||||
|
file[key] = x.getElementsByTagName(key)[0].\
|
||||||
|
childNodes[0].nodeValue
|
||||||
|
files.append(file)
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
file['name'] = file['name'].encode('utf-8')
|
||||||
|
file['content_type'] = file['content_type'].encode('utf-8')
|
||||||
|
return files
|
||||||
|
else:
|
||||||
|
content = self.conn.response.read()
|
||||||
|
if content:
|
||||||
|
lines = content.split('\n')
|
||||||
|
if lines and not lines[-1]:
|
||||||
|
lines = lines[:-1]
|
||||||
|
return lines
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
elif status == 204:
|
||||||
|
return []
|
||||||
|
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
def info(self, hdrs={}, parms={}, cfg={}):
|
||||||
|
status = self.conn.make_request('HEAD', self.path, hdrs=hdrs,
|
||||||
|
parms=parms, cfg=cfg)
|
||||||
|
|
||||||
|
if self.conn.response.status == 204:
|
||||||
|
fields = [['bytes_used', 'x-container-bytes-used'],
|
||||||
|
['object_count', 'x-container-object-count']]
|
||||||
|
|
||||||
|
return self.header_fields(fields)
|
||||||
|
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self):
|
||||||
|
return [self.name]
|
||||||
|
|
||||||
|
|
||||||
|
class File(Base):
|
||||||
|
def __init__(self, conn, account, container, name):
|
||||||
|
self.conn = conn
|
||||||
|
self.account = str(account)
|
||||||
|
self.container = str(container)
|
||||||
|
self.name = str(name)
|
||||||
|
|
||||||
|
self.chunked_write_in_progress = False
|
||||||
|
self.content_type = None
|
||||||
|
self.size = None
|
||||||
|
self.metadata = {}
|
||||||
|
|
||||||
|
def make_headers(self, cfg={}):
|
||||||
|
headers = {}
|
||||||
|
if not cfg.get('no_content_length'):
|
||||||
|
if cfg.get('set_content_length'):
|
||||||
|
headers['Content-Length'] = cfg.get('set_content_length')
|
||||||
|
elif self.size:
|
||||||
|
headers['Content-Length'] = self.size
|
||||||
|
else:
|
||||||
|
headers['Content-Length'] = 0
|
||||||
|
|
||||||
|
if cfg.get('no_content_type'):
|
||||||
|
pass
|
||||||
|
elif self.content_type:
|
||||||
|
headers['Content-Type'] = self.content_type
|
||||||
|
else:
|
||||||
|
headers['Content-Type'] = 'application/octet-stream'
|
||||||
|
|
||||||
|
for key in self.metadata:
|
||||||
|
headers['X-Object-Meta-' + key] = self.metadata[key]
|
||||||
|
|
||||||
|
return headers
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def compute_md5sum(cls, data):
|
||||||
|
block_size = 4096
|
||||||
|
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = StringIO.StringIO(data)
|
||||||
|
|
||||||
|
checksum = hashlib.md5()
|
||||||
|
buff = data.read(block_size)
|
||||||
|
while buff:
|
||||||
|
checksum.update(buff)
|
||||||
|
buff = data.read(block_size)
|
||||||
|
data.seek(0)
|
||||||
|
return checksum.hexdigest()
|
||||||
|
|
||||||
|
def copy(self, dest_cont, dest_file, hdrs={}, parms={}, cfg={}):
|
||||||
|
if 'destination' in cfg:
|
||||||
|
headers = {'Destination': cfg['destination']}
|
||||||
|
elif cfg.get('no_destination'):
|
||||||
|
headers = {}
|
||||||
|
else:
|
||||||
|
headers = {'Destination': '%s/%s' % (dest_cont, dest_file)}
|
||||||
|
headers.update(hdrs)
|
||||||
|
|
||||||
|
if 'Destination' in headers:
|
||||||
|
headers['Destination'] = urllib.quote(headers['Destination'])
|
||||||
|
|
||||||
|
return self.conn.make_request('COPY', self.path, hdrs=headers,
|
||||||
|
parms=parms) == 201
|
||||||
|
|
||||||
|
def delete(self, hdrs={}, parms={}):
|
||||||
|
if self.conn.make_request('DELETE', self.path, hdrs=hdrs,
|
||||||
|
parms=parms) != 204:
|
||||||
|
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def info(self, hdrs={}, parms={}, cfg={}):
|
||||||
|
if self.conn.make_request('HEAD', self.path, hdrs=hdrs,
|
||||||
|
parms=parms, cfg=cfg) != 200:
|
||||||
|
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
fields = [['content_length', 'content-length'],
|
||||||
|
['content_type', 'content-type'],
|
||||||
|
['last_modified', 'last-modified'],
|
||||||
|
['etag', 'etag']]
|
||||||
|
|
||||||
|
header_fields = self.header_fields(fields)
|
||||||
|
header_fields['etag'] = header_fields['etag'].strip('"')
|
||||||
|
return header_fields
|
||||||
|
|
||||||
|
def initialize(self, hdrs={}, parms={}):
|
||||||
|
if not self.name:
|
||||||
|
return False
|
||||||
|
|
||||||
|
status = self.conn.make_request('HEAD', self.path, hdrs=hdrs,
|
||||||
|
parms=parms)
|
||||||
|
if status == 404:
|
||||||
|
return False
|
||||||
|
elif (status < 200) or (status > 299):
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
for hdr in self.conn.response.getheaders():
|
||||||
|
if hdr[0].lower() == 'content-type':
|
||||||
|
self.content_type = hdr[1]
|
||||||
|
if hdr[0].lower().startswith('x-object-meta-'):
|
||||||
|
self.metadata[hdr[0][14:]] = hdr[1]
|
||||||
|
if hdr[0].lower() == 'etag':
|
||||||
|
self.etag = hdr[1].strip('"')
|
||||||
|
if hdr[0].lower() == 'content-length':
|
||||||
|
self.size = int(hdr[1])
|
||||||
|
if hdr[0].lower() == 'last-modified':
|
||||||
|
self.last_modified = hdr[1]
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def load_from_filename(self, filename, callback=None):
|
||||||
|
fobj = open(filename, 'rb')
|
||||||
|
self.write(fobj, callback=callback)
|
||||||
|
fobj.close()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self):
|
||||||
|
return [self.container, self.name]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def random_data(cls, size=None):
|
||||||
|
if size is None:
|
||||||
|
size = random.randint(1, 32768)
|
||||||
|
fd = open('/dev/urandom', 'r')
|
||||||
|
data = fd.read(size)
|
||||||
|
fd.close()
|
||||||
|
return data
|
||||||
|
|
||||||
|
def read(self, size=-1, offset=0, hdrs=None, buffer=None,
|
||||||
|
callback=None, cfg={}):
|
||||||
|
|
||||||
|
if size > 0:
|
||||||
|
range = 'bytes=%d-%d' % (offset, (offset + size) - 1)
|
||||||
|
if hdrs:
|
||||||
|
hdrs['Range'] = range
|
||||||
|
else:
|
||||||
|
hdrs = {'Range': range}
|
||||||
|
|
||||||
|
status = self.conn.make_request('GET', self.path, hdrs=hdrs,
|
||||||
|
cfg=cfg)
|
||||||
|
|
||||||
|
if(status < 200) or (status > 299):
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
for hdr in self.conn.response.getheaders():
|
||||||
|
if hdr[0].lower() == 'content-type':
|
||||||
|
self.content_type = hdr[1]
|
||||||
|
|
||||||
|
if hasattr(buffer, 'write'):
|
||||||
|
scratch = self.conn.response.read(8192)
|
||||||
|
transferred = 0
|
||||||
|
|
||||||
|
while len(scratch) > 0:
|
||||||
|
buffer.write(scratch)
|
||||||
|
transferred += len(scratch)
|
||||||
|
if callable(callback):
|
||||||
|
callback(transferred, self.size)
|
||||||
|
scratch = self.conn.response.read(8192)
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return self.conn.response.read()
|
||||||
|
|
||||||
|
def read_md5(self):
|
||||||
|
status = self.conn.make_request('GET', self.path)
|
||||||
|
|
||||||
|
if(status < 200) or (status > 299):
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
checksum = hashlib.md5()
|
||||||
|
|
||||||
|
scratch = self.conn.response.read(8192)
|
||||||
|
while len(scratch) > 0:
|
||||||
|
checksum.update(scratch)
|
||||||
|
scratch = self.conn.response.read(8192)
|
||||||
|
|
||||||
|
return checksum.hexdigest()
|
||||||
|
|
||||||
|
def save_to_filename(self, filename, callback=None):
|
||||||
|
try:
|
||||||
|
fobj = open(filename, 'wb')
|
||||||
|
self.read(buffer=fobj, callback=callback)
|
||||||
|
finally:
|
||||||
|
fobj.close()
|
||||||
|
|
||||||
|
def sync_metadata(self, metadata={}, cfg={}):
|
||||||
|
self.metadata.update(metadata)
|
||||||
|
|
||||||
|
if self.metadata:
|
||||||
|
headers = self.make_headers(cfg=cfg)
|
||||||
|
if not cfg.get('no_content_length'):
|
||||||
|
if cfg.get('set_content_length'):
|
||||||
|
headers['Content-Length'] = \
|
||||||
|
cfg.get('set_content_length')
|
||||||
|
else:
|
||||||
|
headers['Content-Length'] = 0
|
||||||
|
|
||||||
|
self.conn.make_request('POST', self.path, hdrs=headers, cfg=cfg)
|
||||||
|
|
||||||
|
if self.conn.response.status not in (201, 202):
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def chunked_write(self, data=None, hdrs={}, parms={}, cfg={}):
|
||||||
|
if data is not None and self.chunked_write_in_progress:
|
||||||
|
self.conn.put_data(data, True)
|
||||||
|
elif data is not None:
|
||||||
|
self.chunked_write_in_progress = True
|
||||||
|
|
||||||
|
headers = self.make_headers(cfg=cfg)
|
||||||
|
headers.update(hdrs)
|
||||||
|
|
||||||
|
self.conn.put_start(self.path, hdrs=headers, parms=parms,
|
||||||
|
cfg=cfg, chunked=True)
|
||||||
|
|
||||||
|
self.conn.put_data(data, True)
|
||||||
|
elif self.chunked_write_in_progress:
|
||||||
|
self.chunked_write_in_progress = False
|
||||||
|
return self.conn.put_end(True) == 201
|
||||||
|
else:
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
def write(self, data='', hdrs={}, parms={}, callback=None, cfg={}):
|
||||||
|
block_size = 2 ** 20
|
||||||
|
|
||||||
|
if isinstance(data, file):
|
||||||
|
try:
|
||||||
|
data.flush()
|
||||||
|
data.seek(0)
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
self.size = int(os.fstat(data.fileno())[6])
|
||||||
|
else:
|
||||||
|
data = StringIO.StringIO(data)
|
||||||
|
self.size = data.len
|
||||||
|
|
||||||
|
headers = self.make_headers(cfg=cfg)
|
||||||
|
headers.update(hdrs)
|
||||||
|
|
||||||
|
self.conn.put_start(self.path, hdrs=headers, parms=parms, cfg=cfg)
|
||||||
|
|
||||||
|
transferred = 0
|
||||||
|
buff = data.read(block_size)
|
||||||
|
try:
|
||||||
|
while len(buff) > 0:
|
||||||
|
self.conn.put_data(buff)
|
||||||
|
buff = data.read(block_size)
|
||||||
|
transferred += len(buff)
|
||||||
|
if callable(callback):
|
||||||
|
callback(transferred, self.size)
|
||||||
|
|
||||||
|
self.conn.put_end()
|
||||||
|
except socket.timeout, err:
|
||||||
|
raise err
|
||||||
|
|
||||||
|
if (self.conn.response.status < 200) or \
|
||||||
|
(self.conn.response.status > 299):
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
|
||||||
|
self.md5 = self.compute_md5sum(data)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def write_random(self, size=None, hdrs={}, parms={}, cfg={}):
|
||||||
|
data = self.random_data(size)
|
||||||
|
if not self.write(data, hdrs=hdrs, parms=parms, cfg=cfg):
|
||||||
|
raise ResponseError(self.conn.response)
|
||||||
|
self.md5 = self.compute_md5sum(StringIO.StringIO(data))
|
||||||
|
return data
|
1617
test/functional/tests.py
Normal file
1617
test/functional/tests.py
Normal file
File diff suppressed because it is too large
Load Diff
0
test/functionalnosetests/__init__.py
Normal file
0
test/functionalnosetests/__init__.py
Normal file
175
test/functionalnosetests/swift_testing.py
Normal file
175
test/functionalnosetests/swift_testing.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
# 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 errno
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
from time import sleep
|
||||||
|
from nose import SkipTest
|
||||||
|
from ConfigParser import MissingSectionHeaderError
|
||||||
|
|
||||||
|
from test import get_config
|
||||||
|
|
||||||
|
from swiftclient import get_auth, http_connection, HTTPException
|
||||||
|
|
||||||
|
conf = get_config('func_test')
|
||||||
|
web_front_end = conf.get('web_front_end', 'integral')
|
||||||
|
normalized_urls = conf.get('normalized_urls', False)
|
||||||
|
|
||||||
|
# If no conf was read, we will fall back to old school env vars
|
||||||
|
swift_test_auth = os.environ.get('SWIFT_TEST_AUTH')
|
||||||
|
swift_test_user = [os.environ.get('SWIFT_TEST_USER'), None, None]
|
||||||
|
swift_test_key = [os.environ.get('SWIFT_TEST_KEY'), None, None]
|
||||||
|
swift_test_tenant = ['', '', '']
|
||||||
|
swift_test_perm = ['', '', '']
|
||||||
|
|
||||||
|
if conf:
|
||||||
|
swift_test_auth_version = str(conf.get('auth_version', '1'))
|
||||||
|
|
||||||
|
swift_test_auth = 'http'
|
||||||
|
if conf.get('auth_ssl', 'no').lower() in ('yes', 'true', 'on', '1'):
|
||||||
|
swift_test_auth = 'https'
|
||||||
|
if 'auth_prefix' not in conf:
|
||||||
|
conf['auth_prefix'] = '/'
|
||||||
|
try:
|
||||||
|
swift_test_auth += \
|
||||||
|
'://%(auth_host)s:%(auth_port)s%(auth_prefix)s' % conf
|
||||||
|
except KeyError:
|
||||||
|
pass # skip
|
||||||
|
|
||||||
|
if swift_test_auth_version == "1":
|
||||||
|
swift_test_auth += 'v1.0'
|
||||||
|
|
||||||
|
if 'account' in conf:
|
||||||
|
swift_test_user[0] = '%(account)s:%(username)s' % conf
|
||||||
|
else:
|
||||||
|
swift_test_user[0] = '%(username)s' % conf
|
||||||
|
swift_test_key[0] = conf['password']
|
||||||
|
try:
|
||||||
|
swift_test_user[1] = '%s%s' % \
|
||||||
|
('%s:' % conf['account2'] if 'account2' in conf else '',
|
||||||
|
conf['username2'])
|
||||||
|
swift_test_key[1] = conf['password2']
|
||||||
|
except KeyError, err:
|
||||||
|
pass # old conf, no second account tests can be run
|
||||||
|
try:
|
||||||
|
swift_test_user[2] = '%s%s' % ('%s:' % conf['account'] if 'account'
|
||||||
|
in conf else '', conf['username3'])
|
||||||
|
swift_test_key[2] = conf['password3']
|
||||||
|
except KeyError, err:
|
||||||
|
pass # old conf, no third account tests can be run
|
||||||
|
|
||||||
|
for _ in range(3):
|
||||||
|
swift_test_perm[_] = swift_test_user[_]
|
||||||
|
|
||||||
|
else:
|
||||||
|
swift_test_user[0] = conf['username']
|
||||||
|
swift_test_tenant[0] = conf['account']
|
||||||
|
swift_test_key[0] = conf['password']
|
||||||
|
swift_test_user[1] = conf['username2']
|
||||||
|
swift_test_tenant[1] = conf['account2']
|
||||||
|
swift_test_key[1] = conf['password2']
|
||||||
|
swift_test_user[2] = conf['username3']
|
||||||
|
swift_test_tenant[2] = conf['account']
|
||||||
|
swift_test_key[2] = conf['password3']
|
||||||
|
|
||||||
|
for _ in range(3):
|
||||||
|
swift_test_perm[_] = swift_test_tenant[_] + ':' + swift_test_user[_]
|
||||||
|
|
||||||
|
skip = not all([swift_test_auth, swift_test_user[0], swift_test_key[0]])
|
||||||
|
if skip:
|
||||||
|
print >>sys.stderr, 'SKIPPING FUNCTIONAL TESTS DUE TO NO CONFIG'
|
||||||
|
|
||||||
|
skip2 = not all([not skip, swift_test_user[1], swift_test_key[1]])
|
||||||
|
if not skip and skip2:
|
||||||
|
print >>sys.stderr, \
|
||||||
|
'SKIPPING SECOND ACCOUNT FUNCTIONAL TESTS DUE TO NO CONFIG FOR THEM'
|
||||||
|
|
||||||
|
skip3 = not all([not skip, swift_test_user[2], swift_test_key[2]])
|
||||||
|
if not skip and skip3:
|
||||||
|
print >>sys.stderr, \
|
||||||
|
'SKIPPING THIRD ACCOUNT FUNCTIONAL TESTS DUE TO NO CONFIG FOR THEM'
|
||||||
|
|
||||||
|
|
||||||
|
class AuthError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InternalServerError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
url = [None, None, None]
|
||||||
|
token = [None, None, None]
|
||||||
|
parsed = [None, None, None]
|
||||||
|
conn = [None, None, None]
|
||||||
|
|
||||||
|
|
||||||
|
def retry(func, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
You can use the kwargs to override the 'retries' (default: 5) and
|
||||||
|
'use_account' (default: 1).
|
||||||
|
"""
|
||||||
|
global url, token, parsed, conn
|
||||||
|
retries = kwargs.get('retries', 5)
|
||||||
|
use_account = 1
|
||||||
|
if 'use_account' in kwargs:
|
||||||
|
use_account = kwargs['use_account']
|
||||||
|
del kwargs['use_account']
|
||||||
|
use_account -= 1
|
||||||
|
attempts = 0
|
||||||
|
backoff = 1
|
||||||
|
while attempts <= retries:
|
||||||
|
attempts += 1
|
||||||
|
try:
|
||||||
|
if not url[use_account] or not token[use_account]:
|
||||||
|
url[use_account], token[use_account] = \
|
||||||
|
get_auth(swift_test_auth, swift_test_user[use_account],
|
||||||
|
swift_test_key[use_account],
|
||||||
|
snet=False,
|
||||||
|
tenant_name=swift_test_tenant[use_account],
|
||||||
|
auth_version=swift_test_auth_version,
|
||||||
|
os_options={})
|
||||||
|
parsed[use_account] = conn[use_account] = None
|
||||||
|
if not parsed[use_account] or not conn[use_account]:
|
||||||
|
parsed[use_account], conn[use_account] = \
|
||||||
|
http_connection(url[use_account])
|
||||||
|
return func(url[use_account], token[use_account],
|
||||||
|
parsed[use_account], conn[use_account], *args, **kwargs)
|
||||||
|
except (socket.error, HTTPException):
|
||||||
|
if attempts > retries:
|
||||||
|
raise
|
||||||
|
parsed[use_account] = conn[use_account] = None
|
||||||
|
except AuthError, err:
|
||||||
|
url[use_account] = token[use_account] = None
|
||||||
|
continue
|
||||||
|
except InternalServerError, err:
|
||||||
|
pass
|
||||||
|
if attempts <= retries:
|
||||||
|
sleep(backoff)
|
||||||
|
backoff *= 2
|
||||||
|
raise Exception('No result after %s retries.' % retries)
|
||||||
|
|
||||||
|
|
||||||
|
def check_response(conn):
|
||||||
|
resp = conn.getresponse()
|
||||||
|
if resp.status == 401:
|
||||||
|
resp.read()
|
||||||
|
raise AuthError()
|
||||||
|
elif resp.status // 100 == 5:
|
||||||
|
resp.read()
|
||||||
|
raise InternalServerError()
|
||||||
|
return resp
|
152
test/functionalnosetests/test_account.py
Executable file
152
test/functionalnosetests/test_account.py
Executable file
@ -0,0 +1,152 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# 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 nose import SkipTest
|
||||||
|
|
||||||
|
from swift.common.constraints import MAX_META_COUNT, MAX_META_NAME_LENGTH, \
|
||||||
|
MAX_META_OVERALL_SIZE, MAX_META_VALUE_LENGTH
|
||||||
|
|
||||||
|
from swift_testing import check_response, retry, skip
|
||||||
|
|
||||||
|
|
||||||
|
class TestAccount(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_metadata(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
def post(url, token, parsed, conn, value):
|
||||||
|
conn.request('POST', parsed.path, '',
|
||||||
|
{'X-Auth-Token': token, 'X-Account-Meta-Test': value})
|
||||||
|
return check_response(conn)
|
||||||
|
def head(url, token, parsed, conn):
|
||||||
|
conn.request('HEAD', parsed.path, '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', parsed.path, '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post, '')
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(head)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-account-meta-test'), None)
|
||||||
|
resp = retry(get)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-account-meta-test'), None)
|
||||||
|
resp = retry(post, 'Value')
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(head)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-account-meta-test'), 'Value')
|
||||||
|
resp = retry(get)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-account-meta-test'), 'Value')
|
||||||
|
|
||||||
|
def test_multi_metadata(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
def post(url, token, parsed, conn, name, value):
|
||||||
|
conn.request('POST', parsed.path, '',
|
||||||
|
{'X-Auth-Token': token, name: value})
|
||||||
|
return check_response(conn)
|
||||||
|
def head(url, token, parsed, conn):
|
||||||
|
conn.request('HEAD', parsed.path, '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post, 'X-Account-Meta-One', '1')
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(head)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-account-meta-one'), '1')
|
||||||
|
resp = retry(post, 'X-Account-Meta-Two', '2')
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(head)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-account-meta-one'), '1')
|
||||||
|
self.assertEquals(resp.getheader('x-account-meta-two'), '2')
|
||||||
|
|
||||||
|
def test_bad_metadata(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
def post(url, token, parsed, conn, extra_headers):
|
||||||
|
headers = {'X-Auth-Token': token}
|
||||||
|
headers.update(extra_headers)
|
||||||
|
conn.request('POST', parsed.path, '', headers)
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post,
|
||||||
|
{'X-Account-Meta-' + ('k' * MAX_META_NAME_LENGTH): 'v'})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(post,
|
||||||
|
{'X-Account-Meta-' + ('k' * (MAX_META_NAME_LENGTH + 1)): 'v'})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
|
||||||
|
resp = retry(post,
|
||||||
|
{'X-Account-Meta-Too-Long': 'k' * MAX_META_VALUE_LENGTH})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(post,
|
||||||
|
{'X-Account-Meta-Too-Long': 'k' * (MAX_META_VALUE_LENGTH + 1)})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
for x in xrange(MAX_META_COUNT):
|
||||||
|
headers['X-Account-Meta-%d' % x] = 'v'
|
||||||
|
resp = retry(post, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
headers = {}
|
||||||
|
for x in xrange(MAX_META_COUNT + 1):
|
||||||
|
headers['X-Account-Meta-%d' % x] = 'v'
|
||||||
|
resp = retry(post, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
header_value = 'k' * MAX_META_VALUE_LENGTH
|
||||||
|
size = 0
|
||||||
|
x = 0
|
||||||
|
while size < MAX_META_OVERALL_SIZE - 4 - MAX_META_VALUE_LENGTH:
|
||||||
|
size += 4 + MAX_META_VALUE_LENGTH
|
||||||
|
headers['X-Account-Meta-%04d' % x] = header_value
|
||||||
|
x += 1
|
||||||
|
if MAX_META_OVERALL_SIZE - size > 1:
|
||||||
|
headers['X-Account-Meta-k'] = \
|
||||||
|
'v' * (MAX_META_OVERALL_SIZE - size - 1)
|
||||||
|
resp = retry(post, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
headers['X-Account-Meta-k'] = \
|
||||||
|
'v' * (MAX_META_OVERALL_SIZE - size)
|
||||||
|
resp = retry(post, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
573
test/functionalnosetests/test_container.py
Executable file
573
test/functionalnosetests/test_container.py
Executable file
@ -0,0 +1,573 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# 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 json
|
||||||
|
import unittest
|
||||||
|
from nose import SkipTest
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from swift.common.constraints import MAX_META_COUNT, MAX_META_NAME_LENGTH, \
|
||||||
|
MAX_META_OVERALL_SIZE, MAX_META_VALUE_LENGTH
|
||||||
|
|
||||||
|
from swift_testing import check_response, retry, skip, skip2, skip3, \
|
||||||
|
swift_test_perm, web_front_end
|
||||||
|
|
||||||
|
|
||||||
|
class TestContainer(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
self.name = uuid4().hex
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', parsed.path + '/' + self.name + '?format=json',
|
||||||
|
'', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
def delete(url, token, parsed, conn, obj):
|
||||||
|
conn.request('DELETE',
|
||||||
|
'/'.join([parsed.path, self.name, obj['name']]), '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
while True:
|
||||||
|
resp = retry(get)
|
||||||
|
body = resp.read()
|
||||||
|
self.assert_(resp.status // 100 == 2, resp.status)
|
||||||
|
objs = json.loads(body)
|
||||||
|
if not objs:
|
||||||
|
break
|
||||||
|
for obj in objs:
|
||||||
|
resp = retry(delete, obj)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
def delete(url, token, parsed, conn):
|
||||||
|
conn.request('DELETE', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(delete)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
def test_multi_metadata(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
def post(url, token, parsed, conn, name, value):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token, name: value})
|
||||||
|
return check_response(conn)
|
||||||
|
def head(url, token, parsed, conn):
|
||||||
|
conn.request('HEAD', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post, 'X-Container-Meta-One', '1')
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(head)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-one'), '1')
|
||||||
|
resp = retry(post, 'X-Container-Meta-Two', '2')
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(head)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-one'), '1')
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-two'), '2')
|
||||||
|
|
||||||
|
def test_PUT_metadata(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
def put(url, token, parsed, conn, name, value):
|
||||||
|
conn.request('PUT', parsed.path + '/' + name, '',
|
||||||
|
{'X-Auth-Token': token, 'X-Container-Meta-Test': value})
|
||||||
|
return check_response(conn)
|
||||||
|
def head(url, token, parsed, conn, name):
|
||||||
|
conn.request('HEAD', parsed.path + '/' + name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
def get(url, token, parsed, conn, name):
|
||||||
|
conn.request('GET', parsed.path + '/' + name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
def delete(url, token, parsed, conn, name):
|
||||||
|
conn.request('DELETE', parsed.path + '/' + name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
name = uuid4().hex
|
||||||
|
resp = retry(put, name, 'Value')
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
resp = retry(head, name)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-test'), 'Value')
|
||||||
|
resp = retry(get, name)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-test'), 'Value')
|
||||||
|
resp = retry(delete, name)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
name = uuid4().hex
|
||||||
|
resp = retry(put, name, '')
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
resp = retry(head, name)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-test'), None)
|
||||||
|
resp = retry(get, name)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-test'), None)
|
||||||
|
resp = retry(delete, name)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
def test_POST_metadata(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
def post(url, token, parsed, conn, value):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token, 'X-Container-Meta-Test': value})
|
||||||
|
return check_response(conn)
|
||||||
|
def head(url, token, parsed, conn):
|
||||||
|
conn.request('HEAD', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(head)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-test'), None)
|
||||||
|
resp = retry(get)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-test'), None)
|
||||||
|
resp = retry(post, 'Value')
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(head)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-test'), 'Value')
|
||||||
|
resp = retry(get)
|
||||||
|
resp.read()
|
||||||
|
self.assert_(resp.status in (200, 204), resp.status)
|
||||||
|
self.assertEquals(resp.getheader('x-container-meta-test'), 'Value')
|
||||||
|
|
||||||
|
def test_PUT_bad_metadata(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
def put(url, token, parsed, conn, name, extra_headers):
|
||||||
|
headers = {'X-Auth-Token': token}
|
||||||
|
headers.update(extra_headers)
|
||||||
|
conn.request('PUT', parsed.path + '/' + name, '', headers)
|
||||||
|
return check_response(conn)
|
||||||
|
def delete(url, token, parsed, conn, name):
|
||||||
|
conn.request('DELETE', parsed.path + '/' + name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
name = uuid4().hex
|
||||||
|
resp = retry(put, name,
|
||||||
|
{'X-Container-Meta-' + ('k' * MAX_META_NAME_LENGTH): 'v'})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
resp = retry(delete, name)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
name = uuid4().hex
|
||||||
|
resp = retry(put, name,
|
||||||
|
{'X-Container-Meta-' + ('k' * (MAX_META_NAME_LENGTH + 1)): 'v'})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
resp = retry(delete, name)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 404)
|
||||||
|
|
||||||
|
name = uuid4().hex
|
||||||
|
resp = retry(put, name,
|
||||||
|
{'X-Container-Meta-Too-Long': 'k' * MAX_META_VALUE_LENGTH})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
resp = retry(delete, name)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
name = uuid4().hex
|
||||||
|
resp = retry(put, name,
|
||||||
|
{'X-Container-Meta-Too-Long': 'k' * (MAX_META_VALUE_LENGTH + 1)})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
resp = retry(delete, name)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 404)
|
||||||
|
|
||||||
|
name = uuid4().hex
|
||||||
|
headers = {}
|
||||||
|
for x in xrange(MAX_META_COUNT):
|
||||||
|
headers['X-Container-Meta-%d' % x] = 'v'
|
||||||
|
resp = retry(put, name, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
resp = retry(delete, name)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
name = uuid4().hex
|
||||||
|
headers = {}
|
||||||
|
for x in xrange(MAX_META_COUNT + 1):
|
||||||
|
headers['X-Container-Meta-%d' % x] = 'v'
|
||||||
|
resp = retry(put, name, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
resp = retry(delete, name)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 404)
|
||||||
|
|
||||||
|
name = uuid4().hex
|
||||||
|
headers = {}
|
||||||
|
header_value = 'k' * MAX_META_VALUE_LENGTH
|
||||||
|
size = 0
|
||||||
|
x = 0
|
||||||
|
while size < MAX_META_OVERALL_SIZE - 4 - MAX_META_VALUE_LENGTH:
|
||||||
|
size += 4 + MAX_META_VALUE_LENGTH
|
||||||
|
headers['X-Container-Meta-%04d' % x] = header_value
|
||||||
|
x += 1
|
||||||
|
if MAX_META_OVERALL_SIZE - size > 1:
|
||||||
|
headers['X-Container-Meta-k'] = \
|
||||||
|
'v' * (MAX_META_OVERALL_SIZE - size - 1)
|
||||||
|
resp = retry(put, name, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
resp = retry(delete, name)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
name = uuid4().hex
|
||||||
|
headers['X-Container-Meta-k'] = \
|
||||||
|
'v' * (MAX_META_OVERALL_SIZE - size)
|
||||||
|
resp = retry(put, name, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
resp = retry(delete, name)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 404)
|
||||||
|
|
||||||
|
def test_POST_bad_metadata(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
def post(url, token, parsed, conn, extra_headers):
|
||||||
|
headers = {'X-Auth-Token': token}
|
||||||
|
headers.update(extra_headers)
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '', headers)
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post,
|
||||||
|
{'X-Container-Meta-' + ('k' * MAX_META_NAME_LENGTH): 'v'})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(post,
|
||||||
|
{'X-Container-Meta-' + ('k' * (MAX_META_NAME_LENGTH + 1)): 'v'})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
|
||||||
|
resp = retry(post,
|
||||||
|
{'X-Container-Meta-Too-Long': 'k' * MAX_META_VALUE_LENGTH})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(post,
|
||||||
|
{'X-Container-Meta-Too-Long': 'k' * (MAX_META_VALUE_LENGTH + 1)})
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
for x in xrange(MAX_META_COUNT):
|
||||||
|
headers['X-Container-Meta-%d' % x] = 'v'
|
||||||
|
resp = retry(post, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
headers = {}
|
||||||
|
for x in xrange(MAX_META_COUNT + 1):
|
||||||
|
headers['X-Container-Meta-%d' % x] = 'v'
|
||||||
|
resp = retry(post, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
header_value = 'k' * MAX_META_VALUE_LENGTH
|
||||||
|
size = 0
|
||||||
|
x = 0
|
||||||
|
while size < MAX_META_OVERALL_SIZE - 4 - MAX_META_VALUE_LENGTH:
|
||||||
|
size += 4 + MAX_META_VALUE_LENGTH
|
||||||
|
headers['X-Container-Meta-%04d' % x] = header_value
|
||||||
|
x += 1
|
||||||
|
if MAX_META_OVERALL_SIZE - size > 1:
|
||||||
|
headers['X-Container-Meta-k'] = \
|
||||||
|
'v' * (MAX_META_OVERALL_SIZE - size - 1)
|
||||||
|
resp = retry(post, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
headers['X-Container-Meta-k'] = \
|
||||||
|
'v' * (MAX_META_OVERALL_SIZE - size)
|
||||||
|
resp = retry(post, headers)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
|
||||||
|
def test_public_container(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', parsed.path + '/' + self.name)
|
||||||
|
return check_response(conn)
|
||||||
|
try:
|
||||||
|
resp = retry(get)
|
||||||
|
raise Exception('Should not have been able to GET')
|
||||||
|
except Exception, err:
|
||||||
|
self.assert_(str(err).startswith('No result after '), err)
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token,
|
||||||
|
'X-Container-Read': '.r:*,.rlistings'})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(get)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token, 'X-Container-Read': ''})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
try:
|
||||||
|
resp = retry(get)
|
||||||
|
raise Exception('Should not have been able to GET')
|
||||||
|
except Exception, err:
|
||||||
|
self.assert_(str(err).startswith('No result after '), err)
|
||||||
|
|
||||||
|
def test_cross_account_container(self):
|
||||||
|
if skip or skip2:
|
||||||
|
raise SkipTest
|
||||||
|
# Obtain the first account's string
|
||||||
|
first_account = ['unknown']
|
||||||
|
def get1(url, token, parsed, conn):
|
||||||
|
first_account[0] = parsed.path
|
||||||
|
conn.request('HEAD', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get1)
|
||||||
|
resp.read()
|
||||||
|
# Ensure we can't access the container with the second account
|
||||||
|
def get2(url, token, parsed, conn):
|
||||||
|
conn.request('GET', first_account[0] + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get2, use_account=2)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
# Make the container accessible by the second account
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token, 'X-Container-Read': swift_test_perm[1],
|
||||||
|
'X-Container-Write': swift_test_perm[1]})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# Ensure we can now use the container with the second account
|
||||||
|
resp = retry(get2, use_account=2)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# Make the container private again
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token, 'X-Container-Read': '',
|
||||||
|
'X-Container-Write': ''})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# Ensure we can't access the container with the second account again
|
||||||
|
resp = retry(get2, use_account=2)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
|
||||||
|
def test_cross_account_public_container(self):
|
||||||
|
if skip or skip2:
|
||||||
|
raise SkipTest
|
||||||
|
# Obtain the first account's string
|
||||||
|
first_account = ['unknown']
|
||||||
|
def get1(url, token, parsed, conn):
|
||||||
|
first_account[0] = parsed.path
|
||||||
|
conn.request('HEAD', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get1)
|
||||||
|
resp.read()
|
||||||
|
# Ensure we can't access the container with the second account
|
||||||
|
def get2(url, token, parsed, conn):
|
||||||
|
conn.request('GET', first_account[0] + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get2, use_account=2)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
# Make the container completely public
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token,
|
||||||
|
'X-Container-Read': '.r:*,.rlistings'})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# Ensure we can now read the container with the second account
|
||||||
|
resp = retry(get2, use_account=2)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# But we shouldn't be able to write with the second account
|
||||||
|
def put2(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', first_account[0] + '/' + self.name + '/object',
|
||||||
|
'test object', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put2, use_account=2)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
# Now make the container also writeable by the second account
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token,
|
||||||
|
'X-Container-Write': swift_test_perm[1]})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# Ensure we can still read the container with the second account
|
||||||
|
resp = retry(get2, use_account=2)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# And that we can now write with the second account
|
||||||
|
resp = retry(put2, use_account=2)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
def test_nonadmin_user(self):
|
||||||
|
if skip or skip3:
|
||||||
|
raise SkipTest
|
||||||
|
# Obtain the first account's string
|
||||||
|
first_account = ['unknown']
|
||||||
|
def get1(url, token, parsed, conn):
|
||||||
|
first_account[0] = parsed.path
|
||||||
|
conn.request('HEAD', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get1)
|
||||||
|
resp.read()
|
||||||
|
# Ensure we can't access the container with the third account
|
||||||
|
def get3(url, token, parsed, conn):
|
||||||
|
conn.request('GET', first_account[0] + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get3, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
# Make the container accessible by the third account
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token, 'X-Container-Read': swift_test_perm[2]})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# Ensure we can now read the container with the third account
|
||||||
|
resp = retry(get3, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# But we shouldn't be able to write with the third account
|
||||||
|
def put3(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', first_account[0] + '/' + self.name + '/object',
|
||||||
|
'test object', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put3, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
# Now make the container also writeable by the third account
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.name, '',
|
||||||
|
{'X-Auth-Token': token,
|
||||||
|
'X-Container-Write': swift_test_perm[2]})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# Ensure we can still read the container with the third account
|
||||||
|
resp = retry(get3, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# And that we can now write with the third account
|
||||||
|
resp = retry(put3, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
def test_long_name_content_type(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
container_name = 'X' * 2048
|
||||||
|
conn.request('PUT', '%s/%s' % (parsed.path,
|
||||||
|
container_name), 'there', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 400)
|
||||||
|
self.assertEquals(resp.getheader('Content-Type'),
|
||||||
|
'text/html; charset=UTF-8')
|
||||||
|
|
||||||
|
def test_null_name(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/abc%%00def' % parsed.path, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
if (web_front_end == 'apache2'):
|
||||||
|
self.assertEquals(resp.status, 404)
|
||||||
|
else:
|
||||||
|
self.assertEquals(resp.read(), 'Invalid UTF8 or contains NULL')
|
||||||
|
self.assertEquals(resp.status, 412)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
600
test/functionalnosetests/test_object.py
Executable file
600
test/functionalnosetests/test_object.py
Executable file
@ -0,0 +1,600 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# 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 nose import SkipTest
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from swift.common.constraints import MAX_META_COUNT, MAX_META_NAME_LENGTH, \
|
||||||
|
MAX_META_OVERALL_SIZE, MAX_META_VALUE_LENGTH
|
||||||
|
|
||||||
|
from swift_testing import check_response, retry, skip, skip3, \
|
||||||
|
swift_test_perm, web_front_end
|
||||||
|
from test import get_config
|
||||||
|
|
||||||
|
|
||||||
|
class TestObject(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
self.container = uuid4().hex
|
||||||
|
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', parsed.path + '/' + self.container, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
self.obj = uuid4().hex
|
||||||
|
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/%s/%s' % (parsed.path, self.container,
|
||||||
|
self.obj), 'test', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
|
||||||
|
def delete(url, token, parsed, conn, obj):
|
||||||
|
conn.request('DELETE',
|
||||||
|
'%s/%s/%s' % (parsed.path, self.container, obj),
|
||||||
|
'', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
|
||||||
|
# get list of objects in container
|
||||||
|
def list(url, token, parsed, conn):
|
||||||
|
conn.request('GET',
|
||||||
|
'%s/%s' % (parsed.path, self.container),
|
||||||
|
'', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(list)
|
||||||
|
object_listing = resp.read()
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
|
||||||
|
# iterate over object listing and delete all objects
|
||||||
|
for obj in object_listing.splitlines():
|
||||||
|
resp = retry(delete, obj)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
# delete the container
|
||||||
|
def delete(url, token, parsed, conn):
|
||||||
|
conn.request('DELETE', parsed.path + '/' + self.container, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(delete)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
def test_copy_object(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
|
||||||
|
source = '%s/%s' % (self.container, self.obj)
|
||||||
|
dest = '%s/%s' % (self.container, 'test_copy')
|
||||||
|
|
||||||
|
# get contents of source
|
||||||
|
def get_source(url, token, parsed, conn):
|
||||||
|
conn.request('GET',
|
||||||
|
'%s/%s' % (parsed.path, source),
|
||||||
|
'', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get_source)
|
||||||
|
source_contents = resp.read()
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
self.assertEquals(source_contents, 'test')
|
||||||
|
|
||||||
|
# copy source to dest with X-Copy-From
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/%s' % (parsed.path, dest), '',
|
||||||
|
{'X-Auth-Token': token,
|
||||||
|
'Content-Length': '0',
|
||||||
|
'X-Copy-From': source})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
contents = resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# contents of dest should be the same as source
|
||||||
|
def get_dest(url, token, parsed, conn):
|
||||||
|
conn.request('GET',
|
||||||
|
'%s/%s' % (parsed.path, dest),
|
||||||
|
'', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get_dest)
|
||||||
|
dest_contents = resp.read()
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
self.assertEquals(dest_contents, source_contents)
|
||||||
|
|
||||||
|
# delete the copy
|
||||||
|
def delete(url, token, parsed, conn):
|
||||||
|
conn.request('DELETE', '%s/%s' % (parsed.path, dest), '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(delete)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
# verify dest does not exist
|
||||||
|
resp = retry(get_dest)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 404)
|
||||||
|
|
||||||
|
# copy source to dest with COPY
|
||||||
|
def copy(url, token, parsed, conn):
|
||||||
|
conn.request('COPY', '%s/%s' % (parsed.path, source), '',
|
||||||
|
{'X-Auth-Token': token,
|
||||||
|
'Destination': dest})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(copy)
|
||||||
|
contents = resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# contents of dest should be the same as source
|
||||||
|
resp = retry(get_dest)
|
||||||
|
dest_contents = resp.read()
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
self.assertEquals(dest_contents, source_contents)
|
||||||
|
|
||||||
|
# delete the copy
|
||||||
|
resp = retry(delete)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
def test_public_object(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET',
|
||||||
|
'%s/%s/%s' % (parsed.path, self.container, self.obj))
|
||||||
|
return check_response(conn)
|
||||||
|
try:
|
||||||
|
resp = retry(get)
|
||||||
|
raise Exception('Should not have been able to GET')
|
||||||
|
except Exception, err:
|
||||||
|
self.assert_(str(err).startswith('No result after '))
|
||||||
|
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.container, '',
|
||||||
|
{'X-Auth-Token': token,
|
||||||
|
'X-Container-Read': '.r:*'})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
resp = retry(get)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', parsed.path + '/' + self.container, '',
|
||||||
|
{'X-Auth-Token': token, 'X-Container-Read': ''})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
try:
|
||||||
|
resp = retry(get)
|
||||||
|
raise Exception('Should not have been able to GET')
|
||||||
|
except Exception, err:
|
||||||
|
self.assert_(str(err).startswith('No result after '))
|
||||||
|
|
||||||
|
def test_private_object(self):
|
||||||
|
if skip or skip3:
|
||||||
|
raise SkipTest
|
||||||
|
|
||||||
|
# Ensure we can't access the object with the third account
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/%s' % (parsed.path, self.container,
|
||||||
|
self.obj), '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
|
||||||
|
# create a shared container writable by account3
|
||||||
|
shared_container = uuid4().hex
|
||||||
|
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/%s' % (parsed.path,
|
||||||
|
shared_container), '',
|
||||||
|
{'X-Auth-Token': token,
|
||||||
|
'X-Container-Read': swift_test_perm[2],
|
||||||
|
'X-Container-Write': swift_test_perm[2]})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# verify third account can not copy from private container
|
||||||
|
def copy(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/%s/%s' % (parsed.path,
|
||||||
|
shared_container,
|
||||||
|
'private_object'),
|
||||||
|
'', {'X-Auth-Token': token,
|
||||||
|
'Content-Length': '0',
|
||||||
|
'X-Copy-From': '%s/%s' % (self.container,
|
||||||
|
self.obj)})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(copy, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
|
||||||
|
# verify third account can write "obj1" to shared container
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/%s/%s' % (parsed.path, shared_container,
|
||||||
|
'obj1'), 'test', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# verify third account can copy "obj1" to shared container
|
||||||
|
def copy2(url, token, parsed, conn):
|
||||||
|
conn.request('COPY', '%s/%s/%s' % (parsed.path,
|
||||||
|
shared_container,
|
||||||
|
'obj1'),
|
||||||
|
'', {'X-Auth-Token': token,
|
||||||
|
'Destination': '%s/%s' % (shared_container,
|
||||||
|
'obj1')})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(copy2, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# verify third account STILL can not copy from private container
|
||||||
|
def copy3(url, token, parsed, conn):
|
||||||
|
conn.request('COPY', '%s/%s/%s' % (parsed.path,
|
||||||
|
self.container,
|
||||||
|
self.obj),
|
||||||
|
'', {'X-Auth-Token': token,
|
||||||
|
'Destination': '%s/%s' % (shared_container,
|
||||||
|
'private_object')})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(copy3, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
|
||||||
|
# clean up "obj1"
|
||||||
|
def delete(url, token, parsed, conn):
|
||||||
|
conn.request('DELETE', '%s/%s/%s' % (parsed.path, shared_container,
|
||||||
|
'obj1'), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(delete)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
# clean up shared_container
|
||||||
|
def delete(url, token, parsed, conn):
|
||||||
|
conn.request('DELETE',
|
||||||
|
parsed.path + '/' + shared_container, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(delete)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
def test_manifest(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
# Data for the object segments
|
||||||
|
segments1 = ['one', 'two', 'three', 'four', 'five']
|
||||||
|
segments2 = ['six', 'seven', 'eight']
|
||||||
|
segments3 = ['nine', 'ten', 'eleven']
|
||||||
|
|
||||||
|
# Upload the first set of segments
|
||||||
|
def put(url, token, parsed, conn, objnum):
|
||||||
|
conn.request('PUT', '%s/%s/segments1/%s' % (parsed.path,
|
||||||
|
self.container, str(objnum)), segments1[objnum],
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
for objnum in xrange(len(segments1)):
|
||||||
|
resp = retry(put, objnum)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# Upload the manifest
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token,
|
||||||
|
'X-Object-Manifest': '%s/segments1/' % self.container,
|
||||||
|
'Content-Type': 'text/jibberish', 'Content-Length': '0'})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# Get the manifest (should get all the segments as the body)
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get)
|
||||||
|
self.assertEquals(resp.read(), ''.join(segments1))
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
self.assertEquals(resp.getheader('content-type'), 'text/jibberish')
|
||||||
|
|
||||||
|
# Get with a range at the start of the second segment
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token, 'Range':
|
||||||
|
'bytes=3-'})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get)
|
||||||
|
self.assertEquals(resp.read(), ''.join(segments1[1:]))
|
||||||
|
self.assertEquals(resp.status, 206)
|
||||||
|
|
||||||
|
# Get with a range in the middle of the second segment
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token, 'Range':
|
||||||
|
'bytes=5-'})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get)
|
||||||
|
self.assertEquals(resp.read(), ''.join(segments1)[5:])
|
||||||
|
self.assertEquals(resp.status, 206)
|
||||||
|
|
||||||
|
# Get with a full start and stop range
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token, 'Range':
|
||||||
|
'bytes=5-10'})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get)
|
||||||
|
self.assertEquals(resp.read(), ''.join(segments1)[5:11])
|
||||||
|
self.assertEquals(resp.status, 206)
|
||||||
|
|
||||||
|
# Upload the second set of segments
|
||||||
|
def put(url, token, parsed, conn, objnum):
|
||||||
|
conn.request('PUT', '%s/%s/segments2/%s' % (parsed.path,
|
||||||
|
self.container, str(objnum)), segments2[objnum],
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
for objnum in xrange(len(segments2)):
|
||||||
|
resp = retry(put, objnum)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# Get the manifest (should still be the first segments of course)
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get)
|
||||||
|
self.assertEquals(resp.read(), ''.join(segments1))
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
|
||||||
|
# Update the manifest
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token,
|
||||||
|
'X-Object-Manifest': '%s/segments2/' % self.container,
|
||||||
|
'Content-Length': '0'})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# Get the manifest (should be the second set of segments now)
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get)
|
||||||
|
self.assertEquals(resp.read(), ''.join(segments2))
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
|
||||||
|
if not skip3:
|
||||||
|
|
||||||
|
# Ensure we can't access the manifest with the third account
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
|
||||||
|
# Grant access to the third account
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', '%s/%s' % (parsed.path, self.container),
|
||||||
|
'', {'X-Auth-Token': token,
|
||||||
|
'X-Container-Read': swift_test_perm[2]})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
# The third account should be able to get the manifest now
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get, use_account=3)
|
||||||
|
self.assertEquals(resp.read(), ''.join(segments2))
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
|
||||||
|
# Create another container for the third set of segments
|
||||||
|
acontainer = uuid4().hex
|
||||||
|
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', parsed.path + '/' + acontainer, '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# Upload the third set of segments in the other container
|
||||||
|
def put(url, token, parsed, conn, objnum):
|
||||||
|
conn.request('PUT', '%s/%s/segments3/%s' % (parsed.path,
|
||||||
|
acontainer, str(objnum)), segments3[objnum],
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
for objnum in xrange(len(segments3)):
|
||||||
|
resp = retry(put, objnum)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# Update the manifest
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token,
|
||||||
|
'X-Object-Manifest': '%s/segments3/' % acontainer,
|
||||||
|
'Content-Length': '0'})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
# Get the manifest to ensure it's the third set of segments
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get)
|
||||||
|
self.assertEquals(resp.read(), ''.join(segments3))
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
|
||||||
|
if not skip3:
|
||||||
|
|
||||||
|
# Ensure we can't access the manifest with the third account
|
||||||
|
# (because the segments are in a protected container even if the
|
||||||
|
# manifest itself is not).
|
||||||
|
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get, use_account=3)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 403)
|
||||||
|
|
||||||
|
# Grant access to the third account
|
||||||
|
def post(url, token, parsed, conn):
|
||||||
|
conn.request('POST', '%s/%s' % (parsed.path, acontainer),
|
||||||
|
'', {'X-Auth-Token': token,
|
||||||
|
'X-Container-Read': swift_test_perm[2]})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(post)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
# The third account should be able to get the manifest now
|
||||||
|
def get(url, token, parsed, conn):
|
||||||
|
conn.request('GET', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(get, use_account=3)
|
||||||
|
self.assertEquals(resp.read(), ''.join(segments3))
|
||||||
|
self.assertEquals(resp.status, 200)
|
||||||
|
|
||||||
|
# Delete the manifest
|
||||||
|
def delete(url, token, parsed, conn, objnum):
|
||||||
|
conn.request('DELETE', '%s/%s/manifest' % (parsed.path,
|
||||||
|
self.container), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(delete, objnum)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
# Delete the third set of segments
|
||||||
|
def delete(url, token, parsed, conn, objnum):
|
||||||
|
conn.request('DELETE', '%s/%s/segments3/%s' % (parsed.path,
|
||||||
|
acontainer, str(objnum)), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
for objnum in xrange(len(segments3)):
|
||||||
|
resp = retry(delete, objnum)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
# Delete the second set of segments
|
||||||
|
def delete(url, token, parsed, conn, objnum):
|
||||||
|
conn.request('DELETE', '%s/%s/segments2/%s' % (parsed.path,
|
||||||
|
self.container, str(objnum)), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
for objnum in xrange(len(segments2)):
|
||||||
|
resp = retry(delete, objnum)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
# Delete the first set of segments
|
||||||
|
def delete(url, token, parsed, conn, objnum):
|
||||||
|
conn.request('DELETE', '%s/%s/segments1/%s' % (parsed.path,
|
||||||
|
self.container, str(objnum)), '', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
for objnum in xrange(len(segments1)):
|
||||||
|
resp = retry(delete, objnum)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
# Delete the extra container
|
||||||
|
def delete(url, token, parsed, conn):
|
||||||
|
conn.request('DELETE', '%s/%s' % (parsed.path, acontainer), '',
|
||||||
|
{'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(delete)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
|
||||||
|
def test_delete_content_type(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/%s/hi' % (parsed.path,
|
||||||
|
self.container), 'there', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 201)
|
||||||
|
|
||||||
|
def delete(url, token, parsed, conn):
|
||||||
|
conn.request('DELETE', '%s/%s/hi' % (parsed.path, self.container),
|
||||||
|
'', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(delete)
|
||||||
|
resp.read()
|
||||||
|
self.assertEquals(resp.status, 204)
|
||||||
|
self.assertEquals(resp.getheader('Content-Type'),
|
||||||
|
'text/html; charset=UTF-8')
|
||||||
|
|
||||||
|
def test_null_name(self):
|
||||||
|
if skip:
|
||||||
|
raise SkipTest
|
||||||
|
|
||||||
|
def put(url, token, parsed, conn):
|
||||||
|
conn.request('PUT', '%s/%s/abc%%00def' % (parsed.path,
|
||||||
|
self.container), 'test', {'X-Auth-Token': token})
|
||||||
|
return check_response(conn)
|
||||||
|
resp = retry(put)
|
||||||
|
if (web_front_end == 'apache2'):
|
||||||
|
self.assertEquals(resp.status, 404)
|
||||||
|
else:
|
||||||
|
self.assertEquals(resp.read(), 'Invalid UTF8 or contains NULL')
|
||||||
|
self.assertEquals(resp.status, 412)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue
Block a user