def test_registered_nondefaults(self): domain_remap.filter_factory({'default_reseller_prefix': 'cupcake', 'mangle_client_paths': 'yes'}) swift_info = utils.get_swift_info() self.assertIn('domain_remap', swift_info) self.assertEqual(swift_info['domain_remap'], { 'default_reseller_prefix': 'cupcake'})
def PUT(self, req): """ Handles PUT Bucket versioning. """ self.set_s3api_command(req, 'put-bucket-versioning') if 'object_versioning' not in get_swift_info(): raise S3NotImplemented() xml = req.xml(MAX_PUT_VERSIONING_BODY_SIZE) try: elem = fromstring(xml, 'VersioningConfiguration') status = elem.find('./Status').text except (XMLSyntaxError, DocumentInvalid): raise MalformedXML() except Exception as e: self.logger.error(e) raise if status not in ['Enabled', 'Suspended']: raise MalformedXML() # Set up versioning # NB: object_versioning responsible for ensuring its container exists req.headers['X-Versions-Enabled'] = str(status == 'Enabled').lower() req.get_response(self.app, 'POST') return HTTPOk()
def test_registered_nondefaults(self): domain_remap.filter_factory({'default_reseller_prefix': 'cupcake'}) swift_info = utils.get_swift_info() self.assertTrue('domain_remap' in swift_info) self.assertEqual( swift_info['domain_remap'].get('default_reseller_prefix'), 'cupcake')
def test_registered_defaults(self): def check_key_is_absnet(key): try: swift_info[key] except KeyError as err: if key not in err: raise test_limits = { 'account_ratelimit': 1, 'max_sleep_time_seconds': 60, 'container_ratelimit_0': 0, 'container_ratelimit_10': 10, 'container_ratelimit_50': 50, 'container_listing_ratelimit_0': 0, 'container_listing_ratelimit_10': 10, 'container_listing_ratelimit_50': 50 } ratelimit.filter_factory(test_limits)('have to pass in an app') swift_info = utils.get_swift_info() self.assertTrue('ratelimit' in swift_info) self.assertEqual(swift_info['ratelimit']['account_ratelimit'], 1.0) self.assertEqual(swift_info['ratelimit']['max_sleep_time_seconds'], 60.0) self.assertEqual(swift_info['ratelimit']['container_ratelimits'][0][0], 0) self.assertEqual(swift_info['ratelimit']['container_ratelimits'][0][1], 0.0) self.assertEqual(swift_info['ratelimit']['container_ratelimits'][1][0], 10) self.assertEqual(swift_info['ratelimit']['container_ratelimits'][1][1], 10.0) self.assertEqual(swift_info['ratelimit']['container_ratelimits'][2][0], 50) self.assertEqual(swift_info['ratelimit']['container_ratelimits'][2][1], 50.0) self.assertEqual( swift_info['ratelimit']['container_listing_ratelimits'][0][0], 0) self.assertEqual( swift_info['ratelimit']['container_listing_ratelimits'][0][1], 0.0) self.assertEqual( swift_info['ratelimit']['container_listing_ratelimits'][1][0], 10) self.assertEqual( swift_info['ratelimit']['container_listing_ratelimits'][1][1], 10.0) self.assertEqual( swift_info['ratelimit']['container_listing_ratelimits'][2][0], 50) self.assertEqual( swift_info['ratelimit']['container_listing_ratelimits'][2][1], 50.0) # these were left out on purpose for key in [ 'log_sleep_time_seconds', 'clock_accuracy', 'rate_buffer_seconds', 'ratelimit_whitelis', 'ratelimit_blacklist' ]: check_key_is_absnet(key)
def test_registered_nondefaults(self): domain_remap.filter_factory({ 'default_reseller_prefix': 'cupcake', 'mangle_client_paths': 'yes' }) swift_info = utils.get_swift_info() self.assertIn('domain_remap', swift_info) self.assertEqual(swift_info['domain_remap'], {'default_reseller_prefix': 'cupcake'})
def test_registered_defaults(self): bulk.filter_factory({}) swift_info = utils.get_swift_info() self.assertTrue("bulk_upload" in swift_info) self.assertTrue(isinstance(swift_info["bulk_upload"].get("max_containers_per_extraction"), numbers.Integral)) self.assertTrue(isinstance(swift_info["bulk_upload"].get("max_failed_extractions"), numbers.Integral)) self.assertTrue("bulk_delete" in swift_info) self.assertTrue(isinstance(swift_info["bulk_delete"].get("max_deletes_per_request"), numbers.Integral)) self.assertTrue(isinstance(swift_info["bulk_delete"].get("max_failed_deletes"), numbers.Integral))
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" """ Handles requests to /info Should return a WSGI-style callable (such as swob.Response). :param req: swob.Request object """ if not self.expose_info: return HTTPForbidden(request=req) admin_request = False sig = req.params.get('swiftinfo_sig', '') expires = req.params.get('swiftinfo_expires', '') if sig != '' or expires != '': admin_request = True if not self.admin_key: return HTTPForbidden(request=req) try: expires = int(expires) except ValueError: return HTTPUnauthorized(request=req) if expires < time(): return HTTPUnauthorized(request=req) valid_sigs = [] for method in self.allowed_hmac_methods[req.method]: valid_sigs.append(get_hmac(method, '/info', expires, self.admin_key)) # While it's true that any() will short-circuit, this doesn't # affect the timing-attack resistance since the only way this will # short-circuit is when a valid signature is passed in. is_valid_hmac = any(streq_const_time(valid_sig, sig) for valid_sig in valid_sigs) if not is_valid_hmac: return HTTPUnauthorized(request=req) headers = {} if 'Origin' in req.headers: headers['Access-Control-Allow-Origin'] = req.headers['Origin'] headers['Access-Control-Expose-Headers'] = ', '.join( ['x-trans-id']) #json.dumps(dict)可以将字典形式的dict对象转换为json格式的对象 info = json.dumps(get_swift_info( admin=admin_request, disallowed_sections=self.disallowed_sections)) return HTTPOk(request=req, headers=headers, body=info, content_type='application/json; charset=UTF-8')
def test_registered_defaults(self): def check_key_is_absent(key): try: swift_info[key] except KeyError as err: if key not in str(err): raise test_limits = {'account_ratelimit': 1, 'max_sleep_time_seconds': 60, 'container_ratelimit_0': 0, 'container_ratelimit_10': 10, 'container_ratelimit_50': 50, 'container_listing_ratelimit_0': 0, 'container_listing_ratelimit_10': 10, 'container_listing_ratelimit_50': 50} ratelimit.filter_factory(test_limits)('have to pass in an app') swift_info = utils.get_swift_info() self.assertIn('ratelimit', swift_info) self.assertEqual(swift_info['ratelimit'] ['account_ratelimit'], 1.0) self.assertEqual(swift_info['ratelimit'] ['max_sleep_time_seconds'], 60.0) self.assertEqual(swift_info['ratelimit'] ['container_ratelimits'][0][0], 0) self.assertEqual(swift_info['ratelimit'] ['container_ratelimits'][0][1], 0.0) self.assertEqual(swift_info['ratelimit'] ['container_ratelimits'][1][0], 10) self.assertEqual(swift_info['ratelimit'] ['container_ratelimits'][1][1], 10.0) self.assertEqual(swift_info['ratelimit'] ['container_ratelimits'][2][0], 50) self.assertEqual(swift_info['ratelimit'] ['container_ratelimits'][2][1], 50.0) self.assertEqual(swift_info['ratelimit'] ['container_listing_ratelimits'][0][0], 0) self.assertEqual(swift_info['ratelimit'] ['container_listing_ratelimits'][0][1], 0.0) self.assertEqual(swift_info['ratelimit'] ['container_listing_ratelimits'][1][0], 10) self.assertEqual(swift_info['ratelimit'] ['container_listing_ratelimits'][1][1], 10.0) self.assertEqual(swift_info['ratelimit'] ['container_listing_ratelimits'][2][0], 50) self.assertEqual(swift_info['ratelimit'] ['container_listing_ratelimits'][2][1], 50.0) # these were left out on purpose for key in ['log_sleep_time_seconds', 'clock_accuracy', 'rate_buffer_seconds', 'ratelimit_whitelis', 'ratelimit_blacklist']: check_key_is_absent(key)
def test_registered_defaults(self): name_check.filter_factory({})(FakeApp()) swift_info = utils.get_swift_info() self.assertTrue('name_check' in swift_info) self.assertTrue( isinstance(swift_info['name_check'].get('maximum_length'), numbers.Integral)) self.assertTrue( isinstance(swift_info['name_check'].get('forbidden_chars'), str)) self.assertTrue( isinstance(swift_info['name_check'].get('forbidden_regexp'), str))
def versioning_filter(app): if allow_object_versioning: if 'symlink' not in get_swift_info(): raise ValueError('object versioning requires symlinks') if allow_oio_versioning: vfunc = OioObjectVersioningMiddleware.is_valid_version_id register_swift_info('object_versioning', is_valid_version_id=vfunc) return OioObjectVersioningMiddleware(app, conf) app = ObjectVersioningMiddleware(app, conf) return VersionedWritesMiddleware(app, conf)
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" """ Handles requests to /info Should return a WSGI-style callable (such as swob.Response). :param req: swob.Request object """ if not self.expose_info: return HTTPForbidden(request=req) admin_request = False sig = req.params.get('swiftinfo_sig', '') expires = req.params.get('swiftinfo_expires', '') if sig != '' or expires != '': admin_request = True if not self.admin_key: return HTTPForbidden(request=req) try: expires = int(expires) except ValueError: return HTTPUnauthorized(request=req) if expires < time(): return HTTPUnauthorized(request=req) valid_sigs = [] for method in self.allowed_hmac_methods[req.method]: valid_sigs.append( get_hmac(method, '/info', expires, self.admin_key)) # While it's true that any() will short-circuit, this doesn't # affect the timing-attack resistance since the only way this will # short-circuit is when a valid signature is passed in. is_valid_hmac = any( streq_const_time(valid_sig, sig) for valid_sig in valid_sigs) if not is_valid_hmac: return HTTPUnauthorized(request=req) headers = {} if 'Origin' in req.headers: headers['Access-Control-Allow-Origin'] = req.headers['Origin'] headers['Access-Control-Expose-Headers'] = ', '.join( ['x-trans-id']) info = json.dumps( get_swift_info(admin=admin_request, disallowed_sections=self.disallowed_sections)) return HTTPOk(request=req, headers=headers, body=info, content_type='application/json; charset=UTF-8')
def test_registered_configured_options(self): conf = {'maximum_length': 512, 'forbidden_chars': '\'\"`', 'forbidden_regexp': "/\./|/\.\./|/\.$"} name_check.filter_factory(conf)(FakeApp()) swift_info = utils.get_swift_info() self.assertTrue('name_check' in swift_info) self.assertEqual(swift_info['name_check'].get('maximum_length'), 512) self.assertEqual(set(swift_info['name_check'].get('forbidden_chars')), set('\'\"`')) self.assertEqual(swift_info['name_check'].get('forbidden_regexp'), "/\./|/\.\./|/\.$")
def test_registered_defaults(self): filter_factory(CONF) swift_info = utils.get_swift_info() self.assertTrue('swift3' in swift_info) self.assertEqual(swift_info['swift3'].get('max_bucket_listing'), CONF.max_bucket_listing) self.assertEqual(swift_info['swift3'].get('max_parts_listing'), CONF.max_parts_listing) self.assertEqual(swift_info['swift3'].get('max_upload_part_num'), CONF.max_upload_part_num) self.assertEqual(swift_info['swift3'].get('max_multi_delete_objects'), CONF.max_multi_delete_objects)
def test_registered_defaults(self): filter_factory(self.conf) swift_info = utils.get_swift_info() self.assertTrue('s3api' in swift_info) self.assertEqual(swift_info['s3api'].get('max_bucket_listing'), self.conf.max_bucket_listing) self.assertEqual(swift_info['s3api'].get('max_parts_listing'), self.conf.max_parts_listing) self.assertEqual(swift_info['s3api'].get('max_upload_part_num'), self.conf.max_upload_part_num) self.assertEqual(swift_info['s3api'].get('max_multi_delete_objects'), self.conf.max_multi_delete_objects)
def test_registered_defaults(self): name_check.filter_factory({})(FakeApp()) swift_info = utils.get_swift_info() self.assertTrue('name_check' in swift_info) self.assertTrue(isinstance( swift_info['name_check'].get('maximum_length'), numbers.Integral)) self.assertTrue(isinstance( swift_info['name_check'].get('forbidden_chars'), str)) self.assertTrue(isinstance( swift_info['name_check'].get('forbidden_regexp'), str))
def _add_object(self, req, elem, o, encoding_type, listing_type, fetch_owner): name = o['name'] if encoding_type == 'url': name = quote(name.encode('utf-8')) if listing_type == 'object-versions': if o['content_type'] == DELETE_MARKER_CONTENT_TYPE: contents = SubElement(elem, 'DeleteMarker') else: contents = SubElement(elem, 'Version') SubElement(contents, 'Key').text = name SubElement(contents, 'VersionId').text = o.get('version_id') or 'null' if 'object_versioning' in get_swift_info(): SubElement( contents, 'IsLatest').text = ('true' if o['is_latest'] else 'false') else: SubElement(contents, 'IsLatest').text = 'true' else: contents = SubElement(elem, 'Contents') SubElement(contents, 'Key').text = name SubElement(contents, 'LastModified').text = \ o['last_modified'][:-3] + 'Z' if contents.tag != 'DeleteMarker': if 's3_etag' in o: # New-enough MUs are already in the right format etag = o['s3_etag'] elif 'slo_etag' in o: # SLOs may be in something *close* to the MU format etag = '"%s-N"' % o['slo_etag'].strip('"') else: # Normal objects just use the MD5 etag = o['hash'] if len(etag) < 2 or etag[::len(etag) - 1] != '""': # Normal objects just use the MD5 etag = '"%s"' % o['hash'] # This also catches sufficiently-old SLOs, but we have # no way to identify those from container listings # Otherwise, somebody somewhere (proxyfs, maybe?) made this # look like an RFC-compliant ETag; we don't need to # quote-wrap. SubElement(contents, 'ETag').text = etag SubElement(contents, 'Size').text = str(o['bytes']) if fetch_owner or listing_type != 'version-2': owner = SubElement(contents, 'Owner') SubElement(owner, 'ID').text = req.user_id SubElement(owner, 'DisplayName').text = req.user_id if contents.tag != 'DeleteMarker': SubElement(contents, 'StorageClass').text = 'STANDARD'
def test_registered_defaults(self): filter_factory(CONF) swift_info = utils.get_swift_info() self.assertTrue('swift3' in swift_info) self.assertEqual(swift_info['swift3'].get('version'), swift3.__version__) self.assertEqual(swift_info['swift3'].get('max_bucket_listing'), CONF.max_bucket_listing) self.assertEqual(swift_info['swift3'].get('max_parts_listing'), CONF.max_parts_listing) self.assertEqual(swift_info['swift3'].get('max_upload_part_num'), CONF.max_upload_part_num) self.assertEqual(swift_info['swift3'].get('max_multi_delete_objects'), CONF.max_multi_delete_objects)
def version_id_param(req): """ Get the version ID specified by the request, if any. """ version_id = req.params.get('versionId') if version_id not in ('null', None): obj_vers_info = get_swift_info().get('object_versioning') if obj_vers_info is None: raise S3NotImplemented() is_valid_version = obj_vers_info.get('is_valid_version_id', lambda x: True) if not is_valid_version(version_id): raise InvalidArgument('versionId', version_id, 'Invalid version id specified') return version_id
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" """ Handles requests to /info Should return a WSGI-style callable (such as swob.Response). :param req: swob.Request object """ if not self.expose_info: return HTTPForbidden(request=req) admin_request = False sig = req.params.get('swiftinfo_sig', '') expires = req.params.get('swiftinfo_expires', '') if sig != '' or expires != '': admin_request = True if not self.admin_key: return HTTPForbidden(request=req) try: expires = int(expires) except ValueError: return HTTPUnauthorized(request=req) if expires < time(): return HTTPUnauthorized(request=req) valid_sigs = [] for method in self.allowed_hmac_methods[req.method]: valid_sigs.append( get_hmac(method, '/info', expires, self.admin_key)) if sig not in valid_sigs: return HTTPUnauthorized(request=req) headers = {} if 'Origin' in req.headers: headers['Access-Control-Allow-Origin'] = req.headers['Origin'] headers['Access-Control-Expose-Headers'] = ', '.join( ['x-trans-id']) info = json.dumps( get_swift_info(admin=admin_request, disallowed_sections=self.disallowed_sections)) return HTTPOk(request=req, headers=headers, body=info, content_type='application/json; charset=UTF-8')
def test_registered_defaults(self): def check_key_is_absnet(key): try: swift_info[key] except KeyError as err: if key not in err: raise test_limits = { "account_ratelimit": 1, "max_sleep_time_seconds": 60, "container_ratelimit_0": 0, "container_ratelimit_10": 10, "container_ratelimit_50": 50, "container_listing_ratelimit_0": 0, "container_listing_ratelimit_10": 10, "container_listing_ratelimit_50": 50, } ratelimit.filter_factory(test_limits)("have to pass in an app") swift_info = utils.get_swift_info() self.assertTrue("ratelimit" in swift_info) self.assertEqual(swift_info["ratelimit"]["account_ratelimit"], 1.0) self.assertEqual(swift_info["ratelimit"]["max_sleep_time_seconds"], 60.0) self.assertEqual(swift_info["ratelimit"]["container_ratelimits"][0][0], 0) self.assertEqual(swift_info["ratelimit"]["container_ratelimits"][0][1], 0.0) self.assertEqual(swift_info["ratelimit"]["container_ratelimits"][1][0], 10) self.assertEqual(swift_info["ratelimit"]["container_ratelimits"][1][1], 10.0) self.assertEqual(swift_info["ratelimit"]["container_ratelimits"][2][0], 50) self.assertEqual(swift_info["ratelimit"]["container_ratelimits"][2][1], 50.0) self.assertEqual(swift_info["ratelimit"]["container_listing_ratelimits"][0][0], 0) self.assertEqual(swift_info["ratelimit"]["container_listing_ratelimits"][0][1], 0.0) self.assertEqual(swift_info["ratelimit"]["container_listing_ratelimits"][1][0], 10) self.assertEqual(swift_info["ratelimit"]["container_listing_ratelimits"][1][1], 10.0) self.assertEqual(swift_info["ratelimit"]["container_listing_ratelimits"][2][0], 50) self.assertEqual(swift_info["ratelimit"]["container_listing_ratelimits"][2][1], 50.0) # these were left out on purpose for key in [ "log_sleep_time_seconds", "clock_accuracy", "rate_buffer_seconds", "ratelimit_whitelis", "ratelimit_blacklist", ]: check_key_is_absnet(key)
def DELETE(self, req): """ Handle DELETE Object request """ if 'versionId' in req.params and \ req.params['versionId'] != 'null' and \ 'object_versioning' not in get_swift_info(): raise S3NotImplemented() version_id = req.params.get('versionId') if version_id not in ('null', None): container_info = req.get_container_info(self.app) if not container_info.get('sysmeta', {}).get( 'versions-container', ''): # Versioning has never been enabled return HTTPNoContent(headers={'x-amz-version-id': version_id}) try: try: query = req.gen_multipart_manifest_delete_query( self.app, version=version_id) except NoSuchKey: query = {} req.headers['Content-Type'] = None # Ignore client content-type if version_id is not None: query['version-id'] = version_id query['symlink'] = 'get' resp = req.get_response(self.app, query=query) if query.get('multipart-manifest') and resp.status_int == HTTP_OK: for chunk in resp.app_iter: pass # drain the bulk-deleter response resp.status = HTTP_NO_CONTENT resp.body = b'' if resp.sw_headers.get('X-Object-Current-Version-Id') == 'null': new_resp = self._restore_on_delete(req) if new_resp: resp = new_resp except NoSuchKey: # expect to raise NoSuchBucket when the bucket doesn't exist req.get_container_info(self.app) # else -- it's gone! Success. return HTTPNoContent() return resp
def GETorHEAD(self, req): had_match = False for match_header in ('if-match', 'if-none-match'): if match_header not in req.headers: continue had_match = True for value in list_from_csv(req.headers[match_header]): value = normalize_etag(value) if value.endswith('-N'): # Deal with fake S3-like etags for SLOs uploaded via Swift req.headers[match_header] += ', ' + value[:-2] if had_match: # Update where to look update_etag_is_at_header(req, sysmeta_header('object', 'etag')) object_name = req.object_name version_id = req.params.get('versionId') if version_id not in ('null', None) and \ 'object_versioning' not in get_swift_info(): raise S3NotImplemented() query = {} if version_id is None else {'version-id': version_id} if version_id not in ('null', None): container_info = req.get_container_info(self.app) if not container_info.get('sysmeta', {}).get( 'versions-container', ''): # Versioning has never been enabled raise NoSuchVersion(object_name, version_id) resp = req.get_response(self.app, query=query) if req.method == 'HEAD': resp.app_iter = None if 'x-amz-meta-deleted' in resp.headers: raise NoSuchKey(object_name) for key in ('content-type', 'content-language', 'expires', 'cache-control', 'content-disposition', 'content-encoding'): if 'response-' + key in req.params: resp.headers[key] = req.params['response-' + key] return resp
def test_registered_defaults(self): bulk.filter_factory({}) swift_info = utils.get_swift_info() self.assertTrue('bulk_upload' in swift_info) self.assertTrue(isinstance( swift_info['bulk_upload'].get('max_containers_per_extraction'), numbers.Integral)) self.assertTrue(isinstance( swift_info['bulk_upload'].get('max_failed_extractions'), numbers.Integral)) self.assertTrue('bulk_delete' in swift_info) self.assertTrue(isinstance( swift_info['bulk_delete'].get('max_deletes_per_request'), numbers.Integral)) self.assertTrue(isinstance( swift_info['bulk_delete'].get('max_failed_deletes'), numbers.Integral))
def GETorHEAD(self, req): """Handler for HTTP GET/HEAD requests.""" """ Handles requests to /info Should return a WSGI-style callable (such as swob.Response). :param req: swob.Request object """ if not self.expose_info: return HTTPForbidden(request=req) admin_request = False sig = req.params.get("swiftinfo_sig", "") expires = req.params.get("swiftinfo_expires", "") if sig != "" or expires != "": admin_request = True if not self.admin_key: return HTTPForbidden(request=req) try: expires = int(expires) except ValueError: return HTTPUnauthorized(request=req) if expires < time(): return HTTPUnauthorized(request=req) valid_sigs = [] for method in self.allowed_hmac_methods[req.method]: valid_sigs.append(get_hmac(method, "/info", expires, self.admin_key)) if sig not in valid_sigs: return HTTPUnauthorized(request=req) headers = {} if "Origin" in req.headers: headers["Access-Control-Allow-Origin"] = req.headers["Origin"] headers["Access-Control-Expose-Headers"] = ", ".join(["x-trans-id"]) info = json.dumps(get_swift_info(admin=admin_request, disallowed_sections=self.disallowed_sections)) return HTTPOk(request=req, headers=headers, body=info, content_type="application/json; charset=UTF-8")
def do_test(conf, expect_enabled): fake_app = object() with mock.patch.dict('swift.common.utils._swift_admin_info', clear=True): # we're not expecting utils._swift_info to be modified but mock # it anyway just in case it is with mock.patch.dict('swift.common.utils._swift_info', clear=True): # Sanity checks... self.assertNotIn('encryption', utils._swift_admin_info) self.assertNotIn('encryption', utils.get_swift_info(admin=True)) self.assertNotIn('encryption', utils.get_swift_info(admin=True)['admin']) factory = crypto.filter_factory(conf) self.assertTrue(callable(factory)) filtered_app = factory(fake_app) self.assertNotIn('encryption', utils._swift_info) self.assertNotIn('encryption', utils.get_swift_info()) self.assertNotIn('encryption', utils.get_swift_info(admin=True)) self.assertIn('encryption', utils._swift_admin_info) self.assertDictEqual({'enabled': expect_enabled}, utils._swift_admin_info['encryption']) self.assertIn('encryption', utils.get_swift_info(admin=True)['admin']) self.assertDictEqual( {'enabled': expect_enabled}, utils.get_swift_info( admin=True)['admin']['encryption']) self.assertIsInstance(filtered_app, crypto.decrypter.Decrypter) self.assertIsInstance(filtered_app.app, crypto.encrypter.Encrypter) self.assertIs(filtered_app.app.app, fake_app)
def versioning_filter(app): if allow_object_versioning: if 'symlink' not in get_swift_info(): raise ValueError('object versioning requires symlinks') app = ObjectVersioningMiddleware(app, conf) return VersionedWritesMiddleware(app, conf)
def test_registered_defaults(self): tempurl.filter_factory({}) swift_info = utils.get_swift_info() self.assertTrue("tempurl" in swift_info) self.assertEqual(set(swift_info["tempurl"]["methods"]), set(("GET", "HEAD", "PUT", "POST", "DELETE")))
def test_non_default_methods(self): tempurl.filter_factory({'methods': 'GET HEAD PUT DELETE BREW'}) swift_info = utils.get_swift_info() self.assertTrue('tempurl' in swift_info) self.assertEqual(set(swift_info['tempurl']['methods']), set(('GET', 'HEAD', 'PUT', 'DELETE', 'BREW')))
def test_registered_defaults(self): tempurl.filter_factory({}) swift_info = utils.get_swift_info() self.assertTrue('tempurl' in swift_info) self.assertEqual(set(swift_info['tempurl']['methods']), set(('GET', 'HEAD', 'PUT', 'POST', 'DELETE')))
def test_non_default_methods(self): tempurl.filter_factory({"methods": "GET HEAD PUT DELETE BREW"}) swift_info = utils.get_swift_info() self.assertTrue("tempurl" in swift_info) self.assertEqual(set(swift_info["tempurl"]["methods"]), set(("GET", "HEAD", "PUT", "DELETE", "BREW")))
def test_registered_nondefaults(self): cname_lookup.filter_factory({'lookup_depth': '2'}) swift_info = utils.get_swift_info() self.assertIn('cname_lookup', swift_info) self.assertEqual(swift_info['cname_lookup'].get('lookup_depth'), 2)
def test_registered_defaults(self): domain_remap.filter_factory({}) swift_info = utils.get_swift_info() self.assertIn('domain_remap', swift_info) self.assertEqual(swift_info['domain_remap'], {'default_reseller_prefix': None})
def test_registered_defaults(self): domain_remap.filter_factory({}) swift_info = utils.get_swift_info() self.assertIn('domain_remap', swift_info) self.assertEqual(swift_info['domain_remap'], { 'default_reseller_prefix': None})
def POST(self, req): """ Handles Delete Multiple Objects. """ def object_key_iter(elem): for obj in elem.iterchildren('Object'): key = obj.find('./Key').text if not key: raise UserKeyMustBeSpecified() version = obj.find('./VersionId') if version is not None: version = version.text yield key, version max_body_size = min( # FWIW, AWS limits multideletes to 1000 keys, and swift limits # object names to 1024 bytes (by default). Add a factor of two to # allow some slop. 2 * self.conf.max_multi_delete_objects * MAX_OBJECT_NAME_LENGTH, # But, don't let operators shoot themselves in the foot 10 * 1024 * 1024) try: xml = req.xml(max_body_size) if not xml: raise MissingRequestBodyError() req.check_md5(xml) elem = fromstring(xml, 'Delete', self.logger) quiet = elem.find('./Quiet') if quiet is not None and quiet.text.lower() == 'true': self.quiet = True else: self.quiet = False delete_list = list(object_key_iter(elem)) if len(delete_list) > self.conf.max_multi_delete_objects: raise MalformedXML() except (XMLSyntaxError, DocumentInvalid): raise MalformedXML() except ErrorResponse: raise except Exception as e: self.logger.error(e) raise elem = Element('DeleteResult') # check bucket existence try: req.get_response(self.app, 'HEAD') except AccessDenied as error: body = self._gen_error_body(error, elem, delete_list) return HTTPOk(body=body) if 'object_versioning' not in get_swift_info() and any( version not in ('null', None) for _key, version in delete_list): raise S3NotImplemented() def do_delete(base_req, key, version): req = copy.copy(base_req) req.environ = copy.copy(base_req.environ) req.object_name = key if version: req.params = {'version-id': version, 'symlink': 'get'} try: try: query = req.gen_multipart_manifest_delete_query( self.app, version=version) except NoSuchKey: query = {} if version: query['version-id'] = version query['symlink'] = 'get' resp = req.get_response(self.app, method='DELETE', query=query, headers={'Accept': 'application/json'}) # Have to read the response to actually do the SLO delete if query.get('multipart-manifest'): try: delete_result = json.loads(resp.body) if delete_result['Errors']: # NB: bulk includes 404s in "Number Not Found", # not "Errors" msg_parts = [delete_result['Response Status']] msg_parts.extend( '%s: %s' % (obj, status) for obj, status in delete_result['Errors']) return key, { 'code': 'SLODeleteError', 'message': '\n'.join(msg_parts) } # else, all good except (ValueError, TypeError, KeyError): # Logs get all the gory details self.logger.exception( 'Could not parse SLO delete response: %r', resp.body) # Client gets something more generic return key, { 'code': 'SLODeleteError', 'message': 'Unexpected swift response' } except NoSuchKey: pass except ErrorResponse as e: return key, {'code': e.__class__.__name__, 'message': e._msg} except Exception: self.logger.exception( 'Unexpected Error handling DELETE of %r %r' % (req.container_name, key)) return key, {'code': 'Server Error', 'message': 'Server Error'} return key, None with StreamingPile(self.conf.multi_delete_concurrency) as pile: for key, err in pile.asyncstarmap( do_delete, ((req, key, version) for key, version in delete_list)): if err: error = SubElement(elem, 'Error') SubElement(error, 'Key').text = key SubElement(error, 'Code').text = err['code'] SubElement(error, 'Message').text = err['message'] elif not self.quiet: deleted = SubElement(elem, 'Deleted') SubElement(deleted, 'Key').text = key body = tostring(elem) return HTTPOk(body=body)
def test_registered_defaults(self): domain_remap.filter_factory({}) swift_info = utils.get_swift_info() self.assertTrue('domain_remap' in swift_info) self.assertTrue( swift_info['domain_remap'].get('default_reseller_prefix') is None)