Esempio n. 1
0
def test_keep_alive_thread_fail_to_start():
    class MockThread:
        def __init__(self):
            pass

        def start(self):
            raise RuntimeError("Fail to start")

        def join(self):
            raise RuntimeError("Fail to join")

    def hack_open(ins):
        ins._keep_alive_thread = MockThread()
        ins._keep_alive_thread.start()

    sas_auth = authentication.SASTokenAuth.from_shared_access_key(
        "sb://fake/fake", "fake", "fake")
    target = "amqps://{}/{}".format("fake", "fake")
    client = AMQPClient(target, auth=sas_auth, keep_alive_interval=10)
    client.open = types.MethodType(hack_open, client)
    try:
        client.open()
    except Exception as exc:
        assert type(exc) == RuntimeError
        client.close()
        assert not client._keep_alive_thread
 def _management_request(self, mgmt_msg, op_type):
     retried_times = 0
     last_exception = None
     while retried_times <= self._config.max_retries:
         mgmt_auth = self._create_auth()
         mgmt_client = AMQPClient(self._mgmt_target)
         try:
             conn = self._conn_manager.get_connection(
                 self._address.hostname, mgmt_auth)  #pylint:disable=assignment-from-none
             mgmt_client.open(connection=conn)
             response = mgmt_client.mgmt_request(
                 mgmt_msg,
                 constants.READ_OPERATION,
                 op_type=op_type,
                 status_code_field=b'status-code',
                 description_fields=b'status-description')
             return response
         except Exception as exception:  # pylint: disable=broad-except
             last_exception = _handle_exception(exception, self)
             self._backoff(retried_times=retried_times,
                           last_exception=last_exception)
             retried_times += 1
             if retried_times > self._config.max_retries:
                 _LOGGER.info("%r returns an exception %r",
                              self._container_id, last_exception)
                 raise last_exception
         finally:
             mgmt_client.close()
 def _management_request(self, mgmt_msg, op_type):
     # type: (Message, bytes) -> Any
     retried_times = 0
     last_exception = None
     while retried_times <= self._config.max_retries:
         mgmt_auth = self._create_auth()
         mgmt_client = AMQPClient(self._mgmt_target)
         try:
             conn = self._conn_manager.get_connection(
                 self._address.hostname, mgmt_auth)  # pylint:disable=assignment-from-none
             mgmt_client.open(connection=conn)
             response = mgmt_client.mgmt_request(
                 mgmt_msg,
                 constants.READ_OPERATION,
                 op_type=op_type,
                 status_code_field=b"status-code",
                 description_fields=b"status-description",
             )
             status_code = response.application_properties[b"status-code"]
             if status_code < 400:
                 return response
             raise errors.AuthenticationException(
                 "Management request error. Status code: {}".format(
                     status_code))
         except Exception as exception:  # pylint: disable=broad-except
             last_exception = _handle_exception(exception, self)
             self._backoff(retried_times=retried_times,
                           last_exception=last_exception)
             retried_times += 1
             if retried_times > self._config.max_retries:
                 _LOGGER.info("%r returns an exception %r",
                              self._container_id, last_exception)
                 raise last_exception
         finally:
             mgmt_client.close()
 def _management_request(self, mgmt_msg, op_type):
     # type: (Message, bytes) -> Any
     # pylint:disable=assignment-from-none
     retried_times = 0
     last_exception = None
     while retried_times <= self._config.max_retries:
         mgmt_auth = self._create_auth()
         mgmt_client = AMQPClient(self._mgmt_target,
                                  auth=mgmt_auth,
                                  debug=self._config.network_tracing)
         try:
             conn = self._conn_manager.get_connection(  # pylint:disable=assignment-from-none
                 self._address.hostname, mgmt_auth)
             mgmt_client.open(connection=conn)
             mgmt_msg.application_properties[
                 "security_token"] = mgmt_auth.token
             response = mgmt_client.mgmt_request(
                 mgmt_msg,
                 constants.READ_OPERATION,
                 op_type=op_type,
                 status_code_field=MGMT_STATUS_CODE,
                 description_fields=MGMT_STATUS_DESC,
             )
             status_code = int(
                 response.application_properties[MGMT_STATUS_CODE])
             description = response.application_properties.get(
                 MGMT_STATUS_DESC)  # type: Optional[Union[str, bytes]]
             if description and isinstance(description, six.binary_type):
                 description = description.decode("utf-8")
             if status_code < 400:
                 return response
             if status_code in [401]:
                 raise errors.AuthenticationException(
                     "Management authentication failed. Status code: {}, Description: {!r}"
                     .format(status_code, description))
             if status_code in [404]:
                 raise ConnectError(
                     "Management connection failed. Status code: {}, Description: {!r}"
                     .format(status_code, description))
             raise errors.AMQPConnectionError(
                 "Management request error. Status code: {}, Description: {!r}"
                 .format(status_code, description))
         except Exception as exception:  # pylint: disable=broad-except
             last_exception = _handle_exception(exception, self)
             self._backoff(retried_times=retried_times,
                           last_exception=last_exception)
             retried_times += 1
             if retried_times > self._config.max_retries:
                 _LOGGER.info("%r returns an exception %r",
                              self._container_id, last_exception)
                 raise last_exception
         finally:
             mgmt_client.close()
