Functional test for function version detach/get
- Only admin user can detach function version. - Admin user has read access to user's function version. Change-Id: I345a2aa17d7131984038f99cdd6e6f246bec1d24 Story: 2001829 Task: 14456
This commit is contained in:
parent
0e8d91c30b
commit
19cd85e818
|
@ -100,7 +100,13 @@ class FunctionsController(rest.RestController):
|
|||
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose()
|
||||
@pecan.expose('json')
|
||||
def get(self, id):
|
||||
"""Get function information or download function package.
|
||||
|
||||
This method can support HTTP request using either
|
||||
'Accept:application/json' or no 'Accept' header.
|
||||
"""
|
||||
LOG.info("Get function %s.", id)
|
||||
|
||||
download = strutils.bool_from_string(
|
||||
|
|
|
@ -65,7 +65,7 @@ class FunctionVersionsController(rest.RestController):
|
|||
|
||||
with db_api.transaction():
|
||||
# Get latest function package md5 and version number
|
||||
func_db = db_api.get_function(function_id)
|
||||
func_db = db_api.get_function(function_id, insecure=False)
|
||||
if func_db.code['source'] != constants.PACKAGE_FUNCTION:
|
||||
raise exc.NotAllowedException(
|
||||
"Function versioning only allowed for %s type "
|
||||
|
@ -140,8 +140,12 @@ class FunctionVersionsController(rest.RestController):
|
|||
@rest_utils.wrap_wsme_controller_exception
|
||||
@wsme_pecan.wsexpose(resources.FunctionVersions, types.uuid)
|
||||
def get_all(self, function_id):
|
||||
"""Get all the versions of the given function.
|
||||
|
||||
Admin user can get all versions for the normal user's function.
|
||||
"""
|
||||
acl.enforce('function_version:get_all', context.get_ctx())
|
||||
LOG.info("Getting function versions for function %s.", function_id)
|
||||
LOG.info("Getting versions for function %s.", function_id)
|
||||
|
||||
# Getting function and versions needs to happen in a db transaction
|
||||
with db_api.transaction():
|
||||
|
@ -155,7 +159,13 @@ class FunctionVersionsController(rest.RestController):
|
|||
|
||||
@rest_utils.wrap_pecan_controller_exception
|
||||
@pecan.expose()
|
||||
@pecan.expose('json')
|
||||
def get(self, function_id, version):
|
||||
"""Get function version or download function version package.
|
||||
|
||||
This method can support HTTP request using either
|
||||
'Accept:application/json' or no 'Accept' header.
|
||||
"""
|
||||
ctx = context.get_ctx()
|
||||
acl.enforce('function_version:get', ctx)
|
||||
|
||||
|
@ -175,7 +185,7 @@ class FunctionVersionsController(rest.RestController):
|
|||
LOG.info("Downloading version %s for function %s.", version,
|
||||
function_id)
|
||||
|
||||
f = self.storage_provider.retrieve(ctx.projectid, function_id,
|
||||
f = self.storage_provider.retrieve(version_db.project_id, function_id,
|
||||
None, version=version)
|
||||
|
||||
if isinstance(f, collections.Iterable):
|
||||
|
|
|
@ -516,10 +516,18 @@ def increase_function_version(function_id, old_version, session=None,
|
|||
return version
|
||||
|
||||
|
||||
@db_base.insecure_aware()
|
||||
@db_base.session_aware()
|
||||
def get_function_version(function_id, version, session=None):
|
||||
version_db = _secure_query(models.FunctionVersion).filter_by(
|
||||
function_id=function_id, version_number=version).first()
|
||||
def get_function_version(function_id, version, session=None, insecure=None):
|
||||
if insecure:
|
||||
query = db_base.model_query(models.FunctionVersion)
|
||||
else:
|
||||
query = _secure_query(models.FunctionVersion)
|
||||
|
||||
version_db = query.filter_by(
|
||||
function_id=function_id, version_number=version
|
||||
).first()
|
||||
|
||||
if not version_db:
|
||||
raise exc.DBEntityNotFoundError(
|
||||
"FunctionVersion not found [function_id=%s, version_number=%s]" %
|
||||
|
|
|
@ -106,14 +106,25 @@ class QinlingClient(client_base.QinlingClientBase):
|
|||
|
||||
return resp, json.loads(resp.text)
|
||||
|
||||
def get_function(self, function_id):
|
||||
resp, body = self.get(
|
||||
'/v1/functions/{id}'.format(id=function_id),
|
||||
)
|
||||
|
||||
return resp, json.loads(body)
|
||||
|
||||
def download_function(self, function_id):
|
||||
return self.get('/v1/functions/%s?download=true' % function_id,
|
||||
headers={})
|
||||
|
||||
def detach_function(self, function_id):
|
||||
return self.post('/v1/functions/%s/detach' % function_id,
|
||||
None,
|
||||
headers={})
|
||||
def detach_function(self, function_id, version=0):
|
||||
if version == 0:
|
||||
url = '/v1/functions/%s/detach' % function_id
|
||||
else:
|
||||
url = '/v1/functions/%s/versions/%s/detach' % \
|
||||
(function_id, version)
|
||||
|
||||
return self.post(url, None, headers={})
|
||||
|
||||
def create_execution(self, function_id, input=None, sync=True, version=0):
|
||||
req_body = {
|
||||
|
@ -130,8 +141,16 @@ class QinlingClient(client_base.QinlingClientBase):
|
|||
return self.get('/v1/executions/%s/log' % execution_id,
|
||||
headers={'Accept': 'text/plain'})
|
||||
|
||||
def get_function_workers(self, function_id):
|
||||
return self.get_resources('functions/%s/workers' % function_id)
|
||||
def get_function_workers(self, function_id, version=0):
|
||||
q_params = None
|
||||
if version > 0:
|
||||
q_params = "/?function_version=%s" % version
|
||||
|
||||
url = 'functions/%s/workers' % function_id
|
||||
if q_params:
|
||||
url += q_params
|
||||
|
||||
return self.get_resources(url)
|
||||
|
||||
def create_webhook(self, function_id, version=0):
|
||||
req_body = {"function_id": function_id, "function_version": version}
|
||||
|
@ -174,3 +193,17 @@ class QinlingClient(client_base.QinlingClientBase):
|
|||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
def get_function_version(self, function_id, version):
|
||||
resp, body = self.get(
|
||||
'/v1/functions/%s/versions/%s' % (function_id, version),
|
||||
)
|
||||
|
||||
return resp, json.loads(body)
|
||||
|
||||
def get_function_versions(self, function_id):
|
||||
resp, body = self.get(
|
||||
'/v1/functions/%s/versions' % (function_id),
|
||||
)
|
||||
|
||||
return resp, json.loads(body)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# limitations under the License.
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions
|
||||
import tenacity
|
||||
|
||||
from qinling_tempest_plugin.tests import base
|
||||
|
||||
|
@ -70,3 +71,81 @@ class FunctionVersionsTest(base.BaseQinlingTest):
|
|||
numbers = [v['version_number'] for v in body['function_versions']]
|
||||
self.assertIn(version_1, numbers)
|
||||
self.assertIn(version_2, numbers)
|
||||
|
||||
@decorators.idempotent_id('3f735ed4-64b0-4ec3-8bf2-507e38dcea19')
|
||||
def test_create_admin_not_allowed(self):
|
||||
"""test_create_admin_not_allowed
|
||||
|
||||
Even admin user can not create function version for normal user's
|
||||
function.
|
||||
"""
|
||||
function_id = self.create_function()
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.NotFound,
|
||||
self.admin_client.create_function_version,
|
||||
function_id
|
||||
)
|
||||
|
||||
@decorators.idempotent_id('43c06f41-d116-43a7-a61c-115f7591b22e')
|
||||
def test_get_by_admin(self):
|
||||
"""Admin user can get normal user's function version."""
|
||||
function_id = self.create_function()
|
||||
version = self.create_function_version(function_id)
|
||||
|
||||
resp, body = self.admin_client.get_function_version(function_id,
|
||||
version)
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(version, body.get("version_number"))
|
||||
|
||||
@decorators.idempotent_id('e6b865d8-ffa8-4cfc-8afb-820c64f9b2af')
|
||||
def test_get_all_by_admin(self):
|
||||
"""Admin user can list normal user's function version."""
|
||||
function_id = self.create_function()
|
||||
version = self.create_function_version(function_id)
|
||||
|
||||
resp, body = self.admin_client.get_function_versions(function_id)
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertIn(
|
||||
version,
|
||||
[v['version_number'] for v in body['function_versions']]
|
||||
)
|
||||
|
||||
@decorators.idempotent_id('7898f89f-a490-42a3-8cf7-63cbd9543a06')
|
||||
def test_detach(self):
|
||||
"""Admin only operation."""
|
||||
function_id = self.create_function()
|
||||
version = self.create_function_version(function_id)
|
||||
|
||||
# Create execution to allocate worker
|
||||
resp, _ = self.client.create_execution(
|
||||
function_id, input='{"name": "Qinling"}', version=version
|
||||
)
|
||||
self.assertEqual(201, resp.status)
|
||||
|
||||
resp, body = self.admin_client.get_function_workers(function_id,
|
||||
version=version)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(1, len(body['workers']))
|
||||
|
||||
# Detach function version from workers
|
||||
resp, _ = self.admin_client.detach_function(function_id,
|
||||
version=version)
|
||||
self.assertEqual(202, resp.status)
|
||||
|
||||
def _assert_workers():
|
||||
resp, body = self.admin_client.get_function_workers(
|
||||
function_id,
|
||||
version=version
|
||||
)
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(0, len(body['workers']))
|
||||
|
||||
r = tenacity.Retrying(
|
||||
wait=tenacity.wait_fixed(1),
|
||||
stop=tenacity.stop_after_attempt(5),
|
||||
retry=tenacity.retry_if_exception_type(AssertionError)
|
||||
)
|
||||
r.call(_assert_workers)
|
||||
|
|
|
@ -68,12 +68,27 @@ class FunctionsTest(base.BaseQinlingTest):
|
|||
|
||||
self.assertEqual(400, resp.status_code)
|
||||
|
||||
@decorators.idempotent_id('051f3106-df01-4fcd-a0a3-c81c99653163')
|
||||
def test_get_all_admin(self):
|
||||
# Create function by normal user
|
||||
@decorators.idempotent_id('f8dde7fc-fbcc-495c-9b39-70666b7d3f64')
|
||||
def test_get_by_admin(self):
|
||||
"""test_get_by_admin
|
||||
|
||||
Admin user can get the function by directly specifying the function id.
|
||||
"""
|
||||
function_id = self.create_function(self.python_zip_file)
|
||||
|
||||
resp, body = self.admin_client.get_function(function_id)
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertEqual(function_id, body['id'])
|
||||
|
||||
@decorators.idempotent_id('051f3106-df01-4fcd-a0a3-c81c99653163')
|
||||
def test_get_all_admin(self):
|
||||
"""test_get_all_admin
|
||||
|
||||
Admin user needs to specify filters to get all the functions.
|
||||
"""
|
||||
function_id = self.create_function(self.python_zip_file)
|
||||
|
||||
# Get functions by admin
|
||||
resp, body = self.admin_client.get_resources('functions')
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
|
@ -82,7 +97,6 @@ class FunctionsTest(base.BaseQinlingTest):
|
|||
[function['id'] for function in body['functions']]
|
||||
)
|
||||
|
||||
# Get other projects functions by admin
|
||||
resp, body = self.admin_client.get_resources(
|
||||
'functions?all_projects=true'
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue