b46b735a3e
Related-Change: I4ecfae2bca6ffa08ad15e584579ebce707f4628d Related-Change: I1e244c231753b8f4b6f1cf95cb0ae4c3c959ae0f Change-Id: Ia386736b9b283858931794690538871b6e1ad9c8
409 lines
16 KiB
Python
409 lines
16 KiB
Python
#!/usr/bin/python -u
|
|
# Copyright (c) 2010-2016 OpenStack Foundation
|
|
#
|
|
# 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.
|
|
|
|
from six.moves import urllib
|
|
|
|
from swift.common.swob import str_to_wsgi
|
|
import test.functional as tf
|
|
from test.functional.tests import Utils, Base, Base2, BaseEnv
|
|
from test.functional.swift_test_client import Connection, ResponseError
|
|
|
|
|
|
def setUpModule():
|
|
tf.setup_package()
|
|
|
|
|
|
def tearDownModule():
|
|
tf.teardown_package()
|
|
|
|
|
|
class TestDloEnv(BaseEnv):
|
|
@classmethod
|
|
def setUp(cls):
|
|
super(TestDloEnv, cls).setUp()
|
|
|
|
cls.container = cls.account.container(Utils.create_name())
|
|
cls.container2 = cls.account.container(Utils.create_name())
|
|
|
|
for cont in (cls.container, cls.container2):
|
|
if not cont.create():
|
|
raise ResponseError(cls.conn.response)
|
|
|
|
prefix = Utils.create_name(10)
|
|
cls.segment_prefix = prefix
|
|
|
|
for letter in ('a', 'b', 'c', 'd', 'e'):
|
|
file_item = cls.container.file("%s/seg_lower%s" % (prefix, letter))
|
|
file_item.write(letter.encode('ascii') * 10)
|
|
|
|
file_item = cls.container.file(
|
|
"%s/seg_upper_%%ff%s" % (prefix, letter))
|
|
file_item.write(letter.upper().encode('ascii') * 10)
|
|
|
|
for letter in ('f', 'g', 'h', 'i', 'j'):
|
|
file_item = cls.container2.file("%s/seg_lower%s" %
|
|
(prefix, letter))
|
|
file_item.write(letter.encode('ascii') * 10)
|
|
|
|
man1 = cls.container.file("man1")
|
|
man1.write(b'man1-contents',
|
|
hdrs={"X-Object-Manifest": "%s/%s/seg_lower" %
|
|
(cls.container.name, prefix)})
|
|
|
|
man2 = cls.container.file("man2")
|
|
man2.write(b'man2-contents',
|
|
hdrs={"X-Object-Manifest": "%s/%s/seg_upper_%%25ff" %
|
|
(cls.container.name, prefix)})
|
|
|
|
manall = cls.container.file("manall")
|
|
manall.write(b'manall-contents',
|
|
hdrs={"X-Object-Manifest": "%s/%s/seg" %
|
|
(cls.container.name, prefix)})
|
|
|
|
mancont2 = cls.container.file("mancont2")
|
|
mancont2.write(
|
|
b'mancont2-contents',
|
|
hdrs={"X-Object-Manifest": "%s/%s/seg_lower" %
|
|
(cls.container2.name, prefix)})
|
|
|
|
|
|
class TestDlo(Base):
|
|
env = TestDloEnv
|
|
|
|
def test_get_manifest(self):
|
|
file_item = self.env.container.file('man1')
|
|
file_contents = file_item.read()
|
|
self.assertEqual(
|
|
file_contents,
|
|
b"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee")
|
|
|
|
file_item = self.env.container.file('man2')
|
|
file_contents = file_item.read()
|
|
self.assertEqual(
|
|
file_contents,
|
|
b"AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEE")
|
|
|
|
file_item = self.env.container.file('manall')
|
|
file_contents = file_item.read()
|
|
self.assertEqual(
|
|
file_contents,
|
|
(b"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" +
|
|
b"AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEE"))
|
|
|
|
def test_get_manifest_document_itself(self):
|
|
file_item = self.env.container.file('man1')
|
|
file_contents = file_item.read(parms={'multipart-manifest': 'get'})
|
|
self.assertEqual(file_contents, b"man1-contents")
|
|
self.assertEqual(file_item.info()['x_object_manifest'],
|
|
"%s/%s/seg_lower" %
|
|
(self.env.container.name, self.env.segment_prefix))
|
|
|
|
def test_get_range(self):
|
|
file_item = self.env.container.file('man1')
|
|
file_contents = file_item.read(size=25, offset=8)
|
|
self.assertEqual(file_contents, b"aabbbbbbbbbbccccccccccddd")
|
|
|
|
file_contents = file_item.read(size=1, offset=47)
|
|
self.assertEqual(file_contents, b"e")
|
|
|
|
def test_get_multiple_ranges(self):
|
|
file_item = self.env.container.file('man1')
|
|
file_contents = file_item.read(
|
|
hdrs={'Range': 'bytes=0-4,10-14'})
|
|
self.assert_status(200) # *not* 206
|
|
self.assertEqual(
|
|
file_contents,
|
|
b"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee")
|
|
|
|
def test_get_range_out_of_range(self):
|
|
file_item = self.env.container.file('man1')
|
|
|
|
self.assertRaises(ResponseError, file_item.read, size=7, offset=50)
|
|
self.assert_status(416)
|
|
self.assert_header('content-range', 'bytes */50')
|
|
|
|
def test_copy(self):
|
|
# Adding a new segment, copying the manifest, and then deleting the
|
|
# segment proves that the new object is really the concatenated
|
|
# segments and not just a manifest.
|
|
f_segment = self.env.container.file("%s/seg_lowerf" %
|
|
(self.env.segment_prefix))
|
|
f_segment.write(b'ffffffffff')
|
|
try:
|
|
man1_item = self.env.container.file('man1')
|
|
man1_item.copy(self.env.container.name, "copied-man1")
|
|
finally:
|
|
# try not to leave this around for other tests to stumble over
|
|
f_segment.delete(tolerate_missing=True)
|
|
|
|
file_item = self.env.container.file('copied-man1')
|
|
file_contents = file_item.read()
|
|
self.assertEqual(
|
|
file_contents,
|
|
b"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff")
|
|
# The copied object must not have X-Object-Manifest
|
|
self.assertNotIn("x_object_manifest", file_item.info())
|
|
|
|
def test_copy_account(self):
|
|
# dlo use same account and same container only
|
|
acct = urllib.parse.unquote(self.env.conn.account_name)
|
|
# Adding a new segment, copying the manifest, and then deleting the
|
|
# segment proves that the new object is really the concatenated
|
|
# segments and not just a manifest.
|
|
f_segment = self.env.container.file("%s/seg_lowerf" %
|
|
(self.env.segment_prefix))
|
|
f_segment.write(b'ffffffffff')
|
|
try:
|
|
man1_item = self.env.container.file('man1')
|
|
man1_item.copy_account(acct,
|
|
self.env.container.name,
|
|
"copied-man1")
|
|
finally:
|
|
# try not to leave this around for other tests to stumble over
|
|
f_segment.delete(tolerate_missing=True)
|
|
|
|
file_item = self.env.container.file('copied-man1')
|
|
file_contents = file_item.read()
|
|
self.assertEqual(
|
|
file_contents,
|
|
b"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff")
|
|
# The copied object must not have X-Object-Manifest
|
|
self.assertNotIn("x_object_manifest", file_item.info())
|
|
|
|
def test_copy_manifest(self):
|
|
# Copying the manifest with multipart-manifest=get query string
|
|
# should result in another manifest
|
|
try:
|
|
man1_item = self.env.container.file('man1')
|
|
man1_item.copy(self.env.container.name, "copied-man1",
|
|
parms={'multipart-manifest': 'get'})
|
|
|
|
copied = self.env.container.file("copied-man1")
|
|
copied_contents = copied.read(parms={'multipart-manifest': 'get'})
|
|
self.assertEqual(copied_contents, b"man1-contents")
|
|
|
|
copied_contents = copied.read()
|
|
self.assertEqual(
|
|
copied_contents,
|
|
b"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee")
|
|
self.assertEqual(man1_item.info()['x_object_manifest'],
|
|
copied.info()['x_object_manifest'])
|
|
finally:
|
|
# try not to leave this around for other tests to stumble over
|
|
self.env.container.file("copied-man1").delete(
|
|
tolerate_missing=True)
|
|
|
|
def test_dlo_if_match_get(self):
|
|
manifest = self.env.container.file("man1")
|
|
etag = manifest.info()['etag']
|
|
|
|
self.assertRaises(ResponseError, manifest.read,
|
|
hdrs={'If-Match': 'not-%s' % etag})
|
|
self.assert_status(412)
|
|
|
|
manifest.read(hdrs={'If-Match': etag})
|
|
self.assert_status(200)
|
|
|
|
def test_dlo_if_none_match_get(self):
|
|
manifest = self.env.container.file("man1")
|
|
etag = manifest.info()['etag']
|
|
|
|
self.assertRaises(ResponseError, manifest.read,
|
|
hdrs={'If-None-Match': etag})
|
|
self.assert_status(304)
|
|
|
|
manifest.read(hdrs={'If-None-Match': "not-%s" % etag})
|
|
self.assert_status(200)
|
|
|
|
def test_dlo_if_match_head(self):
|
|
manifest = self.env.container.file("man1")
|
|
etag = manifest.info()['etag']
|
|
|
|
self.assertRaises(ResponseError, manifest.info,
|
|
hdrs={'If-Match': 'not-%s' % etag})
|
|
self.assert_status(412)
|
|
|
|
manifest.info(hdrs={'If-Match': etag})
|
|
self.assert_status(200)
|
|
|
|
def test_dlo_if_none_match_head(self):
|
|
manifest = self.env.container.file("man1")
|
|
etag = manifest.info()['etag']
|
|
|
|
self.assertRaises(ResponseError, manifest.info,
|
|
hdrs={'If-None-Match': etag})
|
|
self.assert_status(304)
|
|
|
|
manifest.info(hdrs={'If-None-Match': "not-%s" % etag})
|
|
self.assert_status(200)
|
|
|
|
def test_dlo_referer_on_segment_container(self):
|
|
if 'username3' not in tf.config:
|
|
self.skipTest('Requires user 3')
|
|
# First the account2 (test3) should fail
|
|
config2 = tf.config.copy()
|
|
config2['username'] = tf.config['username3']
|
|
config2['password'] = tf.config['password3']
|
|
conn2 = Connection(config2)
|
|
conn2.authenticate()
|
|
headers = {'X-Auth-Token': conn2.storage_token,
|
|
'Referer': 'http://blah.example.com'}
|
|
dlo_file = self.env.container.file("mancont2")
|
|
self.assertRaises(ResponseError, dlo_file.read,
|
|
hdrs=headers)
|
|
self.assert_status(403)
|
|
|
|
# Now set the referer on the dlo container only
|
|
referer_metadata = {'X-Container-Read': '.r:*.example.com,.rlistings'}
|
|
self.env.container.update_metadata(referer_metadata)
|
|
|
|
self.assertRaises(ResponseError, dlo_file.read,
|
|
hdrs=headers)
|
|
self.assert_status(403)
|
|
|
|
# Finally set the referer on the segment container
|
|
self.env.container2.update_metadata(referer_metadata)
|
|
|
|
contents = dlo_file.read(hdrs=headers)
|
|
self.assertEqual(
|
|
contents,
|
|
b"ffffffffffgggggggggghhhhhhhhhhiiiiiiiiiijjjjjjjjjj")
|
|
|
|
def test_dlo_post_with_manifest_header(self):
|
|
# verify that performing a POST to a DLO manifest
|
|
# preserves the fact that it is a manifest file.
|
|
# verify that the x-object-manifest header may be updated.
|
|
|
|
# create a new manifest for this test to avoid test coupling.
|
|
x_o_m = self.env.container.file('man1').info()['x_object_manifest']
|
|
file_item = self.env.container.file(Utils.create_name())
|
|
file_item.write(b'manifest-contents',
|
|
hdrs={"X-Object-Manifest": x_o_m})
|
|
|
|
# sanity checks
|
|
manifest_contents = file_item.read(parms={'multipart-manifest': 'get'})
|
|
self.assertEqual(b'manifest-contents', manifest_contents)
|
|
expected_contents = ''.join((c * 10) for c in 'abcde').encode('ascii')
|
|
contents = file_item.read(parms={})
|
|
self.assertEqual(expected_contents, contents)
|
|
|
|
# POST a modified x-object-manifest value
|
|
new_x_o_m = x_o_m.rstrip('lower') + 'upper'
|
|
file_item.post({'x-object-meta-foo': 'bar',
|
|
'x-object-manifest': new_x_o_m})
|
|
|
|
# verify that x-object-manifest was updated
|
|
file_item.info()
|
|
resp_headers = [(h.lower(), v)
|
|
for h, v in file_item.conn.response.getheaders()]
|
|
self.assertIn(('x-object-manifest', str_to_wsgi(new_x_o_m)),
|
|
resp_headers)
|
|
self.assertIn(('x-object-meta-foo', 'bar'), resp_headers)
|
|
|
|
# verify that manifest content was not changed
|
|
manifest_contents = file_item.read(parms={'multipart-manifest': 'get'})
|
|
self.assertEqual(b'manifest-contents', manifest_contents)
|
|
|
|
# verify that updated manifest points to new content
|
|
expected_contents = ''.join((c * 10) for c in 'ABCDE').encode('ascii')
|
|
contents = file_item.read(parms={})
|
|
self.assertEqual(expected_contents, contents)
|
|
|
|
# Now revert the manifest to point to original segments, including a
|
|
# multipart-manifest=get param just to check that has no effect
|
|
file_item.post({'x-object-manifest': x_o_m},
|
|
parms={'multipart-manifest': 'get'})
|
|
|
|
# verify that x-object-manifest was reverted
|
|
info = file_item.info()
|
|
self.assertIn('x_object_manifest', info)
|
|
self.assertEqual(x_o_m, info['x_object_manifest'])
|
|
|
|
# verify that manifest content was not changed
|
|
manifest_contents = file_item.read(parms={'multipart-manifest': 'get'})
|
|
self.assertEqual(b'manifest-contents', manifest_contents)
|
|
|
|
# verify that updated manifest points new content
|
|
expected_contents = ''.join((c * 10) for c in 'abcde').encode('ascii')
|
|
contents = file_item.read(parms={})
|
|
self.assertEqual(expected_contents, contents)
|
|
|
|
def test_dlo_post_without_manifest_header(self):
|
|
# verify that a POST to a DLO manifest object with no
|
|
# x-object-manifest header will cause the existing x-object-manifest
|
|
# header to be lost
|
|
|
|
# create a new manifest for this test to avoid test coupling.
|
|
x_o_m = self.env.container.file('man1').info()['x_object_manifest']
|
|
file_item = self.env.container.file(Utils.create_name())
|
|
file_item.write(b'manifest-contents',
|
|
hdrs={"X-Object-Manifest": x_o_m})
|
|
|
|
# sanity checks
|
|
manifest_contents = file_item.read(parms={'multipart-manifest': 'get'})
|
|
self.assertEqual(b'manifest-contents', manifest_contents)
|
|
expected_contents = ''.join((c * 10) for c in 'abcde').encode('ascii')
|
|
contents = file_item.read(parms={})
|
|
self.assertEqual(expected_contents, contents)
|
|
|
|
# POST with no x-object-manifest header
|
|
file_item.post({})
|
|
|
|
# verify that existing x-object-manifest was removed
|
|
info = file_item.info()
|
|
self.assertNotIn('x_object_manifest', info)
|
|
|
|
# verify that object content was not changed
|
|
manifest_contents = file_item.read(parms={'multipart-manifest': 'get'})
|
|
self.assertEqual(b'manifest-contents', manifest_contents)
|
|
|
|
# verify that object is no longer a manifest
|
|
contents = file_item.read(parms={})
|
|
self.assertEqual(b'manifest-contents', contents)
|
|
|
|
def test_dlo_post_with_manifest_regular_object(self):
|
|
# verify that performing a POST to a regular object
|
|
# with a manifest header will create a DLO.
|
|
|
|
# Put a regular object
|
|
file_item = self.env.container.file(Utils.create_name())
|
|
file_item.write(b'file contents', hdrs={})
|
|
|
|
# sanity checks
|
|
file_contents = file_item.read(parms={})
|
|
self.assertEqual(b'file contents', file_contents)
|
|
|
|
# get the path associated with man1
|
|
x_o_m = self.env.container.file('man1').info()['x_object_manifest']
|
|
|
|
# POST a x-object-manifest value to the regular object
|
|
file_item.post({'x-object-manifest': x_o_m})
|
|
|
|
# verify that the file is now a manifest
|
|
manifest_contents = file_item.read(parms={'multipart-manifest': 'get'})
|
|
self.assertEqual(b'file contents', manifest_contents)
|
|
expected_contents = ''.join([(c * 10) for c in 'abcde']).encode()
|
|
contents = file_item.read(parms={})
|
|
self.assertEqual(expected_contents, contents)
|
|
file_item.info()
|
|
resp_headers = [(h.lower(), v)
|
|
for h, v in file_item.conn.response.getheaders()]
|
|
self.assertIn(('x-object-manifest', str_to_wsgi(x_o_m)), resp_headers)
|
|
|
|
|
|
class TestDloUTF8(Base2, TestDlo):
|
|
pass
|