class BaseHandler(object):  # pylint: disable=too-many-instance-attributes
    def __init__(self,
                 endpoint,
                 auth_config,
                 connection=None,
                 encoding='UTF-8',
                 debug=False,
                 **kwargs):
        self.running = False
        self.error = None
        self.endpoint = endpoint
        self.entity = urlparse(endpoint).path.strip('/')
        self.mgmt_target = self.entity + "/$management"
        self.debug = debug
        self.encoding = encoding
        self.auth_config = auth_config
        self.connection = connection
        self.auto_reconnect = kwargs.pop('auto_reconnect', True)
        self.properties = create_properties()
        self.error_policy = kwargs.pop('error_policy', None)
        self.handler_kwargs = kwargs
        if not self.error_policy:
            max_retries = kwargs.pop('max_message_retries', 3)
            is_session = hasattr(self, 'session_id')
            self.error_policy = _ServiceBusErrorPolicy(max_retries=max_retries,
                                                       is_session=is_session)
        self._handler = None
        self._build_handler()

    def __enter__(self):
        """Open the handler in a context manager."""
        self.open()
        return self

    def __exit__(self, *args):
        """Close the handler when exiting a context manager."""
        self.close()

    def _build_handler(self):
        auth = None if self.connection else authentication.SASTokenAuth.from_shared_access_key(
            **self.auth_config)
        self._handler = AMQPClient(self.endpoint,
                                   auth=auth,
                                   debug=self.debug,
                                   properties=self.properties,
                                   error_policy=self.error_policy,
                                   encoding=self.encoding,
                                   **self.handler_kwargs)

    def _mgmt_request_response(self, operation, message, callback, **kwargs):
        if not self.running:
            raise InvalidHandlerState("Client connection is closed.")

        try:
            application_properties = {
                ASSOCIATEDLINKPROPERTYNAME: self._handler.message_handler.name
            }
        except AttributeError:
            application_properties = {}

        mgmt_msg = Message(body=message,
                           properties=MessageProperties(
                               reply_to=self.mgmt_target,
                               encoding=self.encoding,
                               **kwargs),
                           application_properties=application_properties)
        try:
            return self._handler.mgmt_request(mgmt_msg,
                                              operation,
                                              op_type=b"entity-mgmt",
                                              node=self.mgmt_target.encode(
                                                  self.encoding),
                                              timeout=5000,
                                              callback=callback)
        except Exception as exp:  # pylint: disable=broad-except
            raise ServiceBusError("Management request failed: {}".format(exp),
                                  exp)

    def _handle_exception(self, exception):
        if isinstance(exception, (errors.LinkDetach, errors.ConnectionClose)):
            if exception.action and exception.action.retry and self.auto_reconnect:
                _log.info("Handler detached. Attempting reconnect.")
                self.reconnect()
            elif exception.condition == constants.ErrorCodes.UnauthorizedAccess:
                _log.info("Handler detached. Shutting down.")
                error = ServiceBusAuthorizationError(str(exception), exception)
                self.close(exception=error)
                raise error
            else:
                _log.info("Handler detached. Shutting down.")
                error = ServiceBusConnectionError(str(exception), exception)
                self.close(exception=error)
                raise error
        elif isinstance(exception, errors.MessageHandlerError):
            if self.auto_reconnect:
                _log.info("Handler error. Attempting reconnect.")
                self.reconnect()
            else:
                _log.info("Handler error. Shutting down.")
                error = ServiceBusConnectionError(str(exception), exception)
                self.close(exception=error)
                raise error
        elif isinstance(exception, errors.AMQPConnectionError):
            message = "Failed to open handler: {}".format(exception)
            raise ServiceBusConnectionError(message, exception)
        else:
            _log.info("Unexpected error occurred (%r). Shutting down.",
                      exception)
            error = ServiceBusError("Handler failed: {}".format(exception))
            self.close(exception=error)
            raise error

    def reconnect(self):
        """Reconnect the handler.

        If the handler was disconnected from the service with
        a retryable error - attempt to reconnect.
        This method will be called automatically for most retryable errors.
        """
        self._handler.close()
        self.running = False
        self._build_handler()
        self.open()

    def open(self):
        """Open handler connection and authenticate session.

        If the handler is already open, this operation will do nothing.
        A handler opened with this method must be explicitly closed.
        It is recommended to open a handler within a context manager as
        opposed to calling the method directly.

        .. note:: This operation is not thread-safe.

        """
        if self.running:
            return
        self.running = True
        try:
            self._handler.open(connection=self.connection)
            while not self._handler.client_ready():
                time.sleep(0.05)
        except Exception as e:  # pylint: disable=broad-except
            try:
                self._handle_exception(e)
            except:
                self.running = False
                raise

    def close(self, exception=None):
        """Close down the handler connection.

        If the handler has already closed, this operation will do nothing. An optional exception can be passed in to
        indicate that the handler was shutdown due to error.
        It is recommended to open a handler within a context manager as
        opposed to calling the method directly.

        .. note:: This operation is not thread-safe.

        :param exception: An optional exception if the handler is closing
         due to an error.
        :type exception: Exception
        """
        self.running = False
        if self.error:
            return
        if isinstance(exception, ServiceBusError):
            self.error = exception
        elif exception:
            self.error = ServiceBusError(str(exception))
        else:
            self.error = ServiceBusError("This message handler is now closed.")
        self._handler.close()
