Esempio n. 1
0
def handle_response_failure(response, default_exc=RestError, raise_404=True):
    """Deal with a response with non-2xx status code

    Args:
        response: The response object
        default_exc: The exception to raise if no specific exception is warranted
        raise_404: If True a response with status code 404 will raise a NotFoundError.
            If False the method will return None.

    Returns:
        None - this function will always raise

    Raises:
        NotFoundError: Status code 404 and raise_404 is True
        WaitExceededError: Status code 408
        ConflictError: Status code 409
        ValidationError: Any other 4xx status codes
        RestConnectionError: Status code 503
        default_exc: Any other status code
    """
    if response.status_code == 404:
        if raise_404:
            raise NotFoundError(response.json())
        else:
            return None
    elif response.status_code == 408:
        raise WaitExceededError(response.json())
    elif response.status_code == 409:
        raise ConflictError(response.json())
    elif 400 <= response.status_code < 500:
        raise ValidationError(response.json())
    elif response.status_code == 503:
        raise RestConnectionError(response.json())
    else:
        raise default_exc(response.json())
Esempio n. 2
0
    def validate_parent_status(self, request):
        """Ensure that a Request's parent request hasn't already completed.

        :param request: The request to validate
        :raises ConflictError: The parent request has already completed
        """
        if request.parent and request.parent.status in Request.COMPLETED_STATUSES:
            raise ConflictError("Parent request has already completed")
Esempio n. 3
0
    def test_new_system_conflict_succeed(self, plugin, ez_client, bg_system):
        ez_client.find_unique_system.side_effect = [None, bg_system]
        ez_client.create_system.side_effect = ConflictError()

        plugin._initialize_system()
        ez_client.create_system.assert_called_once_with(bg_system)
        assert ez_client.find_unique_system.call_count == 2
        assert ez_client.update_system.called is True
Esempio n. 4
0
    def test_new_system_conflict_fail(self, plugin, ez_client, bg_system):
        ez_client.find_unique_system.return_value = None
        ez_client.create_system.side_effect = ConflictError()

        with pytest.raises(PluginValidationError):
            plugin._initialize_system()

        ez_client.create_system.assert_called_once_with(bg_system)
        assert ez_client.find_unique_system.call_count == 2
        assert ez_client.update_system.called is False
Esempio n. 5
0
    def get_and_validate_parent(self, request):
        """Ensure that a Request's parent request hasn't already completed.

        :param request: The request to validate
        :return: Boolean indicating if this request has a parent
        :raises ConflictError: The parent request has already completed
        """
        if not request.parent:
            return False

        if request.parent.status in Request.COMPLETED_STATUSES:
            raise ConflictError("Parent request has already completed")

        return True
Esempio n. 6
0
 def _handle_response_failure(response,
                              default_exc=RestError,
                              raise_404=True):
     if response.status_code == 404:
         if raise_404:
             raise NotFoundError(response.json())
         else:
             return None
     elif response.status_code == 408:
         raise WaitExceededError(response.json())
     elif response.status_code == 409:
         raise ConflictError(response.json())
     elif 400 <= response.status_code < 500:
         raise ValidationError(response.json())
     elif response.status_code == 503:
         raise RestConnectionError(response.json())
     else:
         raise default_exc(response.json())
Esempio n. 7
0
def handle_response_failure(response, default_exc=RestError, raise_404=True):
    # type: (Response, Type[BrewtilsException], bool) -> NoReturn
    """Deal with a response with non-2xx status code

    Args:
        response: The response object
        default_exc: The exception to raise if no specific exception is warranted
        raise_404: If True a response with status code 404 will raise a NotFoundError.
            If False the method will return None.

    Returns:
        None - this function will always raise

    Raises:
        NotFoundError: Status code 404 and raise_404 is True
        WaitExceededError: Status code 408
        ConflictError: Status code 409
        TooLargeError: Status code 413
        ValidationError: Any other 4xx status codes
        RestConnectionError: Status code 503
        default_exc: Any other status code
    """
    try:
        message = response.json()
    except ValueError:
        message = response.text

    if response.status_code == 404:
        if raise_404:
            raise NotFoundError(message)
        else:
            return None
    elif response.status_code == 408:
        raise WaitExceededError(message)
    elif response.status_code == 409:
        raise ConflictError(message)
    elif response.status_code == 413:
        raise TooLargeError(message)
    elif 400 <= response.status_code < 500:
        raise ValidationError(message)
    elif response.status_code == 503:
        raise RestConnectionError(message)
    else:
        raise default_exc(message)
