# Copyright 2016, Google Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Defines a number of module-scope gRPC scenarios to test clean exit.""" import argparse import threading import time import grpc from tests.unit.framework.common import test_constants WAIT_TIME = 1000 REQUEST = b'request' UNSTARTED_SERVER = 'unstarted_server' RUNNING_SERVER = 'running_server' POLL_CONNECTIVITY_NO_SERVER = 'poll_connectivity_no_server' POLL_CONNECTIVITY = 'poll_connectivity' IN_FLIGHT_UNARY_UNARY_CALL = 'in_flight_unary_unary_call' IN_FLIGHT_UNARY_STREAM_CALL = 'in_flight_unary_stream_call' IN_FLIGHT_STREAM_UNARY_CALL = 'in_flight_stream_unary_call' IN_FLIGHT_STREAM_STREAM_CALL = 'in_flight_stream_stream_call' IN_FLIGHT_PARTIAL_UNARY_STREAM_CALL = 'in_flight_partial_unary_stream_call' IN_FLIGHT_PARTIAL_STREAM_UNARY_CALL = 'in_flight_partial_stream_unary_call' IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL = 'in_flight_partial_stream_stream_call' UNARY_UNARY = b'/test/UnaryUnary' UNARY_STREAM = b'/test/UnaryStream' STREAM_UNARY = b'/test/StreamUnary' STREAM_STREAM = b'/test/StreamStream' PARTIAL_UNARY_STREAM = b'/test/PartialUnaryStream' PARTIAL_STREAM_UNARY = b'/test/PartialStreamUnary' PARTIAL_STREAM_STREAM = b'/test/PartialStreamStream' TEST_TO_METHOD = { IN_FLIGHT_UNARY_UNARY_CALL: UNARY_UNARY, IN_FLIGHT_UNARY_STREAM_CALL: UNARY_STREAM, IN_FLIGHT_STREAM_UNARY_CALL: STREAM_UNARY, IN_FLIGHT_STREAM_STREAM_CALL: STREAM_STREAM, IN_FLIGHT_PARTIAL_UNARY_STREAM_CALL: PARTIAL_UNARY_STREAM, IN_FLIGHT_PARTIAL_STREAM_UNARY_CALL: PARTIAL_STREAM_UNARY, IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL: PARTIAL_STREAM_STREAM, } def hang_unary_unary(request, servicer_context): time.sleep(WAIT_TIME) def hang_unary_stream(request, servicer_context): time.sleep(WAIT_TIME) def hang_partial_unary_stream(request, servicer_context): for _ in range(test_constants.STREAM_LENGTH // 2): yield request time.sleep(WAIT_TIME) def hang_stream_unary(request_iterator, servicer_context): time.sleep(WAIT_TIME) def hang_partial_stream_unary(request_iterator, servicer_context): for _ in range(test_constants.STREAM_LENGTH // 2): next(request_iterator) time.sleep(WAIT_TIME) def hang_stream_stream(request_iterator, servicer_context): time.sleep(WAIT_TIME) def hang_partial_stream_stream(request_iterator, servicer_context): for _ in range(test_constants.STREAM_LENGTH // 2): yield next(request_iterator) time.sleep(WAIT_TIME) class MethodHandler(grpc.RpcMethodHandler): def __init__(self, request_streaming, response_streaming, partial_hang): self.request_streaming = request_streaming self.response_streaming = response_streaming self.request_deserializer = None self.response_serializer = None self.unary_unary = None self.unary_stream = None self.stream_unary = None self.stream_stream = None if self.request_streaming and self.response_streaming: if partial_hang: self.stream_stream = hang_partial_stream_stream else: self.stream_stream = hang_stream_stream elif self.request_streaming: if partial_hang: self.stream_unary = hang_partial_stream_unary else: self.stream_unary = hang_stream_unary elif self.response_streaming: if partial_hang: self.unary_stream = hang_partial_unary_stream else: self.unary_stream = hang_unary_stream else: self.unary_unary = hang_unary_unary class GenericHandler(grpc.GenericRpcHandler): def service(self, handler_call_details): if handler_call_details.method == UNARY_UNARY: return MethodHandler(False, False, False) elif handler_call_details.method == UNARY_STREAM: return MethodHandler(False, True, False) elif handler_call_details.method == STREAM_UNARY: return MethodHandler(True, False, False) elif handler_call_details.method == STREAM_STREAM: return MethodHandler(True, True, False) elif handler_call_details.method == PARTIAL_UNARY_STREAM: return MethodHandler(False, True, True) elif handler_call_details.method == PARTIAL_STREAM_UNARY: return MethodHandler(True, False, True) elif handler_call_details.method == PARTIAL_STREAM_STREAM: return MethodHandler(True, True, True) else: return None # Traditional executors will not exit until all their # current jobs complete. Because we submit jobs that will # never finish, we don't want to block exit on these jobs. class DaemonPool(object): def submit(self, fn, *args, **kwargs): thread = threading.Thread(target=fn, args=args, kwargs=kwargs) thread.daemon = True thread.start() def shutdown(self, wait=True): pass def infinite_request_iterator(): while True: yield REQUEST if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('scenario', type=str) parser.add_argument( '--wait_for_interrupt', dest='wait_for_interrupt', action='store_true') args = parser.parse_args() if args.scenario == UNSTARTED_SERVER: server = grpc.server((), DaemonPool()) if args.wait_for_interrupt: time.sleep(WAIT_TIME) elif args.scenario == RUNNING_SERVER: server = grpc.server((), DaemonPool()) port = server.add_insecure_port('[::]:0') server.start() if args.wait_for_interrupt: time.sleep(WAIT_TIME) elif args.scenario == POLL_CONNECTIVITY_NO_SERVER: channel = grpc.insecure_channel('localhost:12345') def connectivity_callback(connectivity): pass channel.subscribe(connectivity_callback, try_to_connect=True) if args.wait_for_interrupt: time.sleep(WAIT_TIME) elif args.scenario == POLL_CONNECTIVITY: server = grpc.server((), DaemonPool()) port = server.add_insecure_port('[::]:0') server.start() channel = grpc.insecure_channel('localhost:%d' % port) def connectivity_callback(connectivity): pass channel.subscribe(connectivity_callback, try_to_connect=True) if args.wait_for_interrupt: time.sleep(WAIT_TIME) else: handler = GenericHandler() server = grpc.server((), DaemonPool()) port = server.add_insecure_port('[::]:0') server.add_generic_rpc_handlers((handler,)) server.start() channel = grpc.insecure_channel('localhost:%d' % port) method = TEST_TO_METHOD[args.scenario] if args.scenario == IN_FLIGHT_UNARY_UNARY_CALL: multi_callable = channel.unary_unary(method) future = multi_callable.future(REQUEST) result, call = multi_callable.with_call(REQUEST) elif (args.scenario == IN_FLIGHT_UNARY_STREAM_CALL or args.scenario == IN_FLIGHT_PARTIAL_UNARY_STREAM_CALL): multi_callable = channel.unary_stream(method) response_iterator = multi_callable(REQUEST) for response in response_iterator: pass elif (args.scenario == IN_FLIGHT_STREAM_UNARY_CALL or args.scenario == IN_FLIGHT_PARTIAL_STREAM_UNARY_CALL): multi_callable = channel.stream_unary(method) future = multi_callable.future(infinite_request_iterator()) result, call = multi_callable.with_call( [REQUEST] * test_constants.STREAM_LENGTH) elif (args.scenario == IN_FLIGHT_STREAM_STREAM_CALL or args.scenario == IN_FLIGHT_PARTIAL_STREAM_STREAM_CALL): multi_callable = channel.stream_stream(method) response_iterator = multi_callable(infinite_request_iterator()) for response in response_iterator: pass