Browse Source

Support alias for execution creation

This commit to support alias for execution creation, to create execution
only use function_alias pramater, and get function_id and function_version
from function_alias. Also add function tests for execution creation.

Change-Id: Iea6d3b3ac3d58d539fae88ffe455013ad08d2c43
Story: #2002143
Task: #19986
changes/96/580896/7
Dong Ma 3 years ago
committed by Lingxian Kong
parent
commit
cd92cfff15
7 changed files with 169 additions and 13 deletions
  1. +6
    -0
      qinling/api/controllers/v1/execution.py
  2. +2
    -1
      qinling/api/controllers/v1/resources.py
  3. +33
    -0
      qinling/tests/unit/api/controllers/v1/test_execution.py
  4. +10
    -2
      qinling/utils/executions.py
  5. +66
    -7
      qinling_tempest_plugin/services/qinling_client.py
  6. +26
    -0
      qinling_tempest_plugin/tests/api/test_executions.py
  7. +26
    -3
      qinling_tempest_plugin/tests/base.py

+ 6
- 0
qinling/api/controllers/v1/execution.py View File

@ -23,6 +23,7 @@ from qinling.api.controllers.v1 import resources
from qinling.api.controllers.v1 import types
from qinling import context
from qinling.db import api as db_api
from qinling import exceptions as exc
from qinling import rpc
from qinling.utils import executions
from qinling.utils import rest_utils
@ -60,6 +61,11 @@ class ExecutionsController(rest.RestController):
acl.enforce('execution:create', ctx)
params = body.to_dict()
if not (params.get("function_id") or params.get("function_alias")):
raise exc.InputException(
'Either function_alias or function_id must be provided.'
)
LOG.info("Creating %s. [params=%s]", self.type, params)
db_model = executions.create_execution(self.engine_client, params)


+ 2
- 1
qinling/api/controllers/v1/resources.py View File

@ -284,8 +284,9 @@ class Runtimes(ResourceList):
class Execution(Resource):
id = types.uuid
function_id = wsme.wsattr(types.uuid, mandatory=True)
function_id = wsme.wsattr(types.uuid)
function_version = wsme.wsattr(int, default=0)
function_alias = wtypes.text
description = wtypes.text
status = wsme.wsattr(wtypes.text, readonly=True)
sync = bool


+ 33
- 0
qinling/tests/unit/api/controllers/v1/test_execution.py View File

@ -58,6 +58,39 @@ class TestExecutionController(base.APITest):
resp = self.app.get('/v1/functions/%s/versions/1' % self.func_id)
self.assertEqual(1, resp.json.get('count'))
@mock.patch('qinling.rpc.EngineClient.create_execution')
def test_post_with_alias(self, mock_rpc):
db_api.increase_function_version(self.func_id, 0,
description="version 1")
name = self.rand_name(name="alias", prefix=self.prefix)
body = {
'function_id': self.func_id,
'function_version': 1,
'name': name
}
db_api.create_function_alias(**body)
execution_body = {
'function_alias': name
}
resp = self.app.post_json('/v1/executions', execution_body)
self.assertEqual(201, resp.status_int)
resp = self.app.get('/v1/functions/%s' % self.func_id)
self.assertEqual(0, resp.json.get('count'))
resp = self.app.get('/v1/functions/%s/versions/1' % self.func_id)
self.assertEqual(1, resp.json.get('count'))
def test_post_without_required_params(self):
resp = self.app.post(
'/v1/executions',
params={},
expect_errors=True
)
self.assertEqual(400, resp.status_int)
@mock.patch('qinling.rpc.EngineClient.create_execution')
def test_post_rpc_error(self, mock_create_execution):
mock_create_execution.side_effect = exc.QinlingException


+ 10
- 2
qinling/utils/executions.py View File

@ -70,10 +70,18 @@ def _update_function_version_db(version_id, pre_count):
def create_execution(engine_client, params):
function_id = params['function_id']
function_alias = params.get('function_alias')
function_id = params.get('function_id')
version = params.get('function_version', 0)
is_sync = params.get('sync', True)
input = params.get('input')
version = params.get('function_version', 0)
if function_alias:
alias_db = db_api.get_function_alias(function_alias)
function_id = alias_db.function_id
version = alias_db.function_version
params.update({'function_id': function_id,
'version': version})
func_db = db_api.get_function(function_id)
runtime_id = func_db.runtime_id


+ 66
- 7
qinling_tempest_plugin/services/qinling_client.py View File

