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:
Takashi Kajinami 2024-01-29 02:38:32 +09:00
parent 156f9f4cb3
commit b43dca80a2
5 changed files with 154 additions and 18 deletions

View File

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

View File

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

View File

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

View File

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

View File

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