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 # Write the config file GSUtil section that includes the default |
| 544 # project ID input from the user. |
| 545 if launch_browser: |
| 546 sys.stdout.write( |
| 547 'Attempting to launch a browser to open the Google API console at ' |
| 548 'URL: %s\n\n' |
| 549 '[Note: due to a Python bug, you may see a spurious error message ' |
| 550 '"object is not\n callable [...] in [...] Popen.__del__" which can ' |
| 551 'be ignored.]\n\n' % GOOG_API_CONSOLE_URI) |
| 552 sys.stdout.write( |
| 553 'In your browser you should see the API Console. Click "Storage" and ' |
| 554 'look for the value under "Identifying your project\n\n') |
| 555 if not webbrowser.open(GOOG_API_CONSOLE_URI, new=1, autoraise=True): |
| 556 sys.stdout.write( |
| 557 'Launching browser appears to have failed; please navigate a ' |
| 558 'browser to the following URL:\n%s\n' % GOOG_API_CONSOLE_URI) |
| 559 # Short delay; webbrowser.open on linux insists on printing out a message |
| 560 # which we don't want to run into the prompt for the auth code. |
| 561 time.sleep(2) |
| 562 else: |
| 563 sys.stdout.write( |
| 564 '\nPlease navigate your browser to %s,\nthen click "Services" on the ' |
| 565 'left side panel and ensure you have Google Cloud\nStorage' |
| 566 'activated, then click "Google Cloud Storage" on the left side ' |
| 567 'panel and\nfind the "x-goog-project-id" on that page.\n' % |
| 568 GOOG_API_CONSOLE_URI) |
| 569 default_project_id = raw_input('What is your project-id? ') |
| 570 project_id_section_prelude = """ |
| 571 # 'default_project_id' specifies the default Google Cloud Storage project ID to |
| 572 # use with the 'mb' and 'ls' commands. If defined it overrides the default value |
| 573 # you set in the API Console. Either of these defaults can be overridden |
| 574 # by specifying the -p option to the 'mb' and 'ls' commands. |
| 575 """ |
| 576 if default_project_id: |
| 577 config_file.write('%sdefault_project_id = %s\n\n\n' % |
| 578 (project_id_section_prelude, default_project_id)) |
| 579 else: |
| 580 sys.stderr.write('No default project ID entered. You will need to edit ' |
| 581 'the default_project_id value\nin your boto config file ' |
| 582 'before using "gsutil ls gs://" or "mb" commands' |
| 583 'with the\ndefault API version (2).\n') |
| 584 config_file.write('%s#default_project_id = <value>\n\n\n' % |
| 585 project_id_section_prelude) |
| 586 |
| 587 # Write the config file OAuth2 section. |
| 588 config_file.write(CONFIG_OAUTH2_CONFIG_CONTENT) |
| 589 |
| 590 # Command entry point. |
| 591 def RunCommand(self): |
| 592 scopes = [] |
| 593 use_oauth2 = True |
| 594 launch_browser = False |
| 595 output_file_name = None |
| 596 for opt, opt_arg in self.sub_opts: |
| 597 if opt == '-a': |
| 598 use_oauth2 = False |
| 599 elif opt == '-b': |
| 600 launch_browser = True |
| 601 elif opt == '-f': |
| 602 scopes.append(SCOPE_FULL_CONTROL) |
| 603 elif opt == '-o': |
| 604 output_file_name = opt_arg |
| 605 elif opt == '-r': |
| 606 scopes.append(SCOPE_READ_ONLY) |
| 607 elif opt == '-s': |
| 608 scopes.append(opt_arg) |
| 609 elif opt == '-w': |
| 610 scopes.append(SCOPE_READ_WRITE) |
| 611 |
| 612 if use_oauth2 and not HAVE_OAUTH2: |
| 613 raise CommandException( |
| 614 'OAuth2 is only supported when running under Python 2.6 or later\n' |
| 615 '(unless additional dependencies are installed, ' |
| 616 'see README for details);\n' |
| 617 'you are running Python %s.\nUse "gsutil config -a" to create a ' |
| 618 'config with Developer Key authentication credentials.' % sys.version) |
| 619 |
| 620 if not scopes: |
| 621 scopes.append(SCOPE_FULL_CONTROL) |
| 622 |
| 623 if output_file_name is None: |
| 624 # Check to see if a default config file name is requested via |
| 625 # environment variable. If so, use it, otherwise use the hard-coded |
| 626 # default file. Then use the default config file name, if it doesn't |
| 627 # exist or can be moved out of the way without clobbering an existing |
| 628 # backup file. |
| 629 boto_config_from_env = os.environ.get('BOTO_CONFIG', None) |
| 630 if boto_config_from_env: |
| 631 default_config_path = boto_config_from_env |
| 632 else: |
| 633 default_config_path = os.path.expanduser(os.path.join('~', '.boto')) |
| 634 if not os.path.exists(default_config_path): |
| 635 output_file_name = default_config_path |
| 636 default_config_path_bak = None |
| 637 else: |
| 638 default_config_path_bak = default_config_path + '.bak' |
| 639 if os.path.exists(default_config_path_bak): |
| 640 raise CommandException('Cannot back up existing config ' |
| 641 'file "%s": backup file exists ("%s").' |
| 642 % (default_config_path, default_config_path_bak)) |
| 643 else: |
| 644 try: |
| 645 sys.stderr.write( |
| 646 'Backing up existing config file "%s" to "%s"...\n' |
| 647 % (default_config_path, default_config_path_bak)) |
| 648 os.rename(default_config_path, default_config_path_bak) |
| 649 except e: |
| 650 raise CommandException('Failed to back up existing config ' |
| 651 'file ("%s" -> "%s"): %s.' |
| 652 % (default_config_path, default_config_path_bak, e)) |
| 653 output_file_name = default_config_path |
| 654 |
| 655 if output_file_name == '-': |
| 656 output_file = sys.stdout |
| 657 else: |
| 658 output_file = self._OpenConfigFile(output_file_name) |
| 659 sys.stderr.write( |
| 660 'This script will create a boto config file at\n%s\ncontaining your ' |
| 661 'credentials, based on your responses to the following questions.\n\n' |
| 662 % output_file_name) |
| 663 |
| 664 # Catch ^C so we can restore the backup. |
| 665 signal.signal(signal.SIGINT, cleanup_handler) |
| 666 try: |
| 667 self._WriteBotoConfigFile(output_file, use_oauth2=use_oauth2, |
| 668 launch_browser=launch_browser, oauth2_scopes=scopes) |
| 669 except Exception, e: |
| 670 user_aborted = isinstance(e, AbortException) |
| 671 if user_aborted: |
| 672 sys.stderr.write('\nCaught ^C; cleaning up\n') |
| 673 # If an error occurred during config file creation, remove the invalid |
| 674 # config file and restore the backup file. |
| 675 if output_file_name != '-': |
| 676 output_file.close() |
| 677 os.unlink(output_file_name) |
| 678 if default_config_path_bak: |
| 679 sys.stderr.write('Restoring previous backed up file (%s)\n' % |
| 680 default_config_path_bak) |
| 681 os.rename(default_config_path_bak, output_file_name) |
| 682 raise |
| 683 |
| 684 if output_file_name != '-': |
| 685 output_file.close() |
| 686 sys.stderr.write( |
| 687 '\nBoto config file "%s" created.\nIf you need to use a proxy to ' |
| 688 'use a proxy to access the Internet please see the instructions in ' |
| 689 'that file.\n' % output_file_name) |
| 690 |
| 691 return 0 |
| 692 |
| 693 def cleanup_handler(signalnum, handler): |
| 694 raise AbortException('User interrupted config command') |
OLD | NEW |