def landoapi_exception(e): sentry.captureException() logger.exception('Uncaught communication exception with Lando API.') return render_template( 'errorhandlers/default_error.html', title='Lando API returned an unexpected error', message=str(e) ), 500
def landoapi_exception(e): sentry.captureException() logger.exception("Uncaught communication exception with Lando API.") return ( render_template( "errorhandlers/default_error.html", title="Lando API returned an unexpected error", message=str(e), ), 500, )
def landoapi_communication(e): sentry.captureException() logger.exception('Uncaught communication exception with Lando API.') return render_template( 'errorhandlers/default_error.html', title='Could not Communicate with Lando API', message=( 'There was an error when trying to communicate with ' 'Lando API. Please try your request again later.' ) ), 500
def landoapi_communication(e): sentry.captureException() logger.exception("Uncaught communication exception with Lando API.") return ( render_template( "errorhandlers/default_error.html", title="Could not Communicate with Lando API", message=("There was an error when trying to communicate with " "Lando API. Please try your request again later."), ), 500, )
def unexpected_error(e): """Handler for all uncaught Exceptions.""" logger.exception('unexpected error') sentry.captureException() return render_template( 'errorhandlers/default_error.html', title='Oops! Something went wrong.', message=( 'Something just went wrong. Try again or tell the team at ' '#lando on irc.' ) ), 500
def unexpected_error(e): """Handler for all uncaught Exceptions.""" logger.exception("unexpected error") sentry.captureException() return ( render_template( "errorhandlers/default_error.html", title="Oops! Something went wrong.", message=( "Something just went wrong. Try again or tell the team at " "#lando on irc."), ), 500, )
def unexpected_error(e): """Handler for all uncaught Exceptions.""" logger.exception("unexpected error") sentry.captureException() return ( render_template( "errorhandlers/default_error.html", title="Oops! Something went wrong.", message=( "Something just went wrong. Try again or tell the team at " "#conduit on Matrix (https://matrix.to/#/#conduit:mozilla.org)." ), ), 500, )
def revision(revision_id, diff_id=''): # TODO: Add diff ID when the API side is complete revision_api_url = '{host}/revisions/{revision_id}'.format( host=current_app.config['LANDO_API_URL'], revision_id=revision_id) try: result = requests.get(revision_api_url) result.raise_for_status() except requests.HTTPError as exc: if exc.response.status_code == 404: # The user looked up a non-existent revision, no special treatment # is necessary. abort(404) else: sentry.captureException() abort(500) except requests.ConnectionError: sentry.captureException() abort(500) revision = result.json() form = RevisionForm() if form.is_submitted(): if form.validate(): # TODO # Any more basic validation # Make request to API for landing diff_id = int(form.diff_id.data) land_response = requests.post( '{host}/landings'.format( host=current_app.config['LANDO_API_URL']), json={ 'revision_id': revision_id, 'diff_id': diff_id, }, headers={ # TODO: Add Phabricator API key for private revisions # 'X-Phabricator-API-Key': '', 'Authorization': 'Bearer {}'.format(session['access_token']), 'Content-Type': 'application/json', }) logger.info(land_response.json(), 'revision.landing.response') if land_response.status_code == 200: redirect_url = '/revisions/{revision_id}/{diff_id}'.format( revision_id=revision_id, diff_id=diff_id) return redirect(redirect_url) else: # TODO: Push an error on to an error stack to show in UI pass else: # TODO # Return validation errors pass # Set the diff id explicitly to avoid timing conflicts with # revision diff IDs being updated form.diff_id.data = revision['diff']['id'] return render_template('revision/revision.html', revision=revision, author=revision['author'], repo=revision['repo'], parents=_flatten_parent_revisions(revision), form=form)
def post_landings_dryrun(self, revision_id, diff_id): """Queries Lando API's /landings/dryrun endpoint. The dryrun checks if there are any issues before landings. Issues can one be either a warning or a blocker. Users may acknowledge a warning and continue landing. If there are any blockers, landing is blocked until those resolve. Lando API also provides a confirmation token which is expected when performing the real landing via the POST /landings endpoint. This token tells the API that we've properly tried a dryrun first and acknowledged any warnings. The LandoAPIClient must be initialized with an auth0 access token to use this endpoint. If a user is not logged in, they should not be able to see warnings and blockers for landing. Args: revision_id: The id of the revision to land in 'D123' format. diff_id: The id of the specific diff of the revision to land. Returns: A dictionary with the following format: { 'confirmation_token': token, 'warnings': [{'id': id, 'message': message}, ...], 'blockers': [{'id': id, 'message': message}, ...] } Does not return if unsuccessful. Exceptions: Raises an AssertionError if the LandoAPIClient was not initialized with an auth0_access_token. Raises a UIError in all cases where communication with lando-api and parsing the response fails. """ # Callers should not be using this method if the user is not logged in. assert self.auth0_access_token # Setup request dryrun_url = '{host}/landings/dryrun'.format(host=self.landoapi_url) params = { 'revision_id': revision_id, 'diff_id': int(diff_id), } headers = { 'Authorization': 'Bearer {}'.format(self.auth0_access_token), 'Content-Type': 'application/json', } # Make request and handle response try: dryrun_response = requests.post(dryrun_url, json=params, headers=headers) dryrun_response.raise_for_status() return dryrun_response.json() except requests.HTTPError as e: # All HTTP errors from Lando API should be in the Connexions # problem exception format and include title, detail, and type. try: problem = e.response.json() uierror = UIError(title=problem['title'], message=problem['detail'], status_code=e.response.status_code) except (json.JSONDecodeError, KeyError) as e2: logger.error( { 'url': dryrun_url, 'revision_id': revision_id, 'diff_id': diff_id, 'status_code': e.response.status_code, 'response_body': e.response.text, 'exception_type': str(type(e2)), 'exception_message': str(e2) }, 'post_landings_dryrun.failed_reading_http_error') sentry.captureException() raise UIError( title='Lando API did not respond successfully.', message=('Unable to communicate with Lando API at this ' 'time. Please try again later.'), status_code=e.response.status_code) logger.debug( { 'url': dryrun_url, 'revision_id': revision_id, 'diff_id': diff_id, 'status_code': e.response.status_code, 'problem_title': problem['title'], 'problem_message': problem['detail'], 'problem_type': problem.get('type') }, 'post_landings_dryrun.failed_dryrun') raise uierror except requests.RequestException: logger.debug( { 'url': dryrun_url, 'revision_id': revision_id, 'diff_id': diff_id, }, 'post_landings_dryrun.failed_connection_to_landoapi') raise UIError( title='Failed to connect to Lando API.', message=('Unable to communicate with Lando API at this ' 'time. Please try again later.'), )
def post_landings(self, revision_id, diff_id, confirmation_token): """Submit a land request to lando-api via the POST /landings endpoint. The LandoAPIClient must be initialized with an auth0 access token to use this method. Args: revision_id: The id of the revision to land in 'D123' format. diff_id: The id of the specific diff of the revision to land. Returns: If successful, returns True. Does not return if unsuccessful. Exceptions: Raises an AssertionError if the LandoAPIClient wasn't initialized with an auth0_access_token. Raises a LandingSubmissionError if the landing submission fails for any reason. """ # Callers should not be using this method if the user is not logged in. assert self.auth0_access_token # Setup request post_landings_url = '{host}/landings'.format(host=self.landoapi_url) params = { 'revision_id': revision_id, 'diff_id': int(diff_id), 'confirmation_token': confirmation_token, } headers = { 'Authorization': 'Bearer {}'.format(self.auth0_access_token), 'Content-Type': 'application/json', } # Make request and handle response try: land_response = requests.post(post_landings_url, json=params, headers=headers) land_response.raise_for_status() if land_response.status_code not in (200, 202): logger.error( { 'url': post_landings_url, 'revision_id': revision_id, 'diff_id': diff_id, 'status_code': land_response.status_code, 'response_body': land_response.text }, 'post_landings.invalid_2xx_code') sentry.captureMessage('landoapi returned an unexpected 2xx') raise LandingSubmissionError( 'Lando API did not respond successfully. ' 'Please try again later.') return True except requests.HTTPError as e: # All HTTP errors from Lando API should be in the Connexions # problem exception format and include title, detail, and type. try: problem = e.response.json() problem_message = ('{title}: {detail}'.format( title=problem['title'], detail=problem['detail'])) except (json.JSONDecodeError, KeyError) as e2: logger.error( { 'url': post_landings_url, 'revision_id': revision_id, 'diff_id': diff_id, 'status_code': e.response.status_code, 'response_body': e.response.text, 'exception_type': str(type(e2)), 'exception_message': str(e2) }, 'post_landings.failed_reading_http_error') sentry.captureException() raise LandingSubmissionError( 'Lando API did not respond successfully. ' 'Please try again later.') logger.debug( { 'url': post_landings_url, 'revision_id': revision_id, 'diff_id': diff_id, 'status_code': e.response.status_code, 'problem_message': problem_message, 'problem_type': problem.get('type') }, 'post_landings.failed_landing') raise LandingSubmissionError(error=problem_message, link=problem.get('type')) except requests.RequestException: logger.debug( { 'url': post_landings_url, 'revision_id': revision_id, 'diff_id': diff_id, }, 'post_landings.failed_connection_to_landoapi') raise LandingSubmissionError('Failed to connect to Lando API. ' 'Please try again later.')