Esempio n. 8
0
    def post(self):
        """
        ---
        summary: Create a new Request
        parameters:
          - name: request
            in: body
            description: The Request definition
            schema:
              $ref: '#/definitions/Request'
          - name: blocking
            in: query
            required: false
            description: Flag indicating whether to wait for request completion
            type: boolean
            default: false
          - name: timeout
            in: query
            required: false
            description: Maximum time (seconds) to wait for request completion
            type: integer
            default: None (Wait forever)
        consumes:
          - application/json
          - application/x-www-form-urlencoded
        responses:
          201:
            description: A new Request has been created
            schema:
              $ref: '#/definitions/Request'
            headers:
              Instance-Status:
                type: string
                description: |
                    Current status of the Instance that will process the
                    created Request
          400:
            $ref: '#/definitions/400Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Requests
        """
        self.request.event.name = Events.REQUEST_CREATED.name

        if self.request.mime_type == "application/json":
            request_model = self.parser.parse_request(
                self.request.decoded_body, from_string=True)
        elif self.request.mime_type == "application/x-www-form-urlencoded":
            args = {"parameters": {}}
            for key, value in self.request.body_arguments.items():
                if key.startswith("parameters."):
                    args["parameters"][key.replace("parameters.",
                                                   "")] = value[0].decode(
                                                       self.request.charset)
                else:
                    args[key] = value[0].decode(self.request.charset)
            request_model = Request(**args)
        else:
            raise ModelValidationError(
                "Unsupported or missing content-type header")

        if request_model.parent:
            request_model.parent = Request.objects.get(
                id=str(request_model.parent.id))
            if request_model.parent.status in Request.COMPLETED_STATUSES:
                raise ConflictError("Parent request has already completed")
            request_model.has_parent = True
        else:
            request_model.has_parent = False

        if self.current_user:
            request_model.requester = self.current_user.username

        # Ok, ready to save
        request_model.save()
        request_id = str(request_model.id)

        # Set up the wait event BEFORE yielding the processRequest call
        blocking = self.get_argument("blocking", default="").lower() == "true"
        if blocking:
            brew_view.request_map[request_id] = Event()

        with thrift_context() as client:
            try:
                yield client.processRequest(request_id)
            except bg_utils.bg_thrift.InvalidRequest as ex:
                request_model.delete()
                raise ModelValidationError(ex.message)
            except bg_utils.bg_thrift.PublishException as ex:
                request_model.delete()
                raise RequestPublishException(ex.message)
            except Exception:
                if request_model.id:
                    request_model.delete()
                raise

        # Query for request from body id
        req = Request.objects.get(id=request_id)

        # Now attempt to add the instance status as a header.
        # The Request is already created at this point so it's a best-effort thing
        self.set_header("Instance-Status", "UNKNOWN")

        try:
            # Since request has system info we can query for a system object
            system = System.objects.get(name=req.system,
                                        version=req.system_version)

            # Loop through all instances in the system until we find the instance that
            # matches the request instance
            for instance in system.instances:
                if instance.name == req.instance_name:
                    self.set_header("Instance-Status", instance.status)

        # The Request is already created at this point so adding the Instance status
        # header is a best-effort thing
        except Exception as ex:
            self.logger.exception(
                "Unable to get Instance status for Request %s: %s", request_id,
                ex)

        self.request.event_extras = {"request": req}

        # Metrics
        request_created(request_model)

        if blocking:
            # Publish metrics and event here here so they aren't skewed
            # See https://github.com/beer-garden/beer-garden/issues/190
            self.request.publish_metrics = False
            http_api_latency_total.labels(
                method=self.request.method.upper(),
                route=self.prometheus_endpoint,
                status=self.get_status(),
            ).observe(request_latency(self.request.created_time))

            self.request.publish_event = False
            brew_view.event_publishers.publish_event(
                self.request.event, **self.request.event_extras)

            try:
                timeout = self.get_argument("timeout", default=None)
                delta = timedelta(seconds=int(timeout)) if timeout else None

                event = brew_view.request_map.get(request_id)

                yield event.wait(delta)

                request_model.reload()
            except TimeoutError:
                raise TimeoutExceededError("Timeout exceeded for request %s" %
                                           request_id)
            finally:
                brew_view.request_map.pop(request_id, None)

        self.set_status(201)
        self.write(
            self.parser.serialize_request(request_model, to_string=False))