@ -127,13 +127,29 @@ class QinlingClient(client_base.QinlingClientBase):
return self.post(url, None, headers={})
def create_execution(self, function_id, input=None, sync=True, version=0):
req_body = {
'function_id': function_id,
'function_version': version,
'sync': sync,
'input': input
}
def create_execution(self, function_id=None, alias_name=None, input=None,
sync=True, version=0):
"""Create execution.
alias_name takes precedence over function_id.
"""
if alias_name:
req_body = {
'function_alias': alias_name,
'sync': sync,
'input': input
}
elif function_id:
req_body = {
'function_id': function_id,
'function_version': version,
'sync': sync,
'input': input
}
else:
raise Exception("Either alias_name or function_id must be "
"provided.")
resp, body = self.post_json('executions', req_body)
return resp, body
@ -208,3 +224,46 @@ class QinlingClient(client_base.QinlingClientBase):
)
return resp, json.loads(body)
def create_function_alias(self, name, function_id,
function_version=0, description=None):
req_body = {
'function_id': function_id,
'function_version': function_version,
'name': name
}
if description is not None:
req_body['description'] = description
resp, body = self.post_json('/aliases', req_body)
return resp, body
def delete_function_alias(self, alias_name, ignore_notfound=False):
try:
resp, _ = self.delete('/v1/aliases/%s' % alias_name)
return resp
except exceptions.NotFound:
if ignore_notfound:
pass
else:
raise
def get_function_alias(self, alias_name):
resp, body = self.get('/v1/aliases/%s' % alias_name)
return resp, json.loads(body)
def update_function_alias(self, alias_name, function_id=None,
function_version=None, description=None):
req_body = {}
if function_id is not None:
req_body['function_id'] = function_id
if function_version is not None:
req_body['function_version'] = function_version
if description is not None:
req_body['description'] = description
resp, body = self.put_json('/v1/aliases/%s' % alias_name, req_body)
return resp, body

+ 26
- 0
qinling_tempest_plugin/tests/api/test_executions.py View File

@ -121,6 +121,32 @@ class ExecutionsTest(base.BaseQinlingTest):
self.assertEqual(200, resp.status)
self.assertNotIn('Hello, World', body)
@decorators.idempotent_id('dbf4bd84-bde3-4d1d-8dec-93aaf18b4b5f')
def test_create_with_function_alias(self):
function_id = self.create_function()
alias_name = self.create_function_alias(function_id)
execution_id = self.create_execution(alias_name=alias_name)
resp, body = self.client.get_execution_log(execution_id)
self.assertEqual(200, resp.status)
self.assertIn('Hello, World', body)
version_1 = self.create_function_version(function_id)
alias_name_1 = self.create_function_alias(function_id, version_1)
execution_id = self.create_execution(alias_name=alias_name_1)
resp, body = self.client.get_execution_log(execution_id)
self.assertEqual(200, resp.status)
self.assertIn('Hello, World', body)
self.update_function_package(function_id,
"python/test_python_sleep.py")
version_2 = self.create_function_version(function_id)
alias_name_2 = self.create_function_alias(function_id, version_2)
execution_id = self.create_execution(alias_name=alias_name_2)
resp, body = self.client.get_execution_log(execution_id)
self.assertEqual(200, resp.status)
self.assertNotIn('Hello, World', body)
@decorators.idempotent_id('8096cc52-64d2-4660-a657-9ac0bdd743ae')
def test_execution_async(self):
function_id = self.create_function()


+ 26
- 3
qinling_tempest_plugin/tests/base.py View File

@ -213,9 +213,15 @@ class BaseQinlingTest(test.BaseTestCase):
return version
def create_execution(self, function_id, version=0, input=None):
resp, body = self.client.create_execution(function_id, version=version,
input=input)
def create_execution(self, function_id=None, alias_name=None, version=0,
input=None):
if alias_name:
resp, body = self.client.create_execution(alias_name=alias_name,
input=input)
else:
resp, body = self.client.create_execution(function_id,
version=version,
input=input)
self.assertEqual(201, resp.status)
@ -226,3 +232,20 @@ class BaseQinlingTest(test.BaseTestCase):
self.assertEqual('success', body['status'])
return execution_id
def create_function_alias(self, function_id=None, function_version=0):
name = data_utils.rand_name(name="alias", prefix=self.name_prefix)
if not function_id:
function_id = self.create_function()
resp, body = self.client.create_function_alias(name,
function_id,
function_version)
self.assertEqual(201, resp.status)
alias_name = body['name']
self.addCleanup(self.client.delete_function_alias, alias_name,
ignore_notfound=True)
return alias_name

Loading…
Cancel
Save