Support to update function timeout
Change-Id: I8c7c364af54a6e33406e8bece8337b1e7eeb75aa Story: 2002174 Task: 26341
This commit is contained in:
parent
cd688170da
commit
dd169d2ae1
@ -46,7 +46,7 @@ CONF = cfg.CONF
|
||||
POST_REQUIRED = set(['code'])
|
||||
CODE_SOURCE = set(['package', 'swift', 'image'])
|
||||
UPDATE_ALLOWED = set(['name', 'description', 'code', 'package', 'entry',
|
||||
'cpu', 'memory_size'])
|
||||
'cpu', 'memory_size', 'timeout'])
|
||||
|
||||
|
||||
class FunctionWorkerController(rest.RestController):
|
||||
@ -169,7 +169,10 @@ class FunctionsController(rest.RestController):
|
||||
),
|
||||
}
|
||||
|
||||
# Check cpu and memory_size values.
|
||||
common.validate_int_in_range(
|
||||
'timeout', values['timeout'], CONF.resource_limits.min_timeout,
|
||||
CONF.resource_limits.max_timeout
|
||||
)
|
||||
common.validate_int_in_range(
|
||||
'cpu', values['cpu'], CONF.resource_limits.min_cpu,
|
||||
CONF.resource_limits.max_cpu
|
||||
@ -364,7 +367,14 @@ class FunctionsController(rest.RestController):
|
||||
LOG.info('Update function %s, params: %s', id, values)
|
||||
ctx = context.get_ctx()
|
||||
|
||||
if set(values.keys()).issubset(set(['name', 'description'])):
|
||||
if values.get('timeout'):
|
||||
common.validate_int_in_range(
|
||||
'timeout', values['timeout'], CONF.resource_limits.min_timeout,
|
||||
CONF.resource_limits.max_timeout
|
||||
)
|
||||
|
||||
db_update_only = set(['name', 'description', 'timeout'])
|
||||
if set(values.keys()).issubset(db_update_only):
|
||||
func_db = db_api.update_function(id, values)
|
||||
else:
|
||||
source = values.get('code', {}).get('source')
|
||||
|
@ -158,11 +158,6 @@ class Link(Resource):
|
||||
target = wtypes.text
|
||||
rel = wtypes.text
|
||||
|
||||
@classmethod
|
||||
def sample(cls):
|
||||
return cls(href='http://example.com/here',
|
||||
target='here', rel='self')
|
||||
|
||||
|
||||
class Function(Resource):
|
||||
id = wtypes.text
|
||||
@ -180,25 +175,6 @@ class Function(Resource):
|
||||
created_at = wtypes.text
|
||||
updated_at = wtypes.text
|
||||
|
||||
@classmethod
|
||||
def sample(cls):
|
||||
return cls(
|
||||
id='123e4567-e89b-12d3-a456-426655440000',
|
||||
name='hello_world',
|
||||
description='this is the first function.',
|
||||
cpu=1,
|
||||
memory_size=1,
|
||||
timeout=1,
|
||||
runtime_id='123e4567-e89b-12d3-a456-426655440001',
|
||||
code={'zip': True},
|
||||
entry='main',
|
||||
count=10,
|
||||
latest_version=0,
|
||||
project_id='default',
|
||||
created_at='1970-01-01T00:00:00.000000',
|
||||
updated_at='1970-01-01T00:00:00.000000'
|
||||
)
|
||||
|
||||
|
||||
class Functions(ResourceList):
|
||||
functions = [Function]
|
||||
@ -208,18 +184,6 @@ class Functions(ResourceList):
|
||||
|
||||
super(Functions, self).__init__(**kwargs)
|
||||
|
||||
@classmethod
|
||||
def sample(cls):
|
||||
sample = cls()
|
||||
sample.functions = [Function.sample()]
|
||||
sample.next = (
|
||||
"http://localhost:7070/v1/functions?"
|
||||
"sort_keys=id,name&sort_dirs=asc,desc&limit=10&"
|
||||
"marker=123e4567-e89b-12d3-a456-426655440000"
|
||||
)
|
||||
|
||||
return sample
|
||||
|
||||
|
||||
class FunctionWorker(Resource):
|
||||
function_id = wsme.wsattr(types.uuid, readonly=True)
|
||||
|
@ -259,6 +259,16 @@ rlimits_opts = [
|
||||
default=5,
|
||||
help='Default function execution timeout(unit: seconds)'
|
||||
),
|
||||
cfg.IntOpt(
|
||||
'min_timeout',
|
||||
default=1,
|
||||
help='Minimum function execution timeout(unit: seconds).'
|
||||
),
|
||||
cfg.IntOpt(
|
||||
'max_timeout',
|
||||
default=300,
|
||||
help='Maximum function execution timeout(unit: seconds).'
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -58,7 +58,8 @@ class TestFunctionController(base.APITest):
|
||||
body.update(
|
||||
{
|
||||
'entry': 'main.main',
|
||||
'code': {"source": "package", "md5sum": "fake_md5"}
|
||||
'code': {"source": "package", "md5sum": "fake_md5"},
|
||||
'timeout': cfg.CONF.resource_limits.default_timeout
|
||||
}
|
||||
)
|
||||
self._assertDictContainsSubset(resp.json, body)
|
||||
@ -82,6 +83,26 @@ class TestFunctionController(base.APITest):
|
||||
self.assertEqual(201, resp.status_int)
|
||||
self.assertEqual(3, resp.json['timeout'])
|
||||
|
||||
def test_post_timeout_invalid(self):
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
body = {
|
||||
'runtime_id': self.runtime_id,
|
||||
'code': json.dumps({"source": "package"}),
|
||||
'timeout': cfg.CONF.resource_limits.max_timeout + 1
|
||||
}
|
||||
resp = self.app.post(
|
||||
'/v1/functions',
|
||||
params=body,
|
||||
upload_files=[('package', f.name, f.read())],
|
||||
expect_errors=True
|
||||
)
|
||||
|
||||
self.assertEqual(400, resp.status_int)
|
||||
self.assertIn(
|
||||
'timeout resource limitation not within the allowable range',
|
||||
resp.json['faultstring']
|
||||
)
|
||||
|
||||
@mock.patch("qinling.utils.openstack.keystone.create_trust")
|
||||
@mock.patch('qinling.utils.openstack.keystone.get_swiftclient')
|
||||
@mock.patch('qinling.context.AuthHook.before')
|
||||
@ -217,6 +238,32 @@ class TestFunctionController(base.APITest):
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertEqual('new_name', resp.json['name'])
|
||||
|
||||
def test_put_timeout(self):
|
||||
db_func = self.create_function(runtime_id=self.runtime_id)
|
||||
|
||||
resp = self.app.put_json(
|
||||
'/v1/functions/%s' % db_func.id, {'timeout': 10}
|
||||
)
|
||||
|
||||
self.assertEqual(200, resp.status_int)
|
||||
self.assertEqual(10, resp.json['timeout'])
|
||||
|
||||
def test_put_timeout_invalid(self):
|
||||
db_func = self.create_function(runtime_id=self.runtime_id)
|
||||
|
||||
# Check for type of cpu values.
|
||||
resp = self.app.put_json(
|
||||
'/v1/functions/%s' % db_func.id,
|
||||
{'timeout': cfg.CONF.resource_limits.max_timeout + 1},
|
||||
expect_errors=True
|
||||
)
|
||||
|
||||
self.assertEqual(400, resp.status_int)
|
||||
self.assertIn(
|
||||
'timeout resource limitation not within the allowable range',
|
||||
resp.json['faultstring']
|
||||
)
|
||||
|
||||
@mock.patch('qinling.utils.etcd_util.delete_function')
|
||||
@mock.patch('qinling.storage.file_system.FileSystemStorage.store')
|
||||
@mock.patch('qinling.storage.file_system.FileSystemStorage.delete')
|
||||
|
@ -182,7 +182,7 @@ class DbTestCase(BaseTest):
|
||||
|
||||
return runtime
|
||||
|
||||
def create_function(self, runtime_id=None, code=None):
|
||||
def create_function(self, runtime_id=None, code=None, timeout=None):
|
||||
if not runtime_id:
|
||||
runtime_id = self.create_runtime().id
|
||||
|
||||
@ -197,7 +197,7 @@ class DbTestCase(BaseTest):
|
||||
'project_id': DEFAULT_PROJECT_ID,
|
||||
'cpu': cfg.CONF.resource_limits.default_cpu,
|
||||
'memory_size': cfg.CONF.resource_limits.default_memory,
|
||||
'timeout': cfg.CONF.resource_limits.default_timeout,
|
||||
'timeout': timeout or cfg.CONF.resource_limits.default_timeout
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -93,6 +93,12 @@ def generate_unicode_uuid(dashed=True):
|
||||
|
||||
|
||||
def validate_int_in_range(name, value, min_allowed, max_allowed):
|
||||
unit_mapping = {
|
||||
"cpu": "millicpu",
|
||||
"memory": "bytes",
|
||||
"timeout": "seconds"
|
||||
}
|
||||
|
||||
try:
|
||||
value_int = int(value)
|
||||
except ValueError:
|
||||
@ -103,8 +109,8 @@ def validate_int_in_range(name, value, min_allowed, max_allowed):
|
||||
if (value_int < min_allowed or value_int > max_allowed):
|
||||
raise exc.InputException(
|
||||
'%s resource limitation not within the allowable range: '
|
||||
'%s ~ %s(%s).' % (name, min_allowed, max_allowed,
|
||||
'millicpu' if name == 'cpu' else 'bytes')
|
||||
'%s ~ %s(%s).' %
|
||||
(name, min_allowed, max_allowed, unit_mapping[name])
|
||||
)
|
||||
|
||||
|
||||
|
@ -506,7 +506,27 @@ class ExecutionsTest(base.BaseQinlingTest):
|
||||
|
||||
result = jsonutils.loads(body['result'])
|
||||
|
||||
self.assertGreater(result['duration'], 5)
|
||||
self.assertGreaterEqual(result['duration'], 5)
|
||||
self.assertIn(
|
||||
'Function execution timeout', result['output']
|
||||
)
|
||||
|
||||
# Update function timeout
|
||||
resp, _ = self.client.update_function(
|
||||
function_id,
|
||||
timeout=10
|
||||
)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
resp, body = self.client.create_execution(
|
||||
function_id,
|
||||
input='{"seconds": 7}'
|
||||
)
|
||||
|
||||
self.assertEqual(201, resp.status)
|
||||
self.addCleanup(self.client.delete_resource, 'executions',
|
||||
body['id'], ignore_notfound=True)
|
||||
self.assertEqual('success', body['status'])
|
||||
|
||||
result = jsonutils.loads(body['result'])
|
||||
self.assertGreaterEqual(result['duration'], 7)
|
||||
|
Loading…
Reference in New Issue
Block a user