Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Side by Side Diff: gslib/__main__.py

Issue 698893003: Update checked in version of gsutil to version 4.6 (Closed) Base URL: http://dart.googlecode.com/svn/third_party/gsutil/
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « gslib/__init__.py ('k') | gslib/aclhelpers.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # coding=utf8 2 # -*- coding: utf-8 -*-
3 # Copyright 2013 Google Inc. All Rights Reserved. 3 # Copyright 2013 Google Inc. All Rights Reserved.
4 # 4 #
5 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with 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 7 # You may obtain a copy of the License at
8 # 8 #
9 # http://www.apache.org/licenses/LICENSE-2.0 9 # http://www.apache.org/licenses/LICENSE-2.0
10 # 10 #
11 # Unless required by applicable law or agreed to in writing, software 11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS, 12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and 14 # See the License for the specific language governing permissions and
15 # limitations under the License. 15 # limitations under the License.
16
17 """Main module for Google Cloud Storage command line tool.""" 16 """Main module for Google Cloud Storage command line tool."""
18 17
18 from __future__ import absolute_import
19
19 import ConfigParser 20 import ConfigParser
21 import datetime
20 import errno 22 import errno
21 import getopt 23 import getopt
22 import logging 24 import logging
23 import os 25 import os
24 import pkgutil
25 import re 26 import re
26 import signal 27 import signal
27 import socket 28 import socket
28 import sys 29 import sys
29 import tempfile
30 import textwrap 30 import textwrap
31 import traceback 31 import traceback
32 32
33 # Load the gsutil version number and append it to boto.UserAgent so the value is 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 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 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 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, 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. 38 # so boto requests would not include gsutil/version# in the UserAgent string.
39 import boto 39 import boto
40 import gslib 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.
41 boto.UserAgent += ' gsutil/%s (%s)' % (gslib.VERSION, sys.platform) 43 boto.UserAgent += ' gsutil/%s (%s)' % (gslib.VERSION, sys.platform)
42 44
43 import apiclient.discovery 45 # pylint: disable=g-bad-import-order
44 import boto.exception 46 # pylint: disable=g-import-not-at-top
45 from gslib import util
46 from gslib import GSUTIL_DIR
47 from gslib import wildcard_iterator
48 from gslib.command_runner import CommandRunner
49 from gslib.exception import CommandException
50 from gslib.util import BOTO_IS_SECURE
51 from gslib.util import GetBotoConfigFileList
52 from gslib.util import GetConfigFilePath
53 from gslib.util import HasConfiguredCredentials
54 from gslib.util import IsRunningInteractively
55 import gslib.exception
56 import httplib2 47 import httplib2
57 import oauth2client 48 import oauth2client
49 from gslib import wildcard_iterator
50 from gslib.cloud_api import AccessDeniedException
51 from gslib.cloud_api import ArgumentException
52 from gslib.cloud_api import BadRequestException
53 from gslib.cloud_api import ProjectIdException
54 from gslib.cloud_api import ServiceException
55 from gslib.command_runner import CommandRunner
56 import gslib.exception
57 from gslib.exception import CommandException
58 import gslib.third_party.storage_apitools.exceptions as apitools_exceptions
59 from gslib.util import CreateLock
60 from gslib.util import GetBotoConfigFileList
61 from gslib.util import GetCertsFile
62 from gslib.util import GetCleanupFiles
63
64 GSUTIL_CLIENT_ID = '909320924072.apps.googleusercontent.com'
65 # Google OAuth2 clients always have a secret, even if the client is an installed
66 # application/utility such as gsutil. Of course, in such cases the "secret" is
67 # actually publicly known; security depends entirely on the secrecy of refresh
68 # tokens, which effectively become bearer tokens.
69 GSUTIL_CLIENT_NOTSOSECRET = 'p3RlpR10xMFh9ZXBS/ZNLYUu'
70
71 CONFIG_KEYS_TO_REDACT = ['proxy', 'proxy_port', 'proxy_user', 'proxy_pass']
72
58 73
59 # We don't use the oauth2 authentication plugin directly; importing it here 74 # We don't use the oauth2 authentication plugin directly; importing it here
60 # ensures that it's loaded and available by default when an operation requiring 75 # ensures that it's loaded and available by default when an operation requiring
61 # authentication is performed. 76 # authentication is performed.
62 try: 77 try:
63 from gslib.third_party.oauth2_plugin import oauth2_plugin 78 # pylint: disable=unused-import,g-import-not-at-top
79 import gcs_oauth2_boto_plugin
64 except ImportError: 80 except ImportError:
65 pass 81 pass
66 82
67 DEBUG_WARNING = """ 83 DEBUG_WARNING = """
68 ***************************** WARNING ***************************** 84 ***************************** WARNING *****************************
69 *** You are running gsutil with debug output enabled. 85 *** You are running gsutil with debug output enabled.
70 *** Be aware that debug output includes authentication credentials. 86 *** Be aware that debug output includes authentication credentials.
71 *** Make sure to remove the value of the Authorization header for 87 *** Make sure to remove the value of the Authorization header for
72 *** each HTTP request printed to the console prior to posting to 88 *** each HTTP request printed to the console prior to posting to
73 *** a public medium such as a forum post or Stack Overflow. 89 *** a public medium such as a forum post or Stack Overflow.
74 ***************************** WARNING ***************************** 90 ***************************** WARNING *****************************
75 """.lstrip() 91 """.lstrip()
76 92
77 HTTP_WARNING = """ 93 HTTP_WARNING = """
78 ***************************** WARNING ***************************** 94 ***************************** WARNING *****************************
79 *** You are running gsutil with either the boto config variable "is_secure" set 95 *** You are running gsutil with the "https_validate_certificates" config
80 *** to False or the "https_validate_certificates" config variable set to False. 96 *** variable set to False. This option should always be set to True in
81 *** These options should always be set to True in production environments, to 97 *** production environments to protect against man-in-the-middle attacks,
82 *** protect against intercepted bearer tokens, man-in-the-middle attacks, and 98 *** and leaking of user data.
83 *** leaking of user data.
84 ***************************** WARNING ***************************** 99 ***************************** WARNING *****************************
85 """.lstrip() 100 """.lstrip()
86 101
87 debug = 0 102 debug = 0
88 103 test_exception_traces = False
89 # Temp files to delete, if possible, when program exits.
90 cleanup_files = []
91 104
92 105
93 def _Cleanup(): 106 def _Cleanup():
94 for fname in cleanup_files: 107 for fname in GetCleanupFiles():
95 try: 108 try:
96 os.remove(fname) 109 os.remove(fname)
97 except OSError: 110 except OSError:
98 pass 111 pass
99 112
100 113
101 def _OutputAndExit(message): 114 def _OutputAndExit(message):
102 if debug == 4: 115 """Outputs message and exists with code 1."""
116 from gslib.util import UTF8 # pylint: disable=g-import-not-at-top
117 if debug >= 2 or test_exception_traces:
103 stack_trace = traceback.format_exc() 118 stack_trace = traceback.format_exc()
104 err = ('DEBUG: Exception stack trace:\n %s\n' % 119 err = ('DEBUG: Exception stack trace:\n %s\n' %
105 re.sub('\\n', '\n ', stack_trace)) 120 re.sub('\\n', '\n ', stack_trace))
106 else: 121 else:
107 err = '%s\n' % message 122 err = '%s\n' % message
108 sys.stderr.write(err.encode('utf-8')) 123 try:
124 sys.stderr.write(err.encode(UTF8))
125 except UnicodeDecodeError:
126 # Can happen when outputting invalid Unicode filenames.
127 sys.stderr.write(err)
109 sys.exit(1) 128 sys.exit(1)
110 129
111 130
112 def _OutputUsageAndExit(command_runner): 131 def _OutputUsageAndExit(command_runner):
113 command_runner.RunNamedCommand('help') 132 command_runner.RunNamedCommand('help')
114 sys.exit(1) 133 sys.exit(1)
115 134
116 135
136 class GsutilFormatter(logging.Formatter):
137 """A logging.Formatter that supports logging microseconds (%f)."""
138
139 def formatTime(self, record, datefmt=None):
140 if datefmt:
141 return datetime.datetime.fromtimestamp(record.created).strftime(datefmt)
142
143 # Use default implementation if datefmt is not specified.
144 return super(GsutilFormatter, self).formatTime(record, datefmt=datefmt)
145
146
117 def _ConfigureLogging(level=logging.INFO): 147 def _ConfigureLogging(level=logging.INFO):
118 """Similar to logging.basicConfig() except it always adds a handler.""" 148 """Similar to logging.basicConfig() except it always adds a handler."""
149 log_format = '%(levelname)s %(asctime)s %(filename)s] %(message)s'
150 date_format = '%m%d %H:%M:%S.%f'
151 formatter = GsutilFormatter(fmt=log_format, datefmt=date_format)
119 handler = logging.StreamHandler() 152 handler = logging.StreamHandler()
153 handler.setFormatter(formatter)
120 root_logger = logging.getLogger() 154 root_logger = logging.getLogger()
121 root_logger.addHandler(handler) 155 root_logger.addHandler(handler)
122 root_logger.setLevel(level) 156 root_logger.setLevel(level)
123 157
124 158
125 def main(): 159 def main():
126 # These modules must be imported after importing gslib.__main__. 160 # Any modules used in initializing multiprocessing variables must be
161 # imported after importing gslib.__main__.
162 # pylint: disable=redefined-outer-name,g-import-not-at-top
163 import gslib.boto_translation
127 import gslib.command 164 import gslib.command
128 import gslib.util 165 import gslib.util
129 from gslib.third_party.oauth2_plugin import oauth2_client 166 from gslib.util import BOTO_IS_SECURE
167 from gslib.util import CERTIFICATE_VALIDATION_ENABLED
168 from gcs_oauth2_boto_plugin import oauth2_client
130 from gslib.util import MultiprocessingIsAvailable 169 from gslib.util import MultiprocessingIsAvailable
131 if MultiprocessingIsAvailable()[0]: 170 if MultiprocessingIsAvailable()[0]:
132 # These setup methods must be called, and, on Windows, they can only be 171 # These setup methods must be called, and, on Windows, they can only be
133 # called from within an "if __name__ == '__main__':" block. 172 # called from within an "if __name__ == '__main__':" block.
134 gslib.util.InitializeMultiprocessingVariables() 173 gslib.util.InitializeMultiprocessingVariables()
135 gslib.command.InitializeMultiprocessingVariables() 174 gslib.command.InitializeMultiprocessingVariables()
136 oauth2_client.InitializeMultiprocessingVariables() 175 gslib.boto_translation.InitializeMultiprocessingVariables()
176
177 # This needs to be done after gslib.util.InitializeMultiprocessingVariables(),
178 # since otherwise we can't call gslib.util.CreateLock.
179 try:
180 # pylint: disable=unused-import,g-import-not-at-top
181 import gcs_oauth2_boto_plugin
182 gcs_oauth2_boto_plugin.oauth2_helper.SetFallbackClientIdAndSecret(
183 GSUTIL_CLIENT_ID, GSUTIL_CLIENT_NOTSOSECRET)
184 gcs_oauth2_boto_plugin.oauth2_helper.SetLock(CreateLock())
185 except ImportError:
186 pass
137 187
138 global debug 188 global debug
189 global test_exception_traces
139 190
140 if not (2, 6) <= sys.version_info[:3] < (3,): 191 if not (2, 6) <= sys.version_info[:3] < (3,):
141 raise gslib.exception.CommandException( 192 raise gslib.exception.CommandException(
142 'gsutil requires python 2.6 or 2.7.') 193 'gsutil requires python 2.6 or 2.7.')
143 194
144 config_file_list = GetBotoConfigFileList() 195 # In gsutil 4.0 and beyond, we don't use the boto library for the JSON
145 command_runner = CommandRunner(config_file_list) 196 # API. However, we still store gsutil configuration data in the .boto
197 # config file for compatibility with previous versions and user convenience.
198 # Many users have a .boto configuration file from previous versions, and it
199 # is useful to have all of the configuration for gsutil stored in one place.
200 command_runner = CommandRunner()
201 if not BOTO_IS_SECURE:
202 raise CommandException('\n'.join(textwrap.wrap(
203 'Your boto configuration has is_secure = False. Gsutil cannot be '
204 'run this way, for security reasons.')))
205
146 headers = {} 206 headers = {}
147 parallel_operations = False 207 parallel_operations = False
148 quiet = False 208 quiet = False
149 version = False 209 version = False
150 debug = 0 210 debug = 0
211 test_exception_traces = False
151 212
152 # If user enters no commands just print the usage info. 213 # If user enters no commands just print the usage info.
153 if len(sys.argv) == 1: 214 if len(sys.argv) == 1:
154 sys.argv.append('help') 215 sys.argv.append('help')
155 216
156 # Change the default of the 'https_validate_certificates' boto option to 217 # Change the default of the 'https_validate_certificates' boto option to
157 # True (it is currently False in boto). 218 # True (it is currently False in boto).
158 if not boto.config.has_option('Boto', 'https_validate_certificates'): 219 if not boto.config.has_option('Boto', 'https_validate_certificates'):
159 if not boto.config.has_section('Boto'): 220 if not boto.config.has_section('Boto'):
160 boto.config.add_section('Boto') 221 boto.config.add_section('Boto')
161 boto.config.setbool('Boto', 'https_validate_certificates', True) 222 boto.config.setbool('Boto', 'https_validate_certificates', True)
162 223
163 # If ca_certificates_file is configured use it; otherwise configure boto to 224 GetCertsFile()
164 # use the cert roots distributed with gsutil.
165 if not boto.config.get_value('Boto', 'ca_certificates_file', None):
166 disk_certs_file = os.path.abspath(
167 os.path.join(gslib.GSLIB_DIR, 'data', 'cacerts.txt'))
168 if not os.path.exists(disk_certs_file):
169 # If the file is not present on disk, this means the gslib module doesn't
170 # actually exist on disk anywhere. This can happen if it's being imported
171 # from a zip file. Unfortunately, we have to copy the certs file to a
172 # local temp file on disk because the underlying SSL socket requires it
173 # to be a filesystem path.
174 certs_data = pkgutil.get_data('gslib', 'data/cacerts.txt')
175 if not certs_data:
176 raise gslib.exception.CommandException(
177 'Certificates file not found. Please reinstall gsutil from scratch')
178 fd, fname = tempfile.mkstemp(suffix='.txt', prefix='gsutil-cacerts')
179 f = os.fdopen(fd, 'w')
180 f.write(certs_data)
181 f.close()
182 disk_certs_file = fname
183 cleanup_files.append(disk_certs_file)
184 boto.config.set('Boto', 'ca_certificates_file', disk_certs_file)
185 225
186 try: 226 try:
187 try: 227 try:
188 opts, args = getopt.getopt(sys.argv[1:], 'dDvo:h:mq', 228 opts, args = getopt.getopt(sys.argv[1:], 'dDvo:h:mq',
189 ['debug', 'detailedDebug', 'version', 'option', 229 ['debug', 'detailedDebug', 'version', 'option',
190 'help', 'header', 'multithreaded', 'quiet']) 230 'help', 'header', 'multithreaded', 'quiet',
231 'testexceptiontraces'])
191 except getopt.GetoptError as e: 232 except getopt.GetoptError as e:
192 _HandleCommandException(gslib.exception.CommandException(e.msg)) 233 _HandleCommandException(gslib.exception.CommandException(e.msg))
193 for o, a in opts: 234 for o, a in opts:
194 if o in ('-d', '--debug'): 235 if o in ('-d', '--debug'):
195 # Passing debug=2 causes boto to include httplib header output. 236 # Passing debug=2 causes boto to include httplib header output.
196 debug = 2 237 debug = 3
197 elif o in ('-D', '--detailedDebug'): 238 elif o in ('-D', '--detailedDebug'):
198 # We use debug level 3 to ask gsutil code to output more detailed 239 # We use debug level 3 to ask gsutil code to output more detailed
199 # debug output. This is a bit of a hack since it overloads the same 240 # debug output. This is a bit of a hack since it overloads the same
200 # flag that was originally implemented for boto use. And we use -DD 241 # flag that was originally implemented for boto use. And we use -DD
201 # to ask for really detailed debugging (i.e., including HTTP payload). 242 # to ask for really detailed debugging (i.e., including HTTP payload).
202 if debug == 3: 243 if debug == 3:
203 debug = 4 244 debug = 4
204 else: 245 else:
205 debug = 3 246 debug = 3
206 elif o in ('-?', '--help'): 247 elif o in ('-?', '--help'):
207 _OutputUsageAndExit(command_runner) 248 _OutputUsageAndExit(command_runner)
208 elif o in ('-h', '--header'): 249 elif o in ('-h', '--header'):
209 (hdr_name, _, hdr_val) = a.partition(':') 250 (hdr_name, _, hdr_val) = a.partition(':')
210 if not hdr_name: 251 if not hdr_name:
211 _OutputUsageAndExit(command_runner) 252 _OutputUsageAndExit(command_runner)
212 headers[hdr_name.lower()] = hdr_val 253 headers[hdr_name.lower()] = hdr_val
213 elif o in ('-m', '--multithreaded'): 254 elif o in ('-m', '--multithreaded'):
214 parallel_operations = True 255 parallel_operations = True
215 elif o in ('-q', '--quiet'): 256 elif o in ('-q', '--quiet'):
216 quiet = True 257 quiet = True
217 elif o in ('-v', '--version'): 258 elif o in ('-v', '--version'):
218 version = True 259 version = True
260 elif o == '--testexceptiontraces': # Hidden flag for integration tests.
261 test_exception_traces = True
219 elif o in ('-o', '--option'): 262 elif o in ('-o', '--option'):
220 (opt_section_name, _, opt_value) = a.partition('=') 263 (opt_section_name, _, opt_value) = a.partition('=')
221 if not opt_section_name: 264 if not opt_section_name:
222 _OutputUsageAndExit(command_runner) 265 _OutputUsageAndExit(command_runner)
223 (opt_section, _, opt_name) = opt_section_name.partition(':') 266 (opt_section, _, opt_name) = opt_section_name.partition(':')
224 if not opt_section or not opt_name: 267 if not opt_section or not opt_name:
225 _OutputUsageAndExit(command_runner) 268 _OutputUsageAndExit(command_runner)
226 if not boto.config.has_section(opt_section): 269 if not boto.config.has_section(opt_section):
227 boto.config.add_section(opt_section) 270 boto.config.add_section(opt_section)
228 boto.config.set(opt_section, opt_name, opt_value) 271 boto.config.set(opt_section, opt_name, opt_value)
229 httplib2.debuglevel = debug 272 httplib2.debuglevel = debug
230 if debug > 1: 273 if debug > 1:
231 sys.stderr.write(DEBUG_WARNING) 274 sys.stderr.write(DEBUG_WARNING)
232 if debug == 2: 275 if debug >= 2:
233 _ConfigureLogging(level=logging.DEBUG)
234 elif debug > 2:
235 _ConfigureLogging(level=logging.DEBUG) 276 _ConfigureLogging(level=logging.DEBUG)
236 command_runner.RunNamedCommand('ver', ['-l']) 277 command_runner.RunNamedCommand('ver', ['-l'])
237 config_items = [] 278 config_items = []
238 try: 279 try:
239 config_items.extend(boto.config.items('Boto')) 280 config_items.extend(boto.config.items('Boto'))
240 config_items.extend(boto.config.items('GSUtil')) 281 config_items.extend(boto.config.items('GSUtil'))
241 except ConfigParser.NoSectionError: 282 except ConfigParser.NoSectionError:
242 pass 283 pass
243 sys.stderr.write('config_file_list: %s\n' % config_file_list) 284 for i in xrange(len(config_items)):
285 config_item_key = config_items[i][0]
286 if config_item_key in CONFIG_KEYS_TO_REDACT:
287 config_items[i] = (config_item_key, 'REDACTED')
288 sys.stderr.write('Command being run: %s\n' % ' '.join(sys.argv))
289 sys.stderr.write('config_file_list: %s\n' % GetBotoConfigFileList())
244 sys.stderr.write('config: %s\n' % str(config_items)) 290 sys.stderr.write('config: %s\n' % str(config_items))
245 elif quiet: 291 elif quiet:
246 _ConfigureLogging(level=logging.WARNING) 292 _ConfigureLogging(level=logging.WARNING)
247 else: 293 else:
248 _ConfigureLogging(level=logging.INFO) 294 _ConfigureLogging(level=logging.INFO)
249 # apiclient and oauth2client use info logging in places that would better 295 # apiclient and oauth2client use info logging in places that would better
250 # correspond to gsutil's debug logging (e.g., when refreshing access 296 # correspond to gsutil's debug logging (e.g., when refreshing access
251 # tokens). 297 # tokens).
252 oauth2client.client.logger.setLevel(logging.WARNING) 298 oauth2client.client.logger.setLevel(logging.WARNING)
253 apiclient.discovery.logger.setLevel(logging.WARNING)
254 299
255 is_secure = BOTO_IS_SECURE 300 if not CERTIFICATE_VALIDATION_ENABLED:
256 if not is_secure[0]:
257 sys.stderr.write(HTTP_WARNING) 301 sys.stderr.write(HTTP_WARNING)
258 302
259 if version: 303 if version:
260 command_name = 'version' 304 command_name = 'version'
261 elif not args: 305 elif not args:
262 command_name = 'help' 306 command_name = 'help'
263 else: 307 else:
264 command_name = args[0] 308 command_name = args[0]
265 309
266 # Unset http_proxy environment variable if it's set, because it confuses 310 # Unset http_proxy environment variable if it's set, because it confuses
267 # boto. (Proxies should instead be configured via the boto config file.) 311 # boto. (Proxies should instead be configured via the boto config file.)
268 if 'http_proxy' in os.environ: 312 if 'http_proxy' in os.environ:
269 if debug > 1: 313 if debug > 1:
270 sys.stderr.write( 314 sys.stderr.write(
271 'Unsetting http_proxy environment variable within gsutil run.\n') 315 'Unsetting http_proxy environment variable within gsutil run.\n')
272 del os.environ['http_proxy'] 316 del os.environ['http_proxy']
273 317
274 return _RunNamedCommandAndHandleExceptions( 318 return _RunNamedCommandAndHandleExceptions(
275 command_runner, command_name, args[1:], headers, debug, 319 command_runner, command_name, args=args[1:], headers=headers,
276 parallel_operations) 320 debug_level=debug, parallel_operations=parallel_operations)
277 finally: 321 finally:
278 _Cleanup() 322 _Cleanup()
279 323
280 324
281 def _HandleUnknownFailure(e): 325 def _HandleUnknownFailure(e):
282 # Called if we fall through all known/handled exceptions. Allows us to 326 # Called if we fall through all known/handled exceptions. Allows us to
283 # print a stacktrace if -D option used. 327 # print a stacktrace if -D option used.
284 if debug > 2: 328 if debug >= 2:
285 stack_trace = traceback.format_exc() 329 stack_trace = traceback.format_exc()
286 sys.stderr.write('DEBUG: Exception stack trace:\n %s\n' % 330 sys.stderr.write('DEBUG: Exception stack trace:\n %s\n' %
287 re.sub('\\n', '\n ', stack_trace)) 331 re.sub('\\n', '\n ', stack_trace))
288 else: 332 else:
289 _OutputAndExit('Failure: %s.' % e) 333 _OutputAndExit('Failure: %s.' % e)
290 334
291 335
292 def _HandleCommandException(e): 336 def _HandleCommandException(e):
293 if e.informational: 337 if e.informational:
294 _OutputAndExit(e.reason) 338 _OutputAndExit(e.reason)
295 else: 339 else:
296 _OutputAndExit('CommandException: %s' % e.reason) 340 _OutputAndExit('CommandException: %s' % e.reason)
297 341
298 342
343 # pylint: disable=unused-argument
299 def _HandleControlC(signal_num, cur_stack_frame): 344 def _HandleControlC(signal_num, cur_stack_frame):
300 """Called when user hits ^C so we can print a brief message instead of 345 """Called when user hits ^C.
301 the normal Python stack trace (unless -D option is used).""" 346
302 if debug > 2: 347 This function prints a brief message instead of the normal Python stack trace
348 (unless -D option is used).
349
350 Args:
351 signal_num: Signal that was caught.
352 cur_stack_frame: Unused.
353 """
354 if debug >= 2:
303 stack_trace = ''.join(traceback.format_list(traceback.extract_stack())) 355 stack_trace = ''.join(traceback.format_list(traceback.extract_stack()))
304 _OutputAndExit( 356 _OutputAndExit(
305 'DEBUG: Caught signal %d - Exception stack trace:\n' 357 'DEBUG: Caught signal %d - Exception stack trace:\n'
306 ' %s' % (signal_num, re.sub('\\n', '\n ', stack_trace))) 358 ' %s' % (signal_num, re.sub('\\n', '\n ', stack_trace)))
307 else: 359 else:
308 _OutputAndExit('Caught signal %d - exiting' % signal_num) 360 _OutputAndExit('Caught signal %d - exiting' % signal_num)
309 361
310 362
311 def _HandleSigQuit(signal_num, cur_stack_frame): 363 def _HandleSigQuit(signal_num, cur_stack_frame):
312 """Called when user hits ^\\, so we can force breakpoint a running gsutil.""" 364 """Called when user hits ^\\, so we can force breakpoint a running gsutil."""
313 import pdb 365 import pdb # pylint: disable=g-import-not-at-top
314 pdb.set_trace() 366 pdb.set_trace()
315 367
316 def _ConstructAclHelp(default_project_id):
317 acct_help_part_1 = (
318 """Your request resulted in an AccountProblem (403) error. Usually this happens
319 if you attempt to create a bucket or upload an object without having first
320 enabled billing for the project you are using. To remedy this problem, please do
321 the following:
322 368
323 1. Navigate to the https://cloud.google.com/console#/project, click on the 369 def _ConstructAccountProblemHelp(reason):
324 project you will use, and then copy the Project Number listed under that 370 """Constructs a help string for an access control error.
325 project.
326 371
327 """) 372 Args:
328 acct_help_part_2 = '\n' 373 reason: e.reason string from caught exception.
374
375 Returns:
376 Contructed help text.
377 """
378 default_project_id = boto.config.get_value('GSUtil', 'default_project_id')
379 # pylint: disable=line-too-long, g-inconsistent-quotes
380 acct_help = (
381 "Your request resulted in an AccountProblem (403) error. Usually this "
382 "happens if you attempt to create a bucket without first having "
383 "enabled billing for the project you are using. Please ensure billing is "
384 "enabled for your project by following the instructions at "
385 "`Google Developers Console<https://developers.google.com/console/help/bil ling>`. ")
329 if default_project_id: 386 if default_project_id:
330 acct_help_part_2 = ( 387 acct_help += (
331 """2. Click "Google Cloud Storage" on the left hand pane, and then check that 388 "In the project overview, ensure that the Project Number listed for "
332 the value listed for "x-goog-project-id" on this page matches the project ID 389 "your project matches the project ID (%s) from your boto config file. "
333 (%s) from your boto config file. 390 % default_project_id)
391 acct_help += (
392 "If the above doesn't resolve your AccountProblem, please send mail to "
393 "gs-team@google.com requesting assistance, noting the exact command you "
394 "ran, the fact that you received a 403 AccountProblem error, and your "
395 "project ID. Please do not post your project ID on StackOverflow. "
396 "Note: It's possible to use Google Cloud Storage without enabling "
397 "billing if you're only listing or reading objects for which you're "
398 "authorized, or if you're uploading objects to a bucket billed to a "
399 "project that has billing enabled. But if you're attempting to create "
400 "buckets or upload objects to a bucket owned by your own project, you "
401 "must first enable billing for that project.")
402 return acct_help
334 403
335 """ % default_project_id)
336 acct_help_part_3 = (
337 """Check whether there's an "!" next to Billing. If so, click Billing and then
338 enable billing for this project. Note that it can take up to one hour after
339 enabling billing for the project to become activated for creating buckets and
340 uploading objects.
341 404
342 If the above doesn't resolve your AccountProblem, please send mail to 405 def _CheckAndHandleCredentialException(e, args):
343 gs-team@google.com requesting assistance, noting the exact command you ran, the 406 # Provide detail to users who have no boto config file (who might previously
344 fact that you received a 403 AccountProblem error, and your project ID. Please 407 # have been using gsutil only for accessing publicly readable buckets and
345 do not post your project ID on StackOverflow. 408 # objects).
409 # pylint: disable=g-import-not-at-top
410 from gslib.util import HasConfiguredCredentials
411 if (not HasConfiguredCredentials() and
412 not boto.config.get_value('Tests', 'bypass_anonymous_access_warning',
413 False)):
414 # The check above allows tests to assert that we get a particular,
415 # expected failure, rather than always encountering this error message
416 # when there are no configured credentials. This allows tests to
417 # simulate a second user without permissions, without actually requiring
418 # two separate configured users.
419 _OutputAndExit('\n'.join(textwrap.wrap(
420 'You are attempting to access protected data with no configured '
421 'credentials. Please visit '
422 'https://cloud.google.com/console#/project and sign up for an '
423 'account, and then run the "gsutil config" command to configure '
424 'gsutil to use these credentials.')))
425 elif (e.reason and
426 (e.reason == 'AccountProblem' or e.reason == 'Account disabled.' or
427 'account for the specified project has been disabled' in e.reason)
428 and ','.join(args).find('gs://') != -1):
429 _OutputAndExit('\n'.join(textwrap.wrap(
430 _ConstructAccountProblemHelp(e.reason))))
346 431
347 Note: It's possible to use Google Cloud Storage without enabling billing if
348 you're only listing or reading objects for which you're authorized, or if
349 you're uploading objects to a bucket billed to a project that has billing
350 enabled. But if you're attempting to create buckets or upload objects to a
351 bucket owned by your own project, you must first enable billing for that
352 project.""")
353 return (acct_help_part_1, acct_help_part_2, acct_help_part_3)
354 432
355 def _RunNamedCommandAndHandleExceptions(command_runner, command_name, args=None, 433 def _RunNamedCommandAndHandleExceptions(command_runner, command_name, args=None,
356 headers=None, debug=0, 434 headers=None, debug_level=0,
357 parallel_operations=False): 435 parallel_operations=False):
436 """Runs the command with the given command runner and arguments."""
437 # pylint: disable=g-import-not-at-top
438 from gslib.util import GetConfigFilePath
439 from gslib.util import IS_WINDOWS
440 from gslib.util import IsRunningInteractively
358 try: 441 try:
359 # Catch ^C so we can print a brief message instead of the normal Python 442 # Catch ^C so we can print a brief message instead of the normal Python
360 # stack trace. 443 # stack trace.
361 signal.signal(signal.SIGINT, _HandleControlC) 444 signal.signal(signal.SIGINT, _HandleControlC)
362 # Catch ^\ so we can force a breakpoint in a running gsutil. 445 # Catch ^\ so we can force a breakpoint in a running gsutil.
363 if not util.IS_WINDOWS: 446 if not IS_WINDOWS:
364 signal.signal(signal.SIGQUIT, _HandleSigQuit) 447 signal.signal(signal.SIGQUIT, _HandleSigQuit)
365 return command_runner.RunNamedCommand(command_name, args, headers, debug, 448 return command_runner.RunNamedCommand(command_name, args, headers,
366 parallel_operations) 449 debug_level, parallel_operations)
367 except AttributeError as e: 450 except AttributeError as e:
368 if str(e).find('secret_access_key') != -1: 451 if str(e).find('secret_access_key') != -1:
369 _OutputAndExit('Missing credentials for the given URI(s). Does your ' 452 _OutputAndExit('Missing credentials for the given URI(s). Does your '
370 'boto config file contain all needed credentials?') 453 'boto config file contain all needed credentials?')
371 else: 454 else:
372 _OutputAndExit(str(e)) 455 _OutputAndExit(str(e))
373 except boto.exception.StorageDataError as e:
374 _OutputAndExit('StorageDataError: %s.' % e.reason)
375 except boto.exception.BotoClientError as e:
376 _OutputAndExit('BotoClientError: %s.' % e.reason)
377 except gslib.exception.CommandException as e: 456 except gslib.exception.CommandException as e:
378 _HandleCommandException(e) 457 _HandleCommandException(e)
379 except getopt.GetoptError as e: 458 except getopt.GetoptError as e:
380 _HandleCommandException(gslib.exception.CommandException(e.msg)) 459 _HandleCommandException(gslib.exception.CommandException(e.msg))
381 except boto.exception.InvalidAclError as e:
382 _OutputAndExit('InvalidAclError: %s.' % str(e))
383 except boto.exception.InvalidUriError as e: 460 except boto.exception.InvalidUriError as e:
384 _OutputAndExit('InvalidUriError: %s.' % e.message) 461 _OutputAndExit('InvalidUriError: %s.' % e.message)
385 except gslib.exception.ProjectIdException as e: 462 except gslib.exception.InvalidUrlError as e:
386 _OutputAndExit('ProjectIdException: %s.' % e.reason) 463 _OutputAndExit('InvalidUrlError: %s.' % e.message)
387 except boto.auth_handler.NotReadyToAuthenticate: 464 except boto.auth_handler.NotReadyToAuthenticate:
388 _OutputAndExit('NotReadyToAuthenticate') 465 _OutputAndExit('NotReadyToAuthenticate')
389 except OSError as e: 466 except OSError as e:
390 _OutputAndExit('OSError: %s.' % e.strerror) 467 _OutputAndExit('OSError: %s.' % e.strerror)
391 except IOError as e: 468 except IOError as e:
392 if e.errno == errno.EPIPE and not IsRunningInteractively(): 469 if (e.errno == errno.EPIPE or (IS_WINDOWS and e.errno == errno.EINVAL)
470 and not IsRunningInteractively()):
393 # If we get a pipe error, this just means that the pipe to stdout or 471 # If we get a pipe error, this just means that the pipe to stdout or
394 # stderr is broken. This can happen if the user pipes gsutil to a command 472 # stderr is broken. This can happen if the user pipes gsutil to a command
395 # that doesn't use the entire output stream. Instead of raising an error, 473 # that doesn't use the entire output stream. Instead of raising an error,
396 # just swallow it up and exit cleanly. 474 # just swallow it up and exit cleanly.
397 sys.exit(0) 475 sys.exit(0)
398 else: 476 else:
399 raise 477 raise
400 except wildcard_iterator.WildcardException as e: 478 except wildcard_iterator.WildcardException as e:
401 _OutputAndExit(e.reason) 479 _OutputAndExit(e.reason)
402 except boto.exception.StorageResponseError as e: 480 except ProjectIdException as e:
403 # Check for access denied, and provide detail to users who have no boto 481 _OutputAndExit(
404 # config file (who might previously have been using gsutil only for 482 'You are attempting to perform an operation that requires a '
405 # accessing publicly readable buckets and objects). 483 'project id, with none configured. Please re-run '
406 if (e.status == 403 484 'gsutil config and make sure to follow the instructions for '
407 or (e.status == 400 and e.code == 'MissingSecurityHeader')): 485 'finding and entering your default project id.')
408 _, _, detail = util.ParseErrorDetail(e) 486 except BadRequestException as e:
409 if detail and detail.find('x-goog-project-id header is required') != -1: 487 if e.reason == 'MissingSecurityHeader':
410 _OutputAndExit('\n'.join(textwrap.wrap( 488 _CheckAndHandleCredentialException(e, args)
411 'You are attempting to perform an operation that requires an ' 489 _OutputAndExit(e)
412 'x-goog-project-id header, with none configured. Please re-run ' 490 except AccessDeniedException as e:
413 'gsutil config and make sure to follow the instructions for ' 491 _CheckAndHandleCredentialException(e, args)
414 'finding and entering your default project id.'))) 492 _OutputAndExit(e)
415 if (not HasConfiguredCredentials() and 493 except ArgumentException as e:
416 not boto.config.get_value('Tests', 'bypass_anonymous_access_warning', 494 _OutputAndExit(e)
417 False)): 495 except ServiceException as e:
418 # The check above allows tests to assert that we get a particular, 496 _OutputAndExit(e)
419 # expected failure, rather than always encountering this error message 497 except apitools_exceptions.HttpError as e:
420 # when there are no configured credentials. This allows tests to 498 # These should usually be retried by the underlying implementation or
421 # simulate a second user without permissions, without actually requiring 499 # wrapped by CloudApi ServiceExceptions, but if we do get them,
422 # two separate configured users. 500 # print something useful.
423 _OutputAndExit('\n'.join(textwrap.wrap( 501 _OutputAndExit('HttpError: %s, %s' % (getattr(e.response, 'status', ''),
424 'You are attempting to access protected data with no configured ' 502 e.content or ''))
425 'credentials. Please visit '
426 'https://cloud.google.com/console#/project and sign up for an '
427 'account, and then run the "gsutil config" command to configure '
428 'gsutil to use these credentials.')))
429 elif (e.error_code == 'AccountProblem'
430 and ','.join(args).find('gs://') != -1):
431 default_project_id = boto.config.get_value('GSUtil',
432 'default_project_id')
433 (acct_help_part_1, acct_help_part_2, acct_help_part_3) = (
434 _ConstructAclHelp(default_project_id))
435 if default_project_id:
436 _OutputAndExit(acct_help_part_1 + acct_help_part_2 + '3. ' +
437 acct_help_part_3)
438 else:
439 _OutputAndExit(acct_help_part_1 + '2. ' + acct_help_part_3)
440
441 exc_name, message, detail = util.ParseErrorDetail(e)
442 _OutputAndExit(util.FormatErrorMessage(
443 exc_name, e.status, e.code, e.reason, message, detail))
444 except boto.exception.ResumableUploadException as e:
445 _OutputAndExit('ResumableUploadException: %s.' % e.message)
446 except socket.error as e: 503 except socket.error as e:
447 if e.args[0] == errno.EPIPE: 504 if e.args[0] == errno.EPIPE:
448 # Retrying with a smaller file (per suggestion below) works because 505 # Retrying with a smaller file (per suggestion below) works because
449 # the library code send loop (in boto/s3/key.py) can get through the 506 # the library code send loop (in boto/s3/key.py) can get through the
450 # entire file and then request the HTTP response before the socket 507 # entire file and then request the HTTP response before the socket
451 # gets closed and the response lost. 508 # gets closed and the response lost.
452 message = ( 509 _OutputAndExit(
453 """ 510 'Got a "Broken pipe" error. This can happen to clients using Python '
454 Got a "Broken pipe" error. This can happen to clients using Python 2.x, 511 '2.x, when the server sends an error response and then closes the '
455 when the server sends an error response and then closes the socket (see 512 'socket (see http://bugs.python.org/issue5542). If you are trying to '
456 http://bugs.python.org/issue5542). If you are trying to upload a large 513 'upload a large object you might retry with a small (say 200k) '
457 object you might retry with a small (say 200k) object, and see if you get 514 'object, and see if you get a more specific error code.'
458 a more specific error code. 515 )
459 """)
460 _OutputAndExit(message)
461 else: 516 else:
462 _HandleUnknownFailure(e) 517 _HandleUnknownFailure(e)
463 except Exception as e: 518 except Exception as e:
464 # Check for two types of errors related to service accounts. These errors 519 # Check for two types of errors related to service accounts. These errors
465 # appear to be the same except for their messages, but they are caused by 520 # appear to be the same except for their messages, but they are caused by
466 # different problems and both have unhelpful error messages. Moreover, 521 # different problems and both have unhelpful error messages. Moreover,
467 # the error type belongs to PyOpenSSL, which is not necessarily installed. 522 # the error type belongs to PyOpenSSL, which is not necessarily installed.
468 if 'mac verify failure' in str(e): 523 if 'mac verify failure' in str(e):
469 _OutputAndExit("Encountered an error while refreshing access token." + 524 _OutputAndExit(
470 " If you are using a service account,\nplease verify that the " + 525 'Encountered an error while refreshing access token. '
471 "gs_service_key_file_password field in your config file," + 526 'If you are using a service account,\nplease verify that the '
472 "\n%s, is correct." % GetConfigFilePath()) 527 'gs_service_key_file_password field in your config file,'
528 '\n%s, is correct.' % GetConfigFilePath())
473 elif 'asn1 encoding routines' in str(e): 529 elif 'asn1 encoding routines' in str(e):
474 _OutputAndExit("Encountered an error while refreshing access token." + 530 _OutputAndExit(
475 " If you are using a service account,\nplease verify that the " + 531 'Encountered an error while refreshing access token. '
476 "gs_service_key_file field in your config file,\n%s, is correct." 532 'If you are using a service account,\nplease verify that the '
533 'gs_service_key_file field in your config file,\n%s, is correct.'
477 % GetConfigFilePath()) 534 % GetConfigFilePath())
478 _HandleUnknownFailure(e) 535 _HandleUnknownFailure(e)
479 536
480 537
481 if __name__ == '__main__': 538 if __name__ == '__main__':
482 sys.exit(main()) 539 sys.exit(main())
OLDNEW
« no previous file with comments | « gslib/__init__.py ('k') | gslib/aclhelpers.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698