Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / tools / telemetry / third_party / gsutilz / gslib / __main__.py
blob89511bc7a03dc6f19f50469f8e427686bba59ff4
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # Copyright 2013 Google Inc. All Rights Reserved.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 """Main module for Google Cloud Storage command line tool."""
18 from __future__ import absolute_import
20 import ConfigParser
21 import datetime
22 import errno
23 import getopt
24 import logging
25 import os
26 import re
27 import signal
28 import socket
29 import sys
30 import textwrap
31 import traceback
33 # Load the gsutil version number and append it to boto.UserAgent so the value is
34 # set before anything instantiates boto. This has to run after THIRD_PARTY_DIR
35 # is modified (done in gsutil.py) but before any calls are made that would cause
36 # boto.s3.Connection to be loaded - otherwise the Connection class would end up
37 # with a static reference to the pre-modified version of the UserAgent field,
38 # so boto requests would not include gsutil/version# in the UserAgent string.
39 import boto
40 import gslib
41 # TODO: gsutil-beta: Cloud SDK scans for this string and performs
42 # substitution; ensure this works with both apitools and boto.
43 boto.UserAgent += ' gsutil/%s (%s)' % (gslib.VERSION, sys.platform)
44 if os.environ.get('CLOUDSDK_WRAPPER') == '1':
45 boto.UserAgent += ' Cloud SDK Command Line Tool'
46 if os.environ.get('CLOUDSDK_VERSION'):
47 boto.UserAgent += ' %s' % os.environ.get('CLOUDSDK_VERSION')
49 # pylint: disable=g-bad-import-order
50 # pylint: disable=g-import-not-at-top
51 import httplib2
52 import oauth2client
53 from gslib import wildcard_iterator
54 from gslib.cloud_api import AccessDeniedException
55 from gslib.cloud_api import ArgumentException
56 from gslib.cloud_api import BadRequestException
57 from gslib.cloud_api import ProjectIdException
58 from gslib.cloud_api import ServiceException
59 from gslib.command_runner import CommandRunner
60 import gslib.exception
61 from gslib.exception import CommandException
62 import apitools.base.py.exceptions as apitools_exceptions
63 from gslib.util import CreateLock
64 from gslib.util import GetBotoConfigFileList
65 from gslib.util import GetCertsFile
66 from gslib.util import GetCleanupFiles
67 from gslib.util import GsutilStreamHandler
68 from gslib.util import ProxyInfoFromEnvironmentVar
69 from gslib.sig_handling import GetCaughtSignals
70 from gslib.sig_handling import InitializeSignalHandling
71 from gslib.sig_handling import RegisterSignalHandler
73 GSUTIL_CLIENT_ID = '909320924072.apps.googleusercontent.com'
74 # Google OAuth2 clients always have a secret, even if the client is an installed
75 # application/utility such as gsutil. Of course, in such cases the "secret" is
76 # actually publicly known; security depends entirely on the secrecy of refresh
77 # tokens, which effectively become bearer tokens.
78 GSUTIL_CLIENT_NOTSOSECRET = 'p3RlpR10xMFh9ZXBS/ZNLYUu'
79 if os.environ.get('CLOUDSDK_WRAPPER') == '1':
80 # Cloud SDK installs have a separate client ID / secret.
81 GSUTIL_CLIENT_ID = '32555940559.apps.googleusercontent.com'
82 GSUTIL_CLIENT_NOTSOSECRET = 'ZmssLNjJy2998hD4CTg2ejr2'
84 CONFIG_KEYS_TO_REDACT = ['proxy', 'proxy_port', 'proxy_user', 'proxy_pass']
87 # We don't use the oauth2 authentication plugin directly; importing it here
88 # ensures that it's loaded and available by default when an operation requiring
89 # authentication is performed.
90 try:
91 # pylint: disable=unused-import,g-import-not-at-top
92 import gcs_oauth2_boto_plugin
93 except ImportError:
94 pass
96 DEBUG_WARNING = """
97 ***************************** WARNING *****************************
98 *** You are running gsutil with debug output enabled.
99 *** Be aware that debug output includes authentication credentials.
100 *** Make sure to remove the value of the Authorization header for
101 *** each HTTP request printed to the console prior to posting to
102 *** a public medium such as a forum post or Stack Overflow.
103 ***************************** WARNING *****************************
104 """.lstrip()
106 HTTP_WARNING = """
107 ***************************** WARNING *****************************
108 *** You are running gsutil with the "https_validate_certificates" config
109 *** variable set to False. This option should always be set to True in
110 *** production environments to protect against man-in-the-middle attacks,
111 *** and leaking of user data.
112 ***************************** WARNING *****************************
113 """.lstrip()
115 debug = 0
116 test_exception_traces = False
119 # pylint: disable=unused-argument
120 def _CleanupSignalHandler(signal_num, cur_stack_frame):
121 """Cleans up if process is killed with SIGINT, SIGQUIT or SIGTERM."""
122 _Cleanup()
125 def _Cleanup():
126 for fname in GetCleanupFiles():
127 try:
128 os.unlink(fname)
129 except: # pylint: disable=bare-except
130 pass
133 def _OutputAndExit(message):
134 """Outputs message and exists with code 1."""
135 from gslib.util import UTF8 # pylint: disable=g-import-not-at-top
136 if debug >= 2 or test_exception_traces:
137 stack_trace = traceback.format_exc()
138 err = ('DEBUG: Exception stack trace:\n %s\n' %
139 re.sub('\\n', '\n ', stack_trace))
140 else:
141 err = '%s\n' % message
142 try:
143 sys.stderr.write(err.encode(UTF8))
144 except UnicodeDecodeError:
145 # Can happen when outputting invalid Unicode filenames.
146 sys.stderr.write(err)
147 sys.exit(1)
150 def _OutputUsageAndExit(command_runner):
151 command_runner.RunNamedCommand('help')
152 sys.exit(1)
155 class GsutilFormatter(logging.Formatter):
156 """A logging.Formatter that supports logging microseconds (%f)."""
158 def formatTime(self, record, datefmt=None):
159 if datefmt:
160 return datetime.datetime.fromtimestamp(record.created).strftime(datefmt)
162 # Use default implementation if datefmt is not specified.
163 return super(GsutilFormatter, self).formatTime(record, datefmt=datefmt)
166 def _ConfigureLogging(level=logging.INFO):
167 """Similar to logging.basicConfig() except it always adds a handler."""
168 log_format = '%(levelname)s %(asctime)s %(filename)s] %(message)s'
169 date_format = '%m%d %H:%M:%S.%f'
170 formatter = GsutilFormatter(fmt=log_format, datefmt=date_format)
171 handler = GsutilStreamHandler()
172 handler.setFormatter(formatter)
173 root_logger = logging.getLogger()
174 root_logger.addHandler(handler)
175 root_logger.setLevel(level)
178 def main():
179 InitializeSignalHandling()
180 # Any modules used in initializing multiprocessing variables must be
181 # imported after importing gslib.__main__.
182 # pylint: disable=redefined-outer-name,g-import-not-at-top
183 import gslib.boto_translation
184 import gslib.command
185 import gslib.util
186 from gslib.util import BOTO_IS_SECURE
187 from gslib.util import CERTIFICATE_VALIDATION_ENABLED
188 # pylint: disable=unused-variable
189 from gcs_oauth2_boto_plugin import oauth2_client
190 # pylint: enable=unused-variable
191 from gslib.util import MultiprocessingIsAvailable
192 if MultiprocessingIsAvailable()[0]:
193 # These setup methods must be called, and, on Windows, they can only be
194 # called from within an "if __name__ == '__main__':" block.
195 gslib.util.InitializeMultiprocessingVariables()
196 gslib.command.InitializeMultiprocessingVariables()
197 gslib.boto_translation.InitializeMultiprocessingVariables()
199 # This needs to be done after gslib.util.InitializeMultiprocessingVariables(),
200 # since otherwise we can't call gslib.util.CreateLock.
201 try:
202 # pylint: disable=unused-import,g-import-not-at-top
203 import gcs_oauth2_boto_plugin
204 gcs_oauth2_boto_plugin.oauth2_helper.SetFallbackClientIdAndSecret(
205 GSUTIL_CLIENT_ID, GSUTIL_CLIENT_NOTSOSECRET)
206 gcs_oauth2_boto_plugin.oauth2_helper.SetLock(CreateLock())
207 except ImportError:
208 pass
210 global debug
211 global test_exception_traces
213 if not (2, 6) <= sys.version_info[:3] < (3,):
214 raise gslib.exception.CommandException(
215 'gsutil requires python 2.6 or 2.7.')
217 # In gsutil 4.0 and beyond, we don't use the boto library for the JSON
218 # API. However, we still store gsutil configuration data in the .boto
219 # config file for compatibility with previous versions and user convenience.
220 # Many users have a .boto configuration file from previous versions, and it
221 # is useful to have all of the configuration for gsutil stored in one place.
222 command_runner = CommandRunner()
223 if not BOTO_IS_SECURE:
224 raise CommandException('\n'.join(textwrap.wrap(
225 'Your boto configuration has is_secure = False. Gsutil cannot be '
226 'run this way, for security reasons.')))
228 headers = {}
229 parallel_operations = False
230 quiet = False
231 version = False
232 debug = 0
233 test_exception_traces = False
235 # If user enters no commands just print the usage info.
236 if len(sys.argv) == 1:
237 sys.argv.append('help')
239 # Change the default of the 'https_validate_certificates' boto option to
240 # True (it is currently False in boto).
241 if not boto.config.has_option('Boto', 'https_validate_certificates'):
242 if not boto.config.has_section('Boto'):
243 boto.config.add_section('Boto')
244 boto.config.setbool('Boto', 'https_validate_certificates', True)
246 gslib.util.certs_file_lock = CreateLock()
247 for signal_num in GetCaughtSignals():
248 RegisterSignalHandler(signal_num, _CleanupSignalHandler)
249 GetCertsFile()
251 try:
252 try:
253 opts, args = getopt.getopt(sys.argv[1:], 'dDvo:h:mq',
254 ['debug', 'detailedDebug', 'version', 'option',
255 'help', 'header', 'multithreaded', 'quiet',
256 'testexceptiontraces'])
257 except getopt.GetoptError as e:
258 _HandleCommandException(gslib.exception.CommandException(e.msg))
259 for o, a in opts:
260 if o in ('-d', '--debug'):
261 # Passing debug=2 causes boto to include httplib header output.
262 debug = 3
263 elif o in ('-D', '--detailedDebug'):
264 # We use debug level 3 to ask gsutil code to output more detailed
265 # debug output. This is a bit of a hack since it overloads the same
266 # flag that was originally implemented for boto use. And we use -DD
267 # to ask for really detailed debugging (i.e., including HTTP payload).
268 if debug == 3:
269 debug = 4
270 else:
271 debug = 3
272 elif o in ('-?', '--help'):
273 _OutputUsageAndExit(command_runner)
274 elif o in ('-h', '--header'):
275 (hdr_name, _, hdr_val) = a.partition(':')
276 if not hdr_name:
277 _OutputUsageAndExit(command_runner)
278 headers[hdr_name.lower()] = hdr_val
279 elif o in ('-m', '--multithreaded'):
280 parallel_operations = True
281 elif o in ('-q', '--quiet'):
282 quiet = True
283 elif o in ('-v', '--version'):
284 version = True
285 elif o == '--testexceptiontraces': # Hidden flag for integration tests.
286 test_exception_traces = True
287 elif o in ('-o', '--option'):
288 (opt_section_name, _, opt_value) = a.partition('=')
289 if not opt_section_name:
290 _OutputUsageAndExit(command_runner)
291 (opt_section, _, opt_name) = opt_section_name.partition(':')
292 if not opt_section or not opt_name:
293 _OutputUsageAndExit(command_runner)
294 if not boto.config.has_section(opt_section):
295 boto.config.add_section(opt_section)
296 boto.config.set(opt_section, opt_name, opt_value)
297 httplib2.debuglevel = debug
298 if debug > 1:
299 sys.stderr.write(DEBUG_WARNING)
300 if debug >= 2:
301 _ConfigureLogging(level=logging.DEBUG)
302 command_runner.RunNamedCommand('ver', ['-l'])
303 config_items = []
304 try:
305 config_items.extend(boto.config.items('Boto'))
306 config_items.extend(boto.config.items('GSUtil'))
307 except ConfigParser.NoSectionError:
308 pass
309 for i in xrange(len(config_items)):
310 config_item_key = config_items[i][0]
311 if config_item_key in CONFIG_KEYS_TO_REDACT:
312 config_items[i] = (config_item_key, 'REDACTED')
313 sys.stderr.write('Command being run: %s\n' % ' '.join(sys.argv))
314 sys.stderr.write('config_file_list: %s\n' % GetBotoConfigFileList())
315 sys.stderr.write('config: %s\n' % str(config_items))
316 elif quiet:
317 _ConfigureLogging(level=logging.WARNING)
318 else:
319 _ConfigureLogging(level=logging.INFO)
320 # oauth2client uses info logging in places that would better
321 # correspond to gsutil's debug logging (e.g., when refreshing
322 # access tokens).
323 oauth2client.client.logger.setLevel(logging.WARNING)
325 if not CERTIFICATE_VALIDATION_ENABLED:
326 sys.stderr.write(HTTP_WARNING)
328 if version:
329 command_name = 'version'
330 elif not args:
331 command_name = 'help'
332 else:
333 command_name = args[0]
335 _CheckAndWarnForProxyDifferences()
337 if os.environ.get('_ARGCOMPLETE', '0') == '1':
338 return _PerformTabCompletion(command_runner)
340 return _RunNamedCommandAndHandleExceptions(
341 command_runner, command_name, args=args[1:], headers=headers,
342 debug_level=debug, parallel_operations=parallel_operations)
343 finally:
344 _Cleanup()
347 def _CheckAndWarnForProxyDifferences():
348 # If there are both boto config and environment variable config present for
349 # proxies, unset the environment variable and warn if it differs.
350 boto_port = boto.config.getint('Boto', 'proxy_port', 0)
351 if boto.config.get('Boto', 'proxy', None) or boto_port:
352 for proxy_env_var in ['http_proxy', 'https_proxy', 'HTTPS_PROXY']:
353 if proxy_env_var in os.environ and os.environ[proxy_env_var]:
354 differing_values = []
355 proxy_info = ProxyInfoFromEnvironmentVar(proxy_env_var)
356 if proxy_info.proxy_host != boto.config.get('Boto', 'proxy', None):
357 differing_values.append(
358 'Boto proxy host: "%s" differs from %s proxy host: "%s"' %
359 (boto.config.get('Boto', 'proxy', None), proxy_env_var,
360 proxy_info.proxy_host))
361 if (proxy_info.proxy_user !=
362 boto.config.get('Boto', 'proxy_user', None)):
363 differing_values.append(
364 'Boto proxy user: "%s" differs from %s proxy user: "%s"' %
365 (boto.config.get('Boto', 'proxy_user', None), proxy_env_var,
366 proxy_info.proxy_user))
367 if (proxy_info.proxy_pass !=
368 boto.config.get('Boto', 'proxy_pass', None)):
369 differing_values.append(
370 'Boto proxy password differs from %s proxy password' %
371 proxy_env_var)
372 # Only compare ports if at least one is present, since the
373 # boto logic for selecting default ports has not yet executed.
374 if ((proxy_info.proxy_port or boto_port) and
375 proxy_info.proxy_port != boto_port):
376 differing_values.append(
377 'Boto proxy port: "%s" differs from %s proxy port: "%s"' %
378 (boto_port, proxy_env_var, proxy_info.proxy_port))
379 if differing_values:
380 sys.stderr.write('\n'.join(textwrap.wrap(
381 'WARNING: Proxy configuration is present in both the %s '
382 'environment variable and boto configuration, but '
383 'configuration differs. boto configuration proxy values will '
384 'be used. Differences detected:' % proxy_env_var)))
385 sys.stderr.write('\n%s\n' % '\n'.join(differing_values))
386 # Regardless of whether the proxy configuration values matched,
387 # delete the environment variable so as not to confuse boto.
388 del os.environ[proxy_env_var]
391 def _HandleUnknownFailure(e):
392 # Called if we fall through all known/handled exceptions. Allows us to
393 # print a stacktrace if -D option used.
394 if debug >= 2:
395 stack_trace = traceback.format_exc()
396 sys.stderr.write('DEBUG: Exception stack trace:\n %s\n' %
397 re.sub('\\n', '\n ', stack_trace))
398 else:
399 _OutputAndExit('Failure: %s.' % e)
402 def _HandleCommandException(e):
403 if e.informational:
404 _OutputAndExit(e.reason)
405 else:
406 _OutputAndExit('CommandException: %s' % e.reason)
409 # pylint: disable=unused-argument
410 def _HandleControlC(signal_num, cur_stack_frame):
411 """Called when user hits ^C.
413 This function prints a brief message instead of the normal Python stack trace
414 (unless -D option is used).
416 Args:
417 signal_num: Signal that was caught.
418 cur_stack_frame: Unused.
420 if debug >= 2:
421 stack_trace = ''.join(traceback.format_list(traceback.extract_stack()))
422 _OutputAndExit(
423 'DEBUG: Caught signal %d - Exception stack trace:\n'
424 ' %s' % (signal_num, re.sub('\\n', '\n ', stack_trace)))
425 else:
426 _OutputAndExit('Caught signal %d - exiting' % signal_num)
429 def _HandleSigQuit(signal_num, cur_stack_frame):
430 """Called when user hits ^\\, so we can force breakpoint a running gsutil."""
431 import pdb # pylint: disable=g-import-not-at-top
432 pdb.set_trace()
435 def _ConstructAccountProblemHelp(reason):
436 """Constructs a help string for an access control error.
438 Args:
439 reason: e.reason string from caught exception.
441 Returns:
442 Contructed help text.
444 default_project_id = boto.config.get_value('GSUtil', 'default_project_id')
445 # pylint: disable=line-too-long, g-inconsistent-quotes
446 acct_help = (
447 "Your request resulted in an AccountProblem (403) error. Usually this "
448 "happens if you attempt to create a bucket without first having "
449 "enabled billing for the project you are using. Please ensure billing is "
450 "enabled for your project by following the instructions at "
451 "`Google Developers Console<https://developers.google.com/console/help/billing>`. ")
452 if default_project_id:
453 acct_help += (
454 "In the project overview, ensure that the Project Number listed for "
455 "your project matches the project ID (%s) from your boto config file. "
456 % default_project_id)
457 acct_help += (
458 "If the above doesn't resolve your AccountProblem, please send mail to "
459 "gs-team@google.com requesting assistance, noting the exact command you "
460 "ran, the fact that you received a 403 AccountProblem error, and your "
461 "project ID. Please do not post your project ID on StackOverflow. "
462 "Note: It's possible to use Google Cloud Storage without enabling "
463 "billing if you're only listing or reading objects for which you're "
464 "authorized, or if you're uploading objects to a bucket billed to a "
465 "project that has billing enabled. But if you're attempting to create "
466 "buckets or upload objects to a bucket owned by your own project, you "
467 "must first enable billing for that project.")
468 return acct_help
471 def _CheckAndHandleCredentialException(e, args):
472 # Provide detail to users who have no boto config file (who might previously
473 # have been using gsutil only for accessing publicly readable buckets and
474 # objects).
475 # pylint: disable=g-import-not-at-top
476 from gslib.util import HasConfiguredCredentials
477 if (not HasConfiguredCredentials() and
478 not boto.config.get_value('Tests', 'bypass_anonymous_access_warning',
479 False)):
480 # The check above allows tests to assert that we get a particular,
481 # expected failure, rather than always encountering this error message
482 # when there are no configured credentials. This allows tests to
483 # simulate a second user without permissions, without actually requiring
484 # two separate configured users.
485 if os.environ.get('CLOUDSDK_WRAPPER') == '1':
486 _OutputAndExit('\n'.join(textwrap.wrap(
487 'You are attempting to access protected data with no configured '
488 'credentials. Please visit '
489 'https://cloud.google.com/console#/project and sign up for an '
490 'account, and then run the "gcloud auth login" command to '
491 'configure gsutil to use these credentials.')))
492 else:
493 _OutputAndExit('\n'.join(textwrap.wrap(
494 'You are attempting to access protected data with no configured '
495 'credentials. Please visit '
496 'https://cloud.google.com/console#/project and sign up for an '
497 'account, and then run the "gsutil config" command to configure '
498 'gsutil to use these credentials.')))
499 elif (e.reason and
500 (e.reason == 'AccountProblem' or e.reason == 'Account disabled.' or
501 'account for the specified project has been disabled' in e.reason)
502 and ','.join(args).find('gs://') != -1):
503 _OutputAndExit('\n'.join(textwrap.wrap(
504 _ConstructAccountProblemHelp(e.reason))))
507 def _RunNamedCommandAndHandleExceptions(command_runner, command_name, args=None,
508 headers=None, debug_level=0,
509 parallel_operations=False):
510 """Runs the command with the given command runner and arguments."""
511 # pylint: disable=g-import-not-at-top
512 from gslib.util import GetConfigFilePath
513 from gslib.util import IS_WINDOWS
514 from gslib.util import IsRunningInteractively
515 try:
516 # Catch ^C so we can print a brief message instead of the normal Python
517 # stack trace. Register as a final signal handler because this handler kills
518 # the main gsutil process (so it must run last).
519 RegisterSignalHandler(signal.SIGINT, _HandleControlC, is_final_handler=True)
520 # Catch ^\ so we can force a breakpoint in a running gsutil.
521 if not IS_WINDOWS:
522 RegisterSignalHandler(signal.SIGQUIT, _HandleSigQuit)
523 return command_runner.RunNamedCommand(command_name, args, headers,
524 debug_level, parallel_operations)
525 except AttributeError as e:
526 if str(e).find('secret_access_key') != -1:
527 _OutputAndExit('Missing credentials for the given URI(s). Does your '
528 'boto config file contain all needed credentials?')
529 else:
530 _OutputAndExit(str(e))
531 except gslib.exception.CommandException as e:
532 _HandleCommandException(e)
533 except getopt.GetoptError as e:
534 _HandleCommandException(gslib.exception.CommandException(e.msg))
535 except boto.exception.InvalidUriError as e:
536 _OutputAndExit('InvalidUriError: %s.' % e.message)
537 except gslib.exception.InvalidUrlError as e:
538 _OutputAndExit('InvalidUrlError: %s.' % e.message)
539 except boto.auth_handler.NotReadyToAuthenticate:
540 _OutputAndExit('NotReadyToAuthenticate')
541 except OSError as e:
542 _OutputAndExit('OSError: %s.' % e.strerror)
543 except IOError as e:
544 if (e.errno == errno.EPIPE or (IS_WINDOWS and e.errno == errno.EINVAL)
545 and not IsRunningInteractively()):
546 # If we get a pipe error, this just means that the pipe to stdout or
547 # stderr is broken. This can happen if the user pipes gsutil to a command
548 # that doesn't use the entire output stream. Instead of raising an error,
549 # just swallow it up and exit cleanly.
550 sys.exit(0)
551 else:
552 raise
553 except wildcard_iterator.WildcardException as e:
554 _OutputAndExit(e.reason)
555 except ProjectIdException as e:
556 _OutputAndExit(
557 'You are attempting to perform an operation that requires a '
558 'project id, with none configured. Please re-run '
559 'gsutil config and make sure to follow the instructions for '
560 'finding and entering your default project id.')
561 except BadRequestException as e:
562 if e.reason == 'MissingSecurityHeader':
563 _CheckAndHandleCredentialException(e, args)
564 _OutputAndExit(e)
565 except AccessDeniedException as e:
566 _CheckAndHandleCredentialException(e, args)
567 _OutputAndExit(e)
568 except ArgumentException as e:
569 _OutputAndExit(e)
570 except ServiceException as e:
571 _OutputAndExit(e)
572 except apitools_exceptions.HttpError as e:
573 # These should usually be retried by the underlying implementation or
574 # wrapped by CloudApi ServiceExceptions, but if we do get them,
575 # print something useful.
576 _OutputAndExit('HttpError: %s, %s' % (getattr(e.response, 'status', ''),
577 e.content or ''))
578 except socket.error as e:
579 if e.args[0] == errno.EPIPE:
580 # Retrying with a smaller file (per suggestion below) works because
581 # the library code send loop (in boto/s3/key.py) can get through the
582 # entire file and then request the HTTP response before the socket
583 # gets closed and the response lost.
584 _OutputAndExit(
585 'Got a "Broken pipe" error. This can happen to clients using Python '
586 '2.x, when the server sends an error response and then closes the '
587 'socket (see http://bugs.python.org/issue5542). If you are trying to '
588 'upload a large object you might retry with a small (say 200k) '
589 'object, and see if you get a more specific error code.'
591 else:
592 _HandleUnknownFailure(e)
593 except Exception as e:
594 # Check for two types of errors related to service accounts. These errors
595 # appear to be the same except for their messages, but they are caused by
596 # different problems and both have unhelpful error messages. Moreover,
597 # the error type belongs to PyOpenSSL, which is not necessarily installed.
598 if 'mac verify failure' in str(e):
599 _OutputAndExit(
600 'Encountered an error while refreshing access token. '
601 'If you are using a service account,\nplease verify that the '
602 'gs_service_key_file_password field in your config file,'
603 '\n%s, is correct.' % GetConfigFilePath())
604 elif 'asn1 encoding routines' in str(e):
605 _OutputAndExit(
606 'Encountered an error while refreshing access token. '
607 'If you are using a service account,\nplease verify that the '
608 'gs_service_key_file field in your config file,\n%s, is correct.'
609 % GetConfigFilePath())
610 _HandleUnknownFailure(e)
613 def _PerformTabCompletion(command_runner):
614 """Performs gsutil-specific tab completion for the shell."""
615 # argparse and argcomplete are bundled with the Google Cloud SDK.
616 # When gsutil is invoked from the Google Cloud SDK, both should be available.
617 try:
618 import argcomplete
619 import argparse
620 except ImportError as e:
621 _OutputAndExit('A library required for performing tab completion was'
622 ' not found.\nCause: %s' % e)
623 parser = argparse.ArgumentParser(add_help=False)
624 subparsers = parser.add_subparsers()
625 command_runner.ConfigureCommandArgumentParsers(subparsers)
626 argcomplete.autocomplete(parser, exit_method=sys.exit)
628 return 0
630 if __name__ == '__main__':
631 sys.exit(main())