OLD | NEW |
| (Empty) |
1 # Copyright 2011 Google Inc. All Rights Reserved. | |
2 # | |
3 # Licensed under the Apache License, Version 2.0 (the "License"); | |
4 # you may not use this file except in compliance with the License. | |
5 # You may obtain a copy of the License at | |
6 # | |
7 # http://www.apache.org/licenses/LICENSE-2.0 | |
8 # | |
9 # Unless required by applicable law or agreed to in writing, software | |
10 # distributed under the License is distributed on an "AS IS" BASIS, | |
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 # See the License for the specific language governing permissions and | |
13 # limitations under the License. | |
14 | |
15 import boto | |
16 import datetime | |
17 import multiprocessing | |
18 import platform | |
19 import os | |
20 import signal | |
21 import sys | |
22 import time | |
23 import webbrowser | |
24 | |
25 from boto.provider import Provider | |
26 from gslib.command import Command | |
27 from gslib.command import COMMAND_NAME | |
28 from gslib.command import COMMAND_NAME_ALIASES | |
29 from gslib.command import CONFIG_REQUIRED | |
30 from gslib.command import FILE_URIS_OK | |
31 from gslib.command import MAX_ARGS | |
32 from gslib.command import MIN_ARGS | |
33 from gslib.command import PROVIDER_URIS_OK | |
34 from gslib.command import SUPPORTED_SUB_ARGS | |
35 from gslib.command import URIS_START_ARG | |
36 from gslib.exception import AbortException | |
37 from gslib.exception import CommandException | |
38 from gslib.help_provider import HELP_NAME | |
39 from gslib.help_provider import HELP_NAME_ALIASES | |
40 from gslib.help_provider import HELP_ONE_LINE_SUMMARY | |
41 from gslib.help_provider import HELP_TEXT | |
42 from gslib.help_provider import HelpType | |
43 from gslib.help_provider import HELP_TYPE | |
44 from gslib.util import HAVE_OAUTH2 | |
45 from gslib.util import TWO_MB | |
46 | |
47 _detailed_help_text = (""" | |
48 <B>SYNOPSIS</B> | |
49 gsutil [-D] config [-a] [-b] [-f] [-o <file>] [-r] [-s <scope>] [-w] | |
50 | |
51 | |
52 <B>DESCRIPTION</B> | |
53 The gsutil config command obtains access credentials for Google Cloud | |
54 Storage and writes a boto/gsutil configuration file containing the obtained | |
55 credentials along with a number of other configuration-controllable values. | |
56 | |
57 Unless specified otherwise (see OPTIONS), the configuration file is written | |
58 to ~/.boto (i.e., the file .boto under the user's home directory). If the | |
59 default file already exists, an attempt is made to rename the existing file | |
60 to ~/.boto.bak; if that attempt fails the command will exit. A different | |
61 destination file can be specified with the -o option (see OPTIONS). | |
62 | |
63 Because the boto configuration file contains your credentials you should | |
64 keep its file permissions set so no one but you has read access. (The file | |
65 is created read-only when you run gsutil config.) | |
66 | |
67 | |
68 <B>CREDENTIALS</B> | |
69 By default gsutil config obtains OAuth2 credentials, and writes them | |
70 to the [Credentials] section of the configuration file. The -r, -w, | |
71 -f options (see OPTIONS below) cause gsutil config to request a token | |
72 with restricted scope; the resulting token will be restricted to read-only | |
73 operations, read-write operation, or all operations (including getacl/setacl/ | |
74 getdefacl/setdefacl/disablelogging/enablelogging/getlogging operations). In | |
75 addition, -s <scope> can be used to request additional (non-Google-Storage) | |
76 scopes. | |
77 | |
78 If you want to use credentials based on access key and secret (the older | |
79 authentication method before OAuth2 was supported) instead of OAuth2, | |
80 see help about the -a option in the OPTIONS section. | |
81 | |
82 If you wish to use gsutil with other providers (or to copy data back and | |
83 forth between multiple providers) you can edit their credentials into the | |
84 [Credentials] section after creating the initial configuration file. | |
85 | |
86 | |
87 <B>CONFIGURATION FILE SELECTION PROCEDURE</B> | |
88 By default, gsutil will look for the configuration file in /etc/boto.cfg and | |
89 ~/.boto. You can override this choice by setting the BOTO_CONFIG environment | |
90 variable. This is also useful if you have several different identities or | |
91 cloud storage environments: By setting up the credentials and any additional | |
92 configuration in separate files for each, you can switch environments by | |
93 changing environment variables. | |
94 | |
95 You can also set up a path of configuration files, by setting the BOTO_PATH | |
96 environment variable to contain a ":" delimited path. For example setting | |
97 the BOTO_PATH environment variable to: | |
98 | |
99 /etc/projects/my_group_project.boto.cfg:/home/mylogin/.boto | |
100 | |
101 will cause gsutil to load each configuration file found in the path in | |
102 order. This is useful if you want to set up some shared configuration | |
103 state among many users: The shared state can go in the central shared file | |
104 ( /etc/projects/my_group_project.boto.cfg) and each user's individual | |
105 credentials can be placed in the configuration file in each of their home | |
106 directories. (For security reasons users should never share credentials | |
107 via a shared configuration file.) | |
108 | |
109 | |
110 <B>CONFIGURATION FILE STRUCTURE</B> | |
111 The configuration file contains a number of sections: [Credentials], | |
112 [Boto], [GSUtil], and [OAuth2]. If you edit the file make sure to edit the | |
113 appropriate section (discussed below), and to be careful not to mis-edit | |
114 any of the setting names (like "gs_access_key_id") and not to remove the | |
115 section delimiters (like "[Credentials]"). | |
116 | |
117 | |
118 <B>ADDITIONAL CONFIGURATION-CONTROLLABLE FEATURES</B> | |
119 With the exception of setting up gsutil to work through a proxy (see | |
120 below), most users won't need to edit values in the boto configuration file; | |
121 values found in there tend to be of more specialized use than command line | |
122 option-controllable features. | |
123 | |
124 The following are the currently defined configuration settings, broken | |
125 down by section. Their use is documented in comments preceding each, in | |
126 the configuration file. If you see a setting you want to change that's not | |
127 listed in your current file, see the section below on Updating to the Latest | |
128 Configuration File. | |
129 | |
130 The currently supported settings, are, by section: | |
131 [Boto] | |
132 proxy | |
133 proxy_port | |
134 proxy_user | |
135 proxy_pass | |
136 is_secure | |
137 https_validate_certificates | |
138 send_crlf_after_proxy_auth_headers | |
139 debug | |
140 num_retries | |
141 | |
142 [GSUtil] | |
143 resumable_threshold | |
144 resumable_tracker_dir | |
145 parallel_process_count | |
146 parallel_thread_count | |
147 default_api_version | |
148 default_project_id | |
149 use_magicfile | |
150 | |
151 [OAuth2] | |
152 token_cache | |
153 token_cache | |
154 client_id | |
155 client_secret | |
156 provider_label | |
157 provider_authorization_uri | |
158 provider_token_uri | |
159 | |
160 | |
161 <B>UPDATING TO THE LATEST CONFIGURATION FILE</B> | |
162 We add new configuration controllable features to the boto configuration file | |
163 over time, but most gsutil users create a configuration file once and then | |
164 keep it for a long time, so new features aren't apparent when you update | |
165 to a newer version of gsutil. If you want to get the latest configuration | |
166 file (which includes all the latest settings and documentation about each) | |
167 you can rename your current file (e.g., to '.boto_old'), run gsutil config, | |
168 and then edit any configuration settings you wanted from your old file | |
169 into the newly created file. Note, however, that if you're using OAuth2 | |
170 credentials and you go back through the OAuth2 configuration dialog it will | |
171 invalidate your previous OAuth2 credentials. | |
172 | |
173 If no explicit scope option is given, -f (full control) is assumed by default. | |
174 | |
175 | |
176 <B>OPTIONS</B> | |
177 -a Prompt for Google Cloud Storage access key and secret (the older | |
178 authentication method before OAuth2 was supported) instead of | |
179 obtaining an OAuth2 token. | |
180 | |
181 -b Causes gsutil config to launch a browser to obtain OAuth2 approval | |
182 and the project ID instead of showing the URL for each and asking | |
183 the user to open the browser. This will probably not work as | |
184 expected if you are running gsutil from an ssh window, or using | |
185 gsutil on Windows. | |
186 | |
187 -f Request token with full-control access (default). | |
188 | |
189 -o <file> Write the configuration to <file> instead of ~/.boto. | |
190 Use '-' for stdout. | |
191 | |
192 -r Request token restricted to read-only access. | |
193 | |
194 -s <scope> Request additional OAuth2 <scope>. | |
195 | |
196 -w Request token restricted to read-write access. | |
197 """) | |
198 | |
199 | |
200 try: | |
201 from oauth2_plugin import oauth2_helper | |
202 except ImportError: | |
203 pass | |
204 | |
205 GOOG_API_CONSOLE_URI = 'http://code.google.com/apis/console' | |
206 | |
207 SCOPE_FULL_CONTROL = 'https://www.googleapis.com/auth/devstorage.full_control' | |
208 SCOPE_READ_WRITE = 'https://www.googleapis.com/auth/devstorage.read_write' | |
209 SCOPE_READ_ONLY = 'https://www.googleapis.com/auth/devstorage.read_only' | |
210 | |
211 CONFIG_PRELUDE_CONTENT = """ | |
212 # This file contains credentials and other configuration information needed | |
213 # by the boto library, used by gsutil. You can edit this file (e.g., to add | |
214 # credentials) but be careful not to mis-edit any of the variable names (like | |
215 # "gs_access_key_id") or remove important markers (like the "[Credentials]" and | |
216 # "[Boto]" section delimiters). | |
217 # | |
218 """ | |
219 | |
220 # Default number of OS processes and Python threads for parallel operations. | |
221 # On Linux systems we automatically scale the number of processes to match | |
222 # the underlying CPU/core count. Given we'll be running multiple concurrent | |
223 # processes on a typical multi-core Linux computer, to avoid being too | |
224 # aggressive with resources, the default number of threads is reduced from | |
225 # the previous value of 24 to 10. | |
226 # On Windows and Mac systems parallel multi-processing and multi-threading | |
227 # in Python presents various challenges so we retain compatibility with | |
228 # the established parallel mode operation, i.e. one process and 24 threads. | |
229 if platform.system() == 'Linux': | |
230 DEFAULT_PARALLEL_PROCESS_COUNT = multiprocessing.cpu_count() | |
231 DEFAULT_PARALLEL_THREAD_COUNT = 10 | |
232 else: | |
233 DEFAULT_PARALLEL_PROCESS_COUNT = 1 | |
234 DEFAULT_PARALLEL_THREAD_COUNT = 24 | |
235 | |
236 CONFIG_BOTO_SECTION_CONTENT = """ | |
237 [Boto] | |
238 | |
239 # To use a proxy, edit and uncomment the proxy and proxy_port lines. If you | |
240 # need a user/password with this proxy, edit and uncomment those lines as well. | |
241 #proxy = <proxy host> | |
242 #proxy_port = <proxy port> | |
243 #proxy_user = <your proxy user name> | |
244 #proxy_pass = <your proxy password> | |
245 | |
246 # The following two options control the use of a secure transport for requests | |
247 # to S3 and Google Cloud Storage. It is highly recommended to set both options | |
248 # to True in production environments, especially when using OAuth2 bearer token | |
249 # authentication with Google Cloud Storage. | |
250 | |
251 # Set 'is_secure' to False to cause boto to connect using HTTP instead of the | |
252 # default HTTPS. This is useful if you want to capture/analyze traffic | |
253 # (e.g., with tcpdump). This option should always be set to True in production | |
254 # environments. | |
255 #is_secure = False | |
256 | |
257 # Set 'https_validate_certificates' to False to disable server certificate | |
258 # checking. The default for this option in the boto library is currently | |
259 # 'False' (to avoid breaking apps that depend on invalid certificates); it is | |
260 # therefore strongly recommended to always set this option explicitly to True | |
261 # in configuration files, to protect against "man-in-the-middle" attacks. | |
262 https_validate_certificates = True | |
263 | |
264 # Set 'send_crlf_after_proxy_auth_headers' to True if you encounter problems | |
265 # tunneling HTTPS through a proxy. Users who don't have a proxy in the path | |
266 # to Google Cloud Storage don't need to touch this. Users who use a proxy will | |
267 # probably find that the default behavior (flag value False) works. If | |
268 # you encounter an error like "EOF occurred in violation of protocol" while | |
269 # trying to use gsutil through your proxy, try setting this flag to True. We | |
270 # (gs-team@google.com) would be interested to hear from you if you need to set | |
271 # this, including the make and version of the proxy server you are using. | |
272 #send_crlf_after_proxy_auth_headers = False | |
273 | |
274 # 'debug' controls the level of debug messages printed: 0 for none, 1 | |
275 # for basic boto debug, 2 for all boto debug plus HTTP requests/responses. | |
276 # Note: 'gsutil -d' sets debug to 2 for that one command run. | |
277 #debug = <0, 1, or 2> | |
278 | |
279 # 'num_retries' controls the number of retry attempts made when errors occur. | |
280 # The default is 6. Note: don't set this value to 0, as it will cause boto to | |
281 # fail when reusing HTTP connections. | |
282 #num_retries = <integer value> | |
283 """ | |
284 | |
285 CONFIG_INPUTLESS_GSUTIL_SECTION_CONTENT = """ | |
286 [GSUtil] | |
287 | |
288 # 'resumable_threshold' specifies the smallest file size [bytes] for which | |
289 # resumable Google Cloud Storage transfers are attempted. The default is 2097152 | |
290 # (2 MiB). | |
291 #resumable_threshold = %(resumable_threshold)d | |
292 | |
293 # 'resumable_tracker_dir' specifies the base location where resumable | |
294 # transfer tracker files are saved. By default they're in ~/.gsutil | |
295 #resumable_tracker_dir = <file path> | |
296 | |
297 # 'parallel_process_count' and 'parallel_thread_count' specify the number | |
298 # of OS processes and Python threads, respectively, to use when executing | |
299 # operations in parallel. The default settings should work well as configured, | |
300 # however, to enhance performance for transfers involving large numbers of | |
301 # files, you may experiment with hand tuning these values to optimize | |
302 # performance for your particular system configuration. | |
303 # MacOS and Windows users should see | |
304 # http://code.google.com/p/gsutil/issues/detail?id=78 before attempting | |
305 # to experiment with these values. | |
306 #parallel_process_count = %(parallel_process_count)d | |
307 #parallel_thread_count = %(parallel_thread_count)d | |
308 | |
309 # 'use_magicfile' specifies if the 'file --mime-type <filename>' command should | |
310 # be used to guess content types instead of the default filename extension-based | |
311 # mechanism. Available on UNIX and MacOS (and possibly on Windows, if you're | |
312 # running Cygwin or some other package that provides implementations of | |
313 # UNIX-like commands). When available and enabled use_magicfile should be more | |
314 # robust because it analyzes file contents in addition to extensions. | |
315 #use_magicfile = False | |
316 | |
317 # 'content_language' specifies the ISO 639-1 language code of the content, to be | |
318 # passed in the Content-Language header. By default no Content-Language is sent. | |
319 # See the ISO 631-1 column of | |
320 # http://www.loc.gov/standards/iso639-2/php/code_list.php for a list of | |
321 # language codes. | |
322 content_language = en | |
323 """ % {'resumable_threshold': TWO_MB, | |
324 'parallel_process_count': DEFAULT_PARALLEL_PROCESS_COUNT, | |
325 'parallel_thread_count': DEFAULT_PARALLEL_THREAD_COUNT} | |
326 | |
327 CONFIG_OAUTH2_CONFIG_CONTENT = """ | |
328 [OAuth2] | |
329 # This section specifies options used with OAuth2 authentication. | |
330 | |
331 # 'token_cache' specifies how the OAuth2 client should cache access tokens. | |
332 # Valid values are: | |
333 # 'in_memory': an in-memory cache is used. This is only useful if the boto | |
334 # client instance (and with it the OAuth2 plugin instance) persists | |
335 # across multiple requests. | |
336 # 'file_system' : access tokens will be cached in the file system, in files | |
337 # whose names include a key derived from the refresh token the access token | |
338 # based on. | |
339 # The default is 'file_system'. | |
340 #token_cache = file_system | |
341 #token_cache = in_memory | |
342 | |
343 # 'token_cache_path_pattern' specifies a path pattern for token cache files. | |
344 # This option is only relevant if token_cache = file_system. | |
345 # The value of this option should be a path, with place-holders '%(key)s' (which | |
346 # will be replaced with a key derived from the refresh token the cached access | |
347 # token was based on), and (optionally), %(uid)s (which will be replaced with | |
348 # the UID of the current user, if available via os.getuid()). | |
349 # Note that the config parser itself interpolates '%' placeholders, and hence | |
350 # the above placeholders need to be escaped as '%%(key)s'. | |
351 # The default value of this option is | |
352 # token_cache_path_pattern = <tmpdir>/oauth2client-tokencache.%%(uid)s.%%(key)s | |
353 # where <tmpdir> is the system-dependent default temp directory. | |
354 | |
355 # The following options specify the OAuth2 client identity and secret that is | |
356 # used when requesting and using OAuth2 tokens. If not specified, a default | |
357 # OAuth2 client for the gsutil tool is used; for uses of the boto library (with | |
358 # OAuth2 authentication plugin) in other client software, it is recommended to | |
359 # use a tool/client-specific OAuth2 client. For more information on OAuth2, see | |
360 # http://code.google.com/apis/accounts/docs/OAuth2.html | |
361 #client_id = <OAuth2 client id> | |
362 #client_secret = <OAuth2 client secret> | |
363 | |
364 # The following options specify the label and endpoint URIs for the OAUth2 | |
365 # authorization provider being used. Primarily useful for tool developers. | |
366 #provider_label = Google | |
367 #provider_authorization_uri = https://accounts.google.com/o/oauth2/auth | |
368 #provider_token_uri = https://accounts.google.com/o/oauth2/token | |
369 """ | |
370 | |
371 class ConfigCommand(Command): | |
372 """Implementation of gsutil config command.""" | |
373 | |
374 # Command specification (processed by parent class). | |
375 command_spec = { | |
376 # Name of command. | |
377 COMMAND_NAME : 'config', | |
378 # List of command name aliases. | |
379 COMMAND_NAME_ALIASES : ['cfg', 'conf', 'configure'], | |
380 # Min number of args required by this command. | |
381 MIN_ARGS : 0, | |
382 # Max number of args required by this command, or NO_MAX. | |
383 MAX_ARGS : 0, | |
384 # Getopt-style string specifying acceptable sub args. | |
385 SUPPORTED_SUB_ARGS : 'habfwrs:o:', | |
386 # True if file URIs acceptable for this command. | |
387 FILE_URIS_OK : False, | |
388 # True if provider-only URIs acceptable for this command. | |
389 PROVIDER_URIS_OK : False, | |
390 # Index in args of first URI arg. | |
391 URIS_START_ARG : 0, | |
392 # True if must configure gsutil before running command. | |
393 CONFIG_REQUIRED : False, | |
394 } | |
395 help_spec = { | |
396 # Name of command or auxiliary help info for which this help applies. | |
397 HELP_NAME : 'config', | |
398 # List of help name aliases. | |
399 HELP_NAME_ALIASES : ['cfg', 'conf', 'configure', 'proxy', 'aws', 's3'], | |
400 # Type of help: | |
401 HELP_TYPE : HelpType.COMMAND_HELP, | |
402 # One line summary of this help. | |
403 HELP_ONE_LINE_SUMMARY : 'Obtain credentials and create configuration file', | |
404 # The full help text. | |
405 HELP_TEXT : _detailed_help_text, | |
406 } | |
407 | |
408 def _OpenConfigFile(self, file_path): | |
409 """Creates and opens a configuration file for writing. | |
410 | |
411 The file is created with mode 0600, and attempts to open existing files will | |
412 fail (the latter is important to prevent symlink attacks). | |
413 | |
414 It is the caller's responsibility to close the file. | |
415 | |
416 Args: | |
417 file_path: Path of the file to be created. | |
418 | |
419 Returns: | |
420 A writable file object for the opened file. | |
421 | |
422 Raises: | |
423 CommandException: if an error occurred when opening the file (including | |
424 when the file already exists). | |
425 """ | |
426 flags = os.O_RDWR | os.O_CREAT | os.O_EXCL | |
427 # Accommodate Windows; stolen from python2.6/tempfile.py. | |
428 if hasattr(os, 'O_NOINHERIT'): | |
429 flags |= os.O_NOINHERIT | |
430 try: | |
431 fd = os.open(file_path, flags, 0600) | |
432 except (OSError, IOError), e: | |
433 raise CommandException('Failed to open %s for writing: %s' % | |
434 (file_path, e)) | |
435 return os.fdopen(fd, 'w') | |
436 | |
437 def _WriteBotoConfigFile(self, config_file, use_oauth2=True, | |
438 launch_browser=True, oauth2_scopes=[SCOPE_FULL_CONTROL]): | |
439 """Creates a boto config file interactively. | |
440 | |
441 Needed credentials are obtained interactively, either by asking the user for | |
442 access key and secret, or by walking the user through the OAuth2 approval | |
443 flow. | |
444 | |
445 Args: | |
446 config_file: File object to which the resulting config file will be | |
447 written. | |
448 use_oauth2: If True, walk user through OAuth2 approval flow and produce a | |
449 config with an oauth2_refresh_token credential. If false, ask the | |
450 user for access key and secret. | |
451 launch_browser: In the OAuth2 approval flow, attempt to open a browser | |
452 window and navigate to the approval URL. | |
453 oauth2_scopes: A list of OAuth2 scopes to request authorization for, when | |
454 using OAuth2. | |
455 """ | |
456 | |
457 # Collect credentials | |
458 provider_map = {'aws': 'aws', 'google': 'gs'} | |
459 uri_map = {'aws': 's3', 'google': 'gs'} | |
460 key_ids = {} | |
461 sec_keys = {} | |
462 if use_oauth2: | |
463 oauth2_refresh_token = oauth2_helper.OAuth2ApprovalFlow( | |
464 oauth2_helper.OAuth2ClientFromBotoConfig(boto.config), | |
465 oauth2_scopes, launch_browser) | |
466 else: | |
467 got_creds = False | |
468 for provider in provider_map: | |
469 if provider == 'google': | |
470 key_ids[provider] = raw_input('What is your %s access key ID? ' % | |
471 provider) | |
472 sec_keys[provider] = raw_input('What is your %s secret access key? ' % | |
473 provider) | |
474 got_creds = True | |
475 if not key_ids[provider] or not sec_keys[provider]: | |
476 raise CommandException( | |
477 'Incomplete credentials provided. Please try again.') | |
478 if not got_creds: | |
479 raise CommandException('No credentials provided. Please try again.') | |
480 | |
481 # Write the config file prelude. | |
482 config_file.write(CONFIG_PRELUDE_CONTENT.lstrip()) | |
483 config_file.write( | |
484 '# This file was created by gsutil version %s at %s.\n' | |
485 % (self.gsutil_ver, | |
486 datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) | |
487 config_file.write('#\n# You can create additional configuration files by ' | |
488 'running\n# gsutil config [options] [-o <config-file>]\n\n\n') | |
489 | |
490 # Write the config file Credentials section. | |
491 config_file.write('[Credentials]\n\n') | |
492 if use_oauth2: | |
493 config_file.write('# Google OAuth2 credentials (for "gs://" URIs):\n') | |
494 config_file.write('# The following OAuth2 token is authorized for ' | |
495 'scope(s):\n') | |
496 for scope in oauth2_scopes: | |
497 config_file.write('# %s\n' % scope) | |
498 config_file.write('gs_oauth2_refresh_token = %s\n\n' % | |
499 oauth2_refresh_token.refresh_token) | |
500 else: | |
501 config_file.write('# To add Google OAuth2 credentials ("gs://" URIs), ' | |
502 'edit and uncomment the\n# following line:\n' | |
503 '#gs_oauth2_refresh_token = <your OAuth2 refresh token>\n\n') | |
504 | |
505 for provider in provider_map: | |
506 key_prefix = provider_map[provider] | |
507 uri_scheme = uri_map[provider] | |
508 if provider in key_ids and provider in sec_keys: | |
509 config_file.write('# %s credentials ("%s://" URIs):\n' % | |
510 (provider, uri_scheme)) | |
511 config_file.write('%s_access_key_id = %s\n' % | |
512 (key_prefix, key_ids[provider])) | |
513 config_file.write('%s_secret_access_key = %s\n' % | |
514 (key_prefix, sec_keys[provider])) | |
515 else: | |
516 config_file.write('# To add %s credentials ("%s://" URIs), edit and ' | |
517 'uncomment the\n# following two lines:\n' | |
518 '#%s_access_key_id = <your %s access key ID>\n' | |
519 '#%s_secret_access_key = <your %s secret access key>\n' % | |
520 (provider, uri_scheme, key_prefix, provider, key_prefix, | |
521 provider)) | |
522 host_key = Provider.HostKeyMap[provider] | |
523 config_file.write('# The ability to specify an alternate storage host ' | |
524 'is primarily for cloud\n# storage service developers.\n' | |
525 '#%s_host = <alternate storage host address>\n\n' % host_key) | |
526 | |
527 # Write the config file Boto section. | |
528 config_file.write('%s\n' % CONFIG_BOTO_SECTION_CONTENT) | |
529 | |
530 # Write the config file GSUtil section that doesn't depend on user input. | |
531 config_file.write(CONFIG_INPUTLESS_GSUTIL_SECTION_CONTENT) | |
532 | |
533 # Write the default API version. | |
534 config_file.write(""" | |
535 # 'default_api_version' specifies the default Google Cloud Storage API | |
536 # version to use. If not set below gsutil defaults to API version 1. | |
537 """) | |
538 api_version = 2 | |
539 if not use_oauth2: api_version = 1 | |
540 | |
541 config_file.write('default_api_version = %d\n' % api_version) | |
542 | |
543 default_project_id = '0' | |
544 project_id_section_prelude = """ | |
545 # 'default_project_id' specifies the default Google Cloud Storage project ID to | |
546 # use with the 'mb' and 'ls' commands. If defined it overrides the default value | |
547 # you set in the API Console. Either of these defaults can be overridden | |
548 # by specifying the -p option to the 'mb' and 'ls' commands. | |
549 """ | |
550 if default_project_id: | |
551 config_file.write('%sdefault_project_id = %s\n\n\n' % | |
552 (project_id_section_prelude, default_project_id)) | |
553 else: | |
554 sys.stderr.write('No default project ID entered. You will need to edit ' | |
555 'the default_project_id value\nin your boto config file ' | |
556 'before using "gsutil ls gs://" or "mb" commands' | |
557 'with the\ndefault API version (2).\n') | |
558 config_file.write('%s#default_project_id = <value>\n\n\n' % | |
559 project_id_section_prelude) | |
560 | |
561 # Write the config file OAuth2 section. | |
562 config_file.write(CONFIG_OAUTH2_CONFIG_CONTENT) | |
563 | |
564 # Command entry point. | |
565 def RunCommand(self): | |
566 scopes = [] | |
567 use_oauth2 = True | |
568 launch_browser = False | |
569 output_file_name = None | |
570 for opt, opt_arg in self.sub_opts: | |
571 if opt == '-a': | |
572 use_oauth2 = False | |
573 elif opt == '-b': | |
574 launch_browser = True | |
575 elif opt == '-f': | |
576 scopes.append(SCOPE_FULL_CONTROL) | |
577 elif opt == '-o': | |
578 output_file_name = opt_arg | |
579 elif opt == '-r': | |
580 scopes.append(SCOPE_READ_ONLY) | |
581 elif opt == '-s': | |
582 scopes.append(opt_arg) | |
583 elif opt == '-w': | |
584 scopes.append(SCOPE_READ_WRITE) | |
585 | |
586 if use_oauth2 and not HAVE_OAUTH2: | |
587 raise CommandException( | |
588 'OAuth2 is only supported when running under Python 2.6 or later\n' | |
589 '(unless additional dependencies are installed, ' | |
590 'see README for details);\n' | |
591 'you are running Python %s.\nUse "gsutil config -a" to create a ' | |
592 'config with Developer Key authentication credentials.' % sys.version) | |
593 | |
594 if not scopes: | |
595 scopes.append(SCOPE_FULL_CONTROL) | |
596 | |
597 default_config_path_bak = None | |
598 if output_file_name is None: | |
599 # Check to see if a default config file name is requested via | |
600 # environment variable. If so, use it, otherwise use the hard-coded | |
601 # default file. Then use the default config file name, if it doesn't | |
602 # exist or can be moved out of the way without clobbering an existing | |
603 # backup file. | |
604 boto_config_from_env = os.environ.get('BOTO_CONFIG', None) | |
605 if boto_config_from_env: | |
606 default_config_path = boto_config_from_env | |
607 else: | |
608 default_config_path = os.path.expanduser(os.path.join('~', '.boto')) | |
609 if not os.path.exists(default_config_path): | |
610 output_file_name = default_config_path | |
611 else: | |
612 default_config_path_bak = default_config_path + '.bak' | |
613 if os.path.exists(default_config_path_bak): | |
614 raise CommandException('Cannot back up existing config ' | |
615 'file "%s": backup file exists ("%s").' | |
616 % (default_config_path, default_config_path_bak)) | |
617 else: | |
618 try: | |
619 sys.stderr.write( | |
620 'Backing up existing config file "%s" to "%s"...\n' | |
621 % (default_config_path, default_config_path_bak)) | |
622 os.rename(default_config_path, default_config_path_bak) | |
623 except e: | |
624 raise CommandException('Failed to back up existing config ' | |
625 'file ("%s" -> "%s"): %s.' | |
626 % (default_config_path, default_config_path_bak, e)) | |
627 output_file_name = default_config_path | |
628 | |
629 if output_file_name == '-': | |
630 output_file = sys.stdout | |
631 else: | |
632 output_file = self._OpenConfigFile(output_file_name) | |
633 | |
634 # Catch ^C so we can restore the backup. | |
635 signal.signal(signal.SIGINT, cleanup_handler) | |
636 try: | |
637 self._WriteBotoConfigFile(output_file, use_oauth2=use_oauth2, | |
638 launch_browser=launch_browser, oauth2_scopes=scopes) | |
639 except Exception, e: | |
640 user_aborted = isinstance(e, AbortException) | |
641 if user_aborted: | |
642 sys.stderr.write('\nCaught ^C; cleaning up\n') | |
643 # If an error occurred during config file creation, remove the invalid | |
644 # config file and restore the backup file. | |
645 if output_file_name != '-': | |
646 output_file.close() | |
647 os.unlink(output_file_name) | |
648 if default_config_path_bak: | |
649 sys.stderr.write('Restoring previous backed up file (%s)\n' % | |
650 default_config_path_bak) | |
651 os.rename(default_config_path_bak, output_file_name) | |
652 raise | |
653 | |
654 if output_file_name != '-': | |
655 output_file.close() | |
656 sys.stderr.write( | |
657 '\nBoto config file "%s" created.\n' % output_file_name) | |
658 | |
659 return 0 | |
660 | |
661 def cleanup_handler(signalnum, handler): | |
662 raise AbortException('User interrupted config command') | |
OLD | NEW |