Skip storlet execution for symlink at object-server
... because the actual object content is not available in symlink. Change-Id: I5e4ba77eb5c689b83a7503682ccabddea8aa1608
This commit is contained in:
parent
156f9f4cb3
commit
b43dca80a2
|
@ -13,6 +13,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from swift.common.request_helpers import get_sys_meta_prefix
|
||||
from swift.common.swob import HTTPMethodNotAllowed, \
|
||||
HTTPRequestedRangeNotSatisfiable, Range
|
||||
from swift.common.utils import public
|
||||
|
@ -44,6 +45,26 @@ class StorletObjectHandler(StorletBaseHandler):
|
|||
"""
|
||||
return self.request.params.get('multipart-manifest') == 'get'
|
||||
|
||||
def is_symlink_response(self, resp):
|
||||
"""
|
||||
Determins whether the response is a symlink one
|
||||
|
||||
:param resp: swob.Response instance
|
||||
:return: Whenther the response is a slo one
|
||||
"""
|
||||
self.logger.debug(
|
||||
'Verify if {0} is a symlink'.format(self.path))
|
||||
|
||||
symlink_header = get_sys_meta_prefix('object') + 'symlink-target'
|
||||
is_symlink = symlink_header in resp.headers
|
||||
if is_symlink:
|
||||
self.logger.debug(
|
||||
'{0} is indeed a symlink'.format(self.path))
|
||||
else:
|
||||
self.logger.debug(
|
||||
'{0} is NOT a symlink'.format(self.path))
|
||||
return is_symlink
|
||||
|
||||
def _get_storlet_invocation_options(self, req):
|
||||
options = super(StorletObjectHandler, self).\
|
||||
_get_storlet_invocation_options(req)
|
||||
|
@ -108,7 +129,9 @@ class StorletObjectHandler(StorletBaseHandler):
|
|||
[self.execute_on_proxy,
|
||||
self.execute_range_on_proxy,
|
||||
self.is_slo_get_request,
|
||||
self.is_slo_response(orig_resp)])
|
||||
self.is_slo_response(orig_resp),
|
||||
self.is_symlink_response(orig_resp)
|
||||
])
|
||||
|
||||
if not_runnable:
|
||||
# Storlet must be invoked on proxy as it is:
|
||||
|
|
|
@ -93,16 +93,17 @@ class StorletProxyHandler(StorletBaseHandler):
|
|||
:param resp: swob.Response instance
|
||||
:return: Whether we should execute the storlet at proxy
|
||||
"""
|
||||
# SLO / proxy only case:
|
||||
# storlet to be invoked now at proxy side:
|
||||
slo_resposne = False
|
||||
checks = [
|
||||
self.execute_on_proxy,
|
||||
self.execute_range_on_proxy
|
||||
]
|
||||
if resp:
|
||||
slo_resposne = self.is_slo_response(resp)
|
||||
checks.extend([
|
||||
self.is_slo_response(resp),
|
||||
self.is_symlink_response(resp)
|
||||
])
|
||||
|
||||
runnable = any(
|
||||
[self.execute_on_proxy,
|
||||
self.execute_range_on_proxy,
|
||||
slo_resposne])
|
||||
runnable = any(checks)
|
||||
return runnable
|
||||
|
||||
@property
|
||||
|
@ -124,6 +125,26 @@ class StorletProxyHandler(StorletBaseHandler):
|
|||
def is_put_copy_request(self):
|
||||
return 'X-Copy-From' in self.request.headers
|
||||
|
||||
def is_symlink_response(self, resp):
|
||||
"""
|
||||
Determins whether the response is a symlink one
|
||||
|
||||
:param resp: swob.Response instance
|
||||
:return: Whenther the response is a slo one
|
||||
"""
|
||||
self.logger.debug(
|
||||
'Verify if {0} is a symlink'.format(self.path))
|
||||
|
||||
symlink_header = 'X-Symlink-Target'
|
||||
is_symlink = symlink_header in resp.headers
|
||||
if is_symlink:
|
||||
self.logger.debug(
|
||||
'{0} is indeed a symlink'.format(self.path))
|
||||
else:
|
||||
self.logger.debug(
|
||||
'{0} is NOT a symlink'.format(self.path))
|
||||
return is_symlink
|
||||
|
||||
def _parse_storlet_params(self, headers):
|
||||
"""
|
||||
Parse storlet parameters from storlet/dependency object metadata
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# Copyright (c) 2010-2017 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 swiftclient import client
|
||||
from tests.functional.python import StorletPythonFunctionalTest
|
||||
import unittest
|
||||
|
||||
|
||||
class TestSymlink(StorletPythonFunctionalTest):
|
||||
def setUp(self):
|
||||
self.storlet_log = 'simple-symlink.log'
|
||||
self.additional_headers = {}
|
||||
self.content = b'abcdefghijklmonp'
|
||||
super(TestSymlink, self).setUp(
|
||||
storlet_dir='simple',
|
||||
storlet_name='simple.py',
|
||||
storlet_main='simple.SimpleStorlet',
|
||||
storlet_file='source.txt',
|
||||
headers={})
|
||||
|
||||
symlink_target = '/'.join([self.container, self.storlet_file])
|
||||
client.put_object(self.url, self.token, self.container, 'test_link',
|
||||
'', headers={'X-Symlink-Target': symlink_target})
|
||||
|
||||
def test_get(self):
|
||||
req_headers = {'X-Run-Storlet': self.storlet_name}
|
||||
headers, content = client.get_object(
|
||||
self.url, self.token, self.container, self.storlet_file,
|
||||
headers=req_headers)
|
||||
self.assertEqual('simple', headers['x-object-meta-test'])
|
||||
self.assertEqual(self.content, content)
|
||||
|
||||
req_headers = {'X-Run-Storlet': self.storlet_name}
|
||||
headers, content = client.get_object(
|
||||
self.url, self.token, self.container, 'test_link',
|
||||
headers=req_headers)
|
||||
self.assertEqual('simple', headers['x-object-meta-test'])
|
||||
self.assertEqual(self.content, content)
|
||||
|
||||
|
||||
class TestSymlinkOnProxy(TestSymlink):
|
||||
def setUp(self):
|
||||
super(TestSymlinkOnProxy, self).setUp()
|
||||
self.additional_headers = {'X-Storlet-Run-On-Proxy': ''}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -36,7 +36,7 @@ class TestStorletMiddlewareObject(BaseTestStorletMiddleware):
|
|||
def test_call_unsupported_method(self):
|
||||
def call(method):
|
||||
path = '/sda1/p/AUTH_a/c/o'
|
||||
headers = {'X-Backend-Storlet-Policy-Index': '0',
|
||||
headers = {'X-Backend-Storage-Policy-Index': '0',
|
||||
'X-Run-Storlet': 'Storlet-1.0.jar'}
|
||||
req = Request.blank(path, environ={'REQUEST_METHOD': method},
|
||||
headers=headers)
|
||||
|
@ -64,7 +64,7 @@ class TestStorletMiddlewareObject(BaseTestStorletMiddleware):
|
|||
def basic_get(path):
|
||||
req = Request.blank(
|
||||
path, environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'X-Backend-Storlet-Policy-Index': '0'})
|
||||
headers={'X-Backend-Storage-Policy-Index': '0'})
|
||||
self.base_app.register('GET', path, HTTPOk, body=b'FAKE APP')
|
||||
resp = self.get_response(req)
|
||||
self.assertEqual('200 OK', resp.status)
|
||||
|
@ -81,7 +81,7 @@ class TestStorletMiddlewareObject(BaseTestStorletMiddleware):
|
|||
|
||||
req = Request.blank(
|
||||
target, environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'X-Backend-Storlet-Policy-Index': '0',
|
||||
headers={'X-Backend-Storage-Policy-Index': '0',
|
||||
'X-Run-Storlet': 'Storlet-1.0.jar'})
|
||||
resp = self.get_response(req)
|
||||
self.assertEqual('200 OK', resp.status)
|
||||
|
@ -93,7 +93,7 @@ class TestStorletMiddlewareObject(BaseTestStorletMiddleware):
|
|||
|
||||
req = Request.blank(
|
||||
target, environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'X-Backend-Storlet-Policy-Index': '0',
|
||||
headers={'X-Backend-Storage-Policy-Index': '0',
|
||||
'X-Run-Storlet': 'Storlet-1.0.jar',
|
||||
'Range': 'bytes=10-20'})
|
||||
resp = self.get_response(req)
|
||||
|
@ -106,7 +106,7 @@ class TestStorletMiddlewareObject(BaseTestStorletMiddleware):
|
|||
|
||||
req = Request.blank(
|
||||
target, environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'X-Backend-Storlet-Policy-Index': '0',
|
||||
headers={'X-Backend-Storage-Policy-Index': '0',
|
||||
'X-Run-Storlet': 'Storlet-1.0.jar',
|
||||
'X-Storlet-Range': 'bytes=1-6',
|
||||
'Range': 'bytes=1-6'})
|
||||
|
@ -122,7 +122,7 @@ class TestStorletMiddlewareObject(BaseTestStorletMiddleware):
|
|||
|
||||
req = Request.blank(
|
||||
target, environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'X-Backend-Storlet-Policy-Index': '0',
|
||||
headers={'X-Backend-Storage-Policy-Index': '0',
|
||||
'X-Run-Storlet': 'Storlet-1.0.jar',
|
||||
'X-Storlet-Range': 'bytes=1-6',
|
||||
'Range': 'bytes=1-6',
|
||||
|
@ -141,7 +141,7 @@ class TestStorletMiddlewareObject(BaseTestStorletMiddleware):
|
|||
|
||||
req = Request.blank(
|
||||
target, environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'X-Backend-Storlet-Policy-Index': '0',
|
||||
headers={'X-Backend-Storage-Policy-Index': '0',
|
||||
'X-Run-Storlet': 'Storlet-1.0.jar'})
|
||||
resp = self.get_response(req)
|
||||
self.assertEqual('200 OK', resp.status)
|
||||
|
@ -155,19 +155,34 @@ class TestStorletMiddlewareObject(BaseTestStorletMiddleware):
|
|||
|
||||
req = Request.blank(
|
||||
target, environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'X-Backend-Storlet-Policy-Index': '0',
|
||||
headers={'X-Backend-Storage-Policy-Index': '0',
|
||||
'multipart-manifest': 'get',
|
||||
'X-Run-Storlet': 'Storlet-1.0.jar'})
|
||||
resp = self.get_response(req)
|
||||
self.assertEqual('200 OK', resp.status)
|
||||
self.assertEqual(b'FAKE SEGMENT', resp.body)
|
||||
|
||||
def test_GET_symlink_with_storlets(self):
|
||||
target = '/sda1/p/AUTH_a/c/o'
|
||||
self.base_app.register(
|
||||
'GET', target, HTTPOk,
|
||||
headers={'X-Object-Sysmeta-Symlink-Target': 'c/o2'},
|
||||
body=b'FAKE CONTENT')
|
||||
|
||||
req = Request.blank(
|
||||
target, environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'X-Backend-Storage-Policy-Index': '0',
|
||||
'X-Run-Storlet': 'Storlet-1.0.jar'})
|
||||
resp = self.get_response(req)
|
||||
self.assertEqual('200 OK', resp.status)
|
||||
self.assertEqual(b'FAKE CONTENT', resp.body)
|
||||
|
||||
def test_storlets_with_invalid_method(self):
|
||||
target = '/sda1/p/AUTH_a/c/o'
|
||||
|
||||
req = Request.blank(
|
||||
target, environ={'REQUEST_METHOD': '_parse_vaco'},
|
||||
headers={'X-Backend-Storlet-Policy-Index': '0',
|
||||
headers={'X-Backend-Storage-Policy-Index': '0',
|
||||
'X-Run-Storlet': 'Storlet-1.0.jar'})
|
||||
resp = self.get_response(req)
|
||||
self.assertEqual('405 Method Not Allowed', resp.status)
|
||||
|
|
|
@ -220,6 +220,23 @@ class TestStorletMiddlewareProxy(BaseTestStorletMiddleware):
|
|||
calls = self.base_app.get_calls()
|
||||
self.assertEqual(2, len(calls))
|
||||
|
||||
def test_GET_symlink_with_storlets(self):
|
||||
target = '/v1/AUTH_a/c/o'
|
||||
self.base_app.register('GET', target, HTTPOk,
|
||||
headers={'x-symlink-target': 'c/o2'},
|
||||
body=b'FAKE APP')
|
||||
storlet = '/v1/AUTH_a/storlet/Storlet-1.0.jar'
|
||||
self.base_app.register('GET', storlet, HTTPOk, body=b'jar binary')
|
||||
|
||||
with storlet_enabled():
|
||||
headers = {'X-Run-Storlet': 'Storlet-1.0.jar'}
|
||||
resp = self.get_request_response(target, 'GET', headers=headers)
|
||||
self.assertEqual('200 OK', resp.status)
|
||||
self.assertEqual(b'FAKE APP', resp.body)
|
||||
|
||||
calls = self.base_app.get_calls()
|
||||
self.assertEqual(2, len(calls))
|
||||
|
||||
def test_GET_with_storlets_no_object(self):
|
||||
target = '/v1/AUTH_a/c/'
|
||||
self.base_app.register('GET', target, HTTPOk,
|
||||
|
|
Loading…
Reference in New Issue