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())
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")
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
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
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
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())
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)
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))