Esempio n. 6
0
class BaseHandler(object):  # pylint: disable=too-many-instance-attributes

    def __init__(self, endpoint, auth_config, connection=None, encoding='UTF-8', debug=False, **kwargs):
        self.running = False
        self.error = None
        self.endpoint = endpoint
        self.entity = urlparse(endpoint).path.strip('/')
        self.mgmt_target = self.entity + "/$management"
        self.debug = debug
        self.encoding = encoding
        self.auth_config = auth_config
        self.connection = connection
        self.auto_reconnect = kwargs.pop('auto_reconnect', True)
        self.properties = create_properties()
        self.error_policy = kwargs.pop('error_policy', None)
        self.handler_kwargs = kwargs
        if not self.error_policy:
            max_retries = kwargs.pop('max_message_retries', 3)
            is_session = hasattr(self, 'session_id')
            self.error_policy = _ServiceBusErrorPolicy(max_retries=max_retries, is_session=is_session)
        self._handler = None
        self._build_handler()

    def __enter__(self):
        """Open the handler in a context manager."""
        self.open()
        return self

    def __exit__(self, *args):
        """Close the handler when exiting a context manager."""
        self.close()

    def _build_handler(self):
        auth = None if self.connection else authentication.SASTokenAuth.from_shared_access_key(**self.auth_config)
        self._handler = AMQPClient(
            self.endpoint,
            auth=auth,
            debug=self.debug,
            properties=self.properties,
            error_policy=self.error_policy,
            encoding=self.encoding,
            **self.handler_kwargs)

    def _mgmt_request_response(self, operation, message, callback, **kwargs):
        if not self.running:
            raise InvalidHandlerState("Client connection is closed.")

        mgmt_msg = Message(
            body=message,
            properties=MessageProperties(
                reply_to=self.mgmt_target,
                encoding=self.encoding,
                **kwargs))
        try:
            return self._handler.mgmt_request(
                mgmt_msg,
                operation,
                op_type=b"entity-mgmt",
                node=self.mgmt_target.encode(self.encoding),
                timeout=5000,
                callback=callback)
        except Exception as exp:  # pylint: disable=broad-except
            raise ServiceBusError("Management request failed: {}".format(exp), exp)

    def _handle_exception(self, exception):
        if isinstance(exception, (errors.LinkDetach, errors.ConnectionClose)):
            if exception.action and exception.action.retry and self.auto_reconnect:
                _log.info("Handler detached. Attempting reconnect.")
                self.reconnect()
            elif exception.condition == constants.ErrorCodes.UnauthorizedAccess:
                _log.info("Handler detached. Shutting down.")
                error = ServiceBusAuthorizationError(str(exception), exception)
                self.close(exception=error)
                raise error
            else:
                _log.info("Handler detached. Shutting down.")
                error = ServiceBusConnectionError(str(exception), exception)
                self.close(exception=error)
                raise error
        elif isinstance(exception, errors.MessageHandlerError):
            if self.auto_reconnect:
                _log.info("Handler error. Attempting reconnect.")
                self.reconnect()
            else:
                _log.info("Handler error. Shutting down.")
                error = ServiceBusConnectionError(str(exception), exception)
                self.close(exception=error)
                raise error
        elif isinstance(exception, errors.AMQPConnectionError):
            message = "Failed to open handler: {}".format(exception)
            raise ServiceBusConnectionError(message, exception)
        else:
            _log.info("Unexpected error occurred (%r). Shutting down.", exception)
            error = ServiceBusError("Handler failed: {}".format(exception))
            self.close(exception=error)
            raise error

    def reconnect(self):
        """Reconnect the handler.

        If the handler was disconnected from the service with
        a retryable error - attempt to reconnect.
        This method will be called automatically for most retryable errors.
        """
        self._handler.close()
        self._build_handler()
        self.open()

    def open(self):
        """Open handler connection and authenticate session.

        If the handler is already open, this operation will do nothing.
        A handler opened with this method must be explicitly closed.
        It is recommended to open a handler within a context manager as
        opposed to calling the method directly.

        .. note:: This operation is not thread-safe.

        """
        if self.running:
            return
        self.running = True
        try:
            self._handler.open(connection=self.connection)
            while not self._handler.client_ready():
                time.sleep(0.05)
        except Exception as e:  # pylint: disable=broad-except
            try:
                self._handle_exception(e)
            except:
                self.running = False
                raise

    def close(self, exception=None):
        """Close down the handler connection.

        If the handler has already closed, this operation will do nothing. An optional exception can be passed in to
        indicate that the handler was shutdown due to error.
        It is recommended to open a handler within a context manager as
        opposed to calling the method directly.

        .. note:: This operation is not thread-safe.

        :param exception: An optional exception if the handler is closing
         due to an error.
        :type exception: Exception
        """
        self.running = False
        if self.error:
            return
        if isinstance(exception, ServiceBusError):
            self.error = exception
        elif exception:
            self.error = ServiceBusError(str(exception))
        else:
            self.error = ServiceBusError("This message handler is now closed.")
        self._handler.close()