File: //snap/google-cloud-cli/current/platform/bq/utils/bq_error_utils.py
#!/usr/bin/env python
"""BQ CLI helper functions for error handling."""
import codecs
import http.client
import logging
import sys
import textwrap
import time
import traceback
from absl import app
from absl import flags
from google.auth import exceptions as google_auth_exceptions
import googleapiclient
import httplib2
import oauth2client_4_0.client
import bq_utils
from gcloud_wrapper import bq_to_gcloud_config_classes
from utils import bq_error
from utils import bq_gcloud_utils
from utils import bq_logging
from pyglib import stringutil
FLAGS = flags.FLAGS
_BIGQUERY_TOS_MESSAGE = (
'In order to get started, please visit the Google APIs Console to '
'create a project and agree to our Terms of Service:\n'
'\thttps://console.cloud.google.com/\n\n'
'For detailed sign-up instructions, please see our Getting Started '
'Guide:\n'
'\thttps://cloud.google.com/bigquery/docs/quickstarts/'
'quickstart-command-line\n\n'
'Once you have completed the sign-up process, please try your command '
'again.'
)
def process_error(
err: BaseException,
name: str = 'unknown',
message_prefix: str = 'You have encountered a bug in the BigQuery CLI.',
) -> int:
"""Translate an error message into some printing and a return code."""
bq_logging.ConfigurePythonLogger(FLAGS.apilog)
logger = logging.getLogger(__name__)
if isinstance(err, SystemExit):
logger.exception('An error has caused the tool to exit', exc_info=err)
return err.code # sys.exit called somewhere, hopefully intentionally.
response = []
retcode = 1
(etype, value, tb) = sys.exc_info()
trace = ''.join(traceback.format_exception(etype, value, tb))
contact_us_msg = _generate_contact_us_message()
platform_str = bq_utils.GetPlatformString()
error_details = (
textwrap.dedent("""\
========================================
== Platform ==
%s
== bq version ==
%s
== Command line ==
%s
== UTC timestamp ==
%s
== Error trace ==
%s
========================================
""")
% (
platform_str,
stringutil.ensure_str(bq_utils.VERSION_NUMBER),
[stringutil.ensure_str(item) for item in sys.argv],
time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime()),
stringutil.ensure_str(trace),
)
)
codecs.register_error('strict', codecs.replace_errors)
message = bq_logging.EncodeForPrinting(err)
if isinstance(
err, (bq_error.BigqueryNotFoundError, bq_error.BigqueryDuplicateError)
):
response.append('BigQuery error in %s operation: %s' % (name, message))
retcode = 2
elif isinstance(err, bq_error.BigqueryTermsOfServiceError):
response.append(str(err) + '\n')
response.append(_BIGQUERY_TOS_MESSAGE)
elif isinstance(err, bq_error.BigqueryInvalidQueryError):
response.append('Error in query string: %s' % (message,))
elif isinstance(err, bq_error.BigqueryError) and not isinstance(
err, bq_error.BigqueryInterfaceError
):
response.append('BigQuery error in %s operation: %s' % (name, message))
elif isinstance(err, (app.UsageError, bq_error.BigqueryTypeError)):
response.append(message)
elif isinstance(
err, (bq_to_gcloud_config_classes.BigqueryGcloudDelegationUserError)
):
response.append(message)
elif isinstance(err, SyntaxError) or isinstance(
err, bq_error.BigquerySchemaError
):
response.append('Invalid input: %s' % (message,))
elif isinstance(err, flags.Error):
response.append('Error parsing command: %s' % (message,))
elif isinstance(err, KeyboardInterrupt):
response.append('')
else: # pylint: disable=broad-except
# Errors with traceback information are printed here.
# The traceback module has nicely formatted the error trace
# for us, so we don't want to undo that via TextWrap.
if isinstance(err, bq_error.BigqueryInterfaceError):
message_prefix = (
'Bigquery service returned an invalid reply in %s operation: %s.'
'\n\n'
'Please make sure you are using the latest version '
'of the bq tool and try again. '
'If this problem persists, you may have encountered a bug in the '
'bigquery client.' % (name, message)
)
elif isinstance(err, oauth2client_4_0.client.Error):
message_prefix = (
'Authorization error. This may be a network connection problem, '
'so please try again. If this problem persists, the credentials '
'may be corrupt. Try deleting and re-creating your credentials. '
'You can delete your credentials using '
'"bq init --delete_credentials".'
'\n\n'
'If this problem still occurs, you may have encountered a bug '
'in the bigquery client.'
)
elif isinstance(err, google_auth_exceptions.RefreshError):
credential_type = 'service account'
message_prefix = (
'Authorization error. If you used %s credentials, the server likely '
'returned an Unauthorized response. Verify that you are using the '
'correct account with the correct permissions to access the service '
'endpoint.'
'\n\n'
'If this problem still occurs, you may have encountered a bug '
'in the bigquery client.' % (credential_type)
)
elif (
isinstance(err, http.client.HTTPException)
or isinstance(err, googleapiclient.errors.Error)
or isinstance(err, httplib2.HttpLib2Error)
):
message_prefix = (
'Network connection problem encountered, please try again.'
'\n\n'
'If this problem persists, you may have encountered a bug in the '
'bigquery client.'
)
message = message_prefix + ' ' + contact_us_msg
wrap_error_message = True
if wrap_error_message:
message = flags.text_wrap(message)
print(message)
print(error_details)
response.append(
'Unexpected exception in %s operation: %s' % (name, message)
)
response_message = '\n'.join(response)
wrap_error_message = True
if wrap_error_message:
response_message = flags.text_wrap(response_message)
logger.exception(response_message, exc_info=err)
print(response_message)
return retcode
def _generate_contact_us_message() -> str:
"""Generates the Contact Us message."""
# pragma pylint: disable=line-too-long
contact_us_msg = (
'Please file a bug report in our '
'public '
'issue tracker:\n'
' https://issuetracker.google.com/issues/new?component=187149&template=0\n'
'Please include a brief description of '
'the steps that led to this issue, as well as '
'any rows that can be made public from '
'the following information: \n\n'
)
# If an internal user runs the public BQ CLI, show the internal issue tracker.
try:
gcloud_configs = bq_gcloud_utils.load_config()
gcloud_core_properties = gcloud_configs.get('core')
if (
'account' in gcloud_core_properties
and '@google.com' in gcloud_core_properties['account']
):
contact_us_msg = contact_us_msg.replace('public', 'internal').replace(
'https://issuetracker.google.com/issues/new?component=187149&template=0',
'http://b/issues/new?component=60322&template=178900',
)
except Exception: # pylint: disable=broad-exception-caught
# No-op if unable to determine the active account using gcloud.
pass
return contact_us_msg