remove support for legacy v2 generator extensions

This removes the support for legacy v2 extensions which were allowed
to be generators to have a pre / post processing phase. The modern
compute api stack never supported this construct.

Because of the remove of the 'pre' phase, the 'post_process' phase is
just renamed to 'process'.

All pre_process and generator tests are removed.

Change-Id: Ia34c1f814fb938915d74c6845dfa5135cba29d0a
This commit is contained in:
Sean Dague 2016-06-21 14:55:57 -04:00
parent 41616f9d55
commit f50d48a2f6
2 changed files with 28 additions and 243 deletions

View File

@ -15,7 +15,6 @@
# under the License.
import functools
import inspect
import math
import time
@ -545,90 +544,25 @@ class Resource(wsgi.Application):
def deserialize(self, body):
return JSONDeserializer().deserialize(body)
# NOTE(sdague): I didn't start the fire, however here is what all
# of this is about.
#
# In the legacy v2 code stack, extensions could extend actions
# with a generator that let 1 method be split into a top and
# bottom half. The top half gets executed before the main
# processing of the request (so effectively gets to modify the
# request before it gets to the main method).
#
# Returning a response triggers a shortcut to fail out. The
# response will nearly always be a failure condition, as it ends
# up skipping further processing one level up from here.
#
# This then passes on the list of extensions, in reverse order,
# on. post_process will run through all those, again with same
# basic logic.
#
# In tree this is only used in the legacy v2 stack, and only in
# the DiskConfig and SchedulerHints from what I can see.
#
# pre_process_extensions can be removed when the legacyv2 code
# goes away. post_process_extensions can be massively simplified
# at that point.
def pre_process_extensions(self, extensions, request, action_args):
# List of callables for post-processing extensions
post = []
for ext in extensions:
if inspect.isgeneratorfunction(ext):
response = None
# If it's a generator function, the part before the
# yield is the preprocessing stage
try:
with ResourceExceptionHandler():
gen = ext(req=request, **action_args)
response = next(gen)
except Fault as ex:
response = ex
# We had a response...
if response:
return response, []
# No response, queue up generator for post-processing
post.append(gen)
else:
# Regular functions only perform post-processing
post.append(ext)
# None is response, it means we keep going. We reverse the
# extension list for post-processing.
return None, reversed(post)
def post_process_extensions(self, extensions, resp_obj, request,
action_args):
def process_extensions(self, extensions, resp_obj, request,
action_args):
for ext in extensions:
response = None
if inspect.isgenerator(ext):
# If it's a generator, run the second half of
# processing
try:
with ResourceExceptionHandler():
response = ext.send(resp_obj)
except StopIteration:
# Normal exit of generator
continue
except Fault as ex:
response = ex
else:
# Regular functions get post-processing...
try:
with ResourceExceptionHandler():
response = ext(req=request, resp_obj=resp_obj,
**action_args)
except exception.VersionNotFoundForAPIMethod:
# If an attached extension (@wsgi.extends) for the
# method has no version match its not an error. We
# just don't run the extends code
continue
except Fault as ex:
response = ex
# Regular functions get post-processing...
try:
with ResourceExceptionHandler():
response = ext(req=request, resp_obj=resp_obj,
**action_args)
except exception.VersionNotFoundForAPIMethod:
# If an attached extension (@wsgi.extends) for the
# method has no version match its not an error. We
# just don't run the extends code
continue
except Fault as ex:
response = ex
# We had a response...
# We had a response return it, to exit early. This is
# actually a failure mode. None is success.
if response:
return response
@ -727,16 +661,12 @@ class Resource(wsgi.Application):
'context_project_id': context.project_id}
return Fault(webob.exc.HTTPBadRequest(explanation=msg))
# Run pre-processing extensions
response, post = self.pre_process_extensions(extensions,
request, action_args)
if not response:
try:
with ResourceExceptionHandler():
action_result = self.dispatch(meth, request, action_args)
except Fault as ex:
response = ex
response = None
try:
with ResourceExceptionHandler():
action_result = self.dispatch(meth, request, action_args)
except Fault as ex:
response = ex
if not response:
# No exceptions; convert action_result into a
@ -754,8 +684,8 @@ class Resource(wsgi.Application):
# Do a preserialize to set up the response object
if hasattr(meth, 'wsgi_code'):
resp_obj._default_code = meth.wsgi_code
# Process post-processing extensions
response = self.post_process_extensions(post, resp_obj,
# Process extensions
response = self.process_extensions(extensions, resp_obj,
request, action_args)
if resp_obj and not response:

View File

@ -10,8 +10,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import inspect
import mock
import six
import testscenarios
@ -785,7 +783,7 @@ class ResourceTest(MicroversionedTest):
self.assertEqual(method, extended._delete)
self.assertEqual(extensions, [])
def test_pre_process_extensions_regular(self):
def test_process_extensions_regular(self):
class Controller(object):
def index(self, req, pants=None):
return pants
@ -803,96 +801,12 @@ class ResourceTest(MicroversionedTest):
called.append(2)
return None
extensions = [extension1, extension2]
response, post = resource.pre_process_extensions(extensions, None, {})
self.assertEqual(called, [])
self.assertIsNone(response)
self.assertEqual(list(post), [extension2, extension1])
def test_pre_process_extensions_generator(self):
class Controller(object):
def index(self, req, pants=None):
return pants
controller = Controller()
resource = wsgi.Resource(controller)
called = []
def extension1(req):
called.append('pre1')
yield
called.append('post1')
def extension2(req):
called.append('pre2')
yield
called.append('post2')
extensions = [extension1, extension2]
response, post = resource.pre_process_extensions(extensions, None, {})
post = list(post)
self.assertEqual(called, ['pre1', 'pre2'])
self.assertIsNone(response)
self.assertEqual(len(post), 2)
self.assertTrue(inspect.isgenerator(post[0]))
self.assertTrue(inspect.isgenerator(post[1]))
for gen in post:
try:
gen.send(None)
except StopIteration:
continue
self.assertEqual(called, ['pre1', 'pre2', 'post2', 'post1'])
def test_pre_process_extensions_generator_response(self):
class Controller(object):
def index(self, req, pants=None):
return pants
controller = Controller()
resource = wsgi.Resource(controller)
called = []
def extension1(req):
called.append('pre1')
yield 'foo'
def extension2(req):
called.append('pre2')
extensions = [extension1, extension2]
response, post = resource.pre_process_extensions(extensions, None, {})
self.assertEqual(called, ['pre1'])
self.assertEqual(response, 'foo')
self.assertEqual(post, [])
def test_post_process_extensions_regular(self):
class Controller(object):
def index(self, req, pants=None):
return pants
controller = Controller()
resource = wsgi.Resource(controller)
called = []
def extension1(req, resp_obj):
called.append(1)
return None
def extension2(req, resp_obj):
called.append(2)
return None
response = resource.post_process_extensions([extension2, extension1],
response = resource.process_extensions([extension2, extension1],
None, None, {})
self.assertEqual(called, [2, 1])
self.assertIsNone(response)
def test_post_process_extensions_regular_response(self):
def test_process_extensions_regular_response(self):
class Controller(object):
def index(self, req, pants=None):
return pants
@ -910,70 +824,11 @@ class ResourceTest(MicroversionedTest):
called.append(2)
return 'foo'
response = resource.post_process_extensions([extension2, extension1],
response = resource.process_extensions([extension2, extension1],
None, None, {})
self.assertEqual(called, [2])
self.assertEqual(response, 'foo')
def test_post_process_extensions_generator(self):
class Controller(object):
def index(self, req, pants=None):
return pants
controller = Controller()
resource = wsgi.Resource(controller)
called = []
def extension1(req):
yield
called.append(1)
def extension2(req):
yield
called.append(2)
ext1 = extension1(None)
next(ext1)
ext2 = extension2(None)
next(ext2)
response = resource.post_process_extensions([ext2, ext1],
None, None, {})
self.assertEqual(called, [2, 1])
self.assertIsNone(response)
def test_post_process_extensions_generator_response(self):
class Controller(object):
def index(self, req, pants=None):
return pants
controller = Controller()
resource = wsgi.Resource(controller)
called = []
def extension1(req):
yield
called.append(1)
def extension2(req):
yield
called.append(2)
yield 'foo'
ext1 = extension1(None)
next(ext1)
ext2 = extension2(None)
next(ext2)
response = resource.post_process_extensions([ext2, ext1],
None, None, {})
self.assertEqual(called, [2])
self.assertEqual(response, 'foo')
def test_resource_exception_handler_type_error(self):
# A TypeError should be translated to a Fault/HTTP 400.
def foo(a,):