Home | History | Annotate | Download | only in commands
      1 # -*- coding: utf-8 -*-
      2 # Copyright 2011 Google Inc. All Rights Reserved.
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #     http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 """Implementation of config command for creating a gsutil configuration file."""
     16 
     17 from __future__ import absolute_import
     18 
     19 import datetime
     20 from httplib import ResponseNotReady
     21 import json
     22 import multiprocessing
     23 import os
     24 import platform
     25 import signal
     26 import socket
     27 import stat
     28 import sys
     29 import textwrap
     30 import time
     31 import webbrowser
     32 
     33 import boto
     34 from boto.provider import Provider
     35 from httplib2 import ServerNotFoundError
     36 from oauth2client.client import HAS_CRYPTO
     37 
     38 import gslib
     39 from gslib.command import Command
     40 from gslib.commands.compose import MAX_COMPONENT_COUNT
     41 from gslib.cred_types import CredTypes
     42 from gslib.exception import AbortException
     43 from gslib.exception import CommandException
     44 from gslib.hashing_helper import CHECK_HASH_ALWAYS
     45 from gslib.hashing_helper import CHECK_HASH_IF_FAST_ELSE_FAIL
     46 from gslib.hashing_helper import CHECK_HASH_IF_FAST_ELSE_SKIP
     47 from gslib.hashing_helper import CHECK_HASH_NEVER
     48 from gslib.sig_handling import RegisterSignalHandler
     49 from gslib.util import EIGHT_MIB
     50 from gslib.util import IS_WINDOWS
     51 
     52 
     53 _SYNOPSIS = """
     54   gsutil [-D] config [-a] [-b] [-e] [-f] [-o <file>] [-r] [-s <scope>] [-w]
     55 """
     56 
     57 _DETAILED_HELP_TEXT = ("""
     58 <B>SYNOPSIS</B>
     59 """ + _SYNOPSIS + """
     60 
     61 
     62 <B>DESCRIPTION</B>
     63   The gsutil config command obtains access credentials for Google Cloud
     64   Storage and writes a boto/gsutil configuration file containing the obtained
     65   credentials along with a number of other configuration-controllable values.
     66 
     67   Unless specified otherwise (see OPTIONS), the configuration file is written
     68   to ~/.boto (i.e., the file .boto under the user's home directory). If the
     69   default file already exists, an attempt is made to rename the existing file
     70   to ~/.boto.bak; if that attempt fails the command will exit. A different
     71   destination file can be specified with the -o option (see OPTIONS).
     72 
     73   Because the boto configuration file contains your credentials you should
     74   keep its file permissions set so no one but you has read access. (The file
     75   is created read-only when you run gsutil config.)
     76 
     77 
     78 <B>CREDENTIALS</B>
     79   By default gsutil config obtains OAuth2 credentials, and writes them
     80   to the [Credentials] section of the configuration file. The -r, -w,
     81   -f options (see OPTIONS below) cause gsutil config to request a token
     82   with restricted scope; the resulting token will be restricted to read-only
     83   operations, read-write operations, or all operations (including acl get/set,
     84   defacl get/set, and logging get/'set on'/'set off' operations). In
     85   addition, -s <scope> can be used to request additional (non-Google-Storage)
     86   scopes.
     87 
     88   If you want to use credentials based on access key and secret (the older
     89   authentication method before OAuth2 was supported) instead of OAuth2,
     90   see help about the -a option in the OPTIONS section.
     91 
     92   If you wish to use gsutil with other providers (or to copy data back and
     93   forth between multiple providers) you can edit their credentials into the
     94   [Credentials] section after creating the initial configuration file.
     95 
     96 
     97 <B>CONFIGURING SERVICE ACCOUNT CREDENTIALS</B>
     98   You can configure credentials for service accounts using the gsutil config -e
     99   option. Service accounts are useful for authenticating on behalf of a service
    100   or application (as opposed to a user).
    101 
    102   When you run gsutil config -e, you will be prompted for your service account
    103   email address and the path to your private key file. To get these data, visit
    104   the `Google Developers Console <https://cloud.google.com/console#/project>`_,
    105   click on the project you are using, then click "APIs & auth", then click
    106   "Credentials", then click "Create new Client ID"; on the pop-up dialog box
    107   select "Service account" and click "Create Client ID". This will download
    108   a private key file, which you should move to somewhere
    109   accessible from the machine where you run gsutil. Make sure to set its
    110   protection so only the users you want to be able to authenticate have
    111   access.
    112 
    113   Note that your service account will NOT be considered an Owner for the
    114   purposes of API access (see "gsutil help creds" for more information about
    115   this). See https://developers.google.com/accounts/docs/OAuth2ServiceAccount
    116   for further information on service account authentication.
    117 
    118 
    119 <B>CONFIGURATION FILE SELECTION PROCEDURE</B>
    120   By default, gsutil will look for the configuration file in /etc/boto.cfg and
    121   ~/.boto. You can override this choice by setting the BOTO_CONFIG environment
    122   variable. This is also useful if you have several different identities or
    123   cloud storage environments: By setting up the credentials and any additional
    124   configuration in separate files for each, you can switch environments by
    125   changing environment variables.
    126 
    127   You can also set up a path of configuration files, by setting the BOTO_PATH
    128   environment variable to contain a ":" delimited path. For example setting
    129   the BOTO_PATH environment variable to:
    130 
    131     /etc/projects/my_group_project.boto.cfg:/home/mylogin/.boto
    132 
    133   will cause gsutil to load each configuration file found in the path in
    134   order. This is useful if you want to set up some shared configuration
    135   state among many users: The shared state can go in the central shared file
    136   ( /etc/projects/my_group_project.boto.cfg) and each user's individual
    137   credentials can be placed in the configuration file in each of their home
    138   directories. (For security reasons users should never share credentials
    139   via a shared configuration file.)
    140 
    141 
    142 <B>CONFIGURATION FILE STRUCTURE</B>
    143   The configuration file contains a number of sections: [Credentials],
    144   [Boto], [GSUtil], and [OAuth2]. If you edit the file make sure to edit the
    145   appropriate section (discussed below), and to be careful not to mis-edit
    146   any of the setting names (like "gs_access_key_id") and not to remove the
    147   section delimiters (like "[Credentials]").
    148 
    149 
    150 <B>ADDITIONAL CONFIGURATION-CONTROLLABLE FEATURES</B>
    151   With the exception of setting up gsutil to work through a proxy (see
    152   below), most users won't need to edit values in the boto configuration file;
    153   values found in there tend to be of more specialized use than command line
    154   option-controllable features.
    155 
    156   The following are the currently defined configuration settings, broken
    157   down by section. Their use is documented in comments preceding each, in
    158   the configuration file. If you see a setting you want to change that's not
    159   listed in your current file, see the section below on Updating to the Latest
    160   Configuration File.
    161 
    162   The currently supported settings, are, by section:
    163 
    164     [Credentials]
    165       aws_access_key_id
    166       aws_secret_access_key
    167       gs_access_key_id
    168       gs_host
    169       gs_json_host
    170       gs_json_port
    171       gs_oauth2_refresh_token
    172       gs_port
    173       gs_secret_access_key
    174       s3_host
    175       s3_port
    176 
    177     [Boto]
    178       proxy
    179       proxy_port
    180       proxy_user
    181       proxy_pass
    182       proxy_rdns
    183       http_socket_timeout
    184       https_validate_certificates
    185       debug
    186       max_retry_delay
    187       num_retries
    188 
    189     [GSUtil]
    190       check_hashes
    191       content_language
    192       default_api_version
    193       default_project_id
    194       json_api_version
    195       parallel_composite_upload_component_size
    196       parallel_composite_upload_threshold
    197       sliced_object_download_component_size
    198       sliced_object_download_max_components
    199       sliced_object_download_threshold
    200       parallel_process_count
    201       parallel_thread_count
    202       prefer_api
    203       resumable_threshold
    204       resumable_tracker_dir (deprecated in 4.6, use state_dir)
    205       rsync_buffer_lines
    206       software_update_check_period
    207       state_dir
    208       tab_completion_time_logs
    209       tab_completion_timeout
    210       use_magicfile
    211 
    212     [OAuth2]
    213       client_id
    214       client_secret
    215       oauth2_refresh_retries
    216       provider_authorization_uri
    217       provider_label
    218       provider_token_uri
    219       token_cache
    220 
    221 
    222 <B>UPDATING TO THE LATEST CONFIGURATION FILE</B>
    223   We add new configuration controllable features to the boto configuration file
    224   over time, but most gsutil users create a configuration file once and then
    225   keep it for a long time, so new features aren't apparent when you update
    226   to a newer version of gsutil. If you want to get the latest configuration
    227   file (which includes all the latest settings and documentation about each)
    228   you can rename your current file (e.g., to '.boto_old'), run gsutil config,
    229   and then edit any configuration settings you wanted from your old file
    230   into the newly created file. Note, however, that if you're using OAuth2
    231   credentials and you go back through the OAuth2 configuration dialog it will
    232   invalidate your previous OAuth2 credentials.
    233 
    234   If no explicit scope option is given, -f (full control) is assumed by default.
    235 
    236 
    237 <B>OPTIONS</B>
    238   -a          Prompt for Google Cloud Storage access key and secret (the older
    239               authentication method before OAuth2 was supported) instead of
    240               obtaining an OAuth2 token.
    241 
    242   -b          Causes gsutil config to launch a browser to obtain OAuth2 approval
    243               and the project ID instead of showing the URL for each and asking
    244               the user to open the browser. This will probably not work as
    245               expected if you are running gsutil from an ssh window, or using
    246               gsutil on Windows.
    247 
    248   -e          Prompt for service account credentials. This option requires that
    249               -a is not set.
    250 
    251   -f          Request token with full-control access (default).
    252 
    253   -o <file>   Write the configuration to <file> instead of ~/.boto.
    254               Use '-' for stdout.
    255 
    256   -r          Request token restricted to read-only access.
    257 
    258   -s <scope>  Request additional OAuth2 <scope>.
    259 
    260   -w          Request token restricted to read-write access.
    261 """)
    262 
    263 
    264 try:
    265   from gcs_oauth2_boto_plugin import oauth2_helper  # pylint: disable=g-import-not-at-top
    266 except ImportError:
    267   pass
    268 
    269 GOOG_CLOUD_CONSOLE_URI = 'https://cloud.google.com/console#/project'
    270 
    271 SCOPE_FULL_CONTROL = 'https://www.googleapis.com/auth/devstorage.full_control'
    272 SCOPE_READ_WRITE = 'https://www.googleapis.com/auth/devstorage.read_write'
    273 SCOPE_READ_ONLY = 'https://www.googleapis.com/auth/devstorage.read_only'
    274 
    275 CONFIG_PRELUDE_CONTENT = """
    276 # This file contains credentials and other configuration information needed
    277 # by the boto library, used by gsutil. You can edit this file (e.g., to add
    278 # credentials) but be careful not to mis-edit any of the variable names (like
    279 # "gs_access_key_id") or remove important markers (like the "[Credentials]" and
    280 # "[Boto]" section delimiters).
    281 #
    282 """
    283 
    284 # Default number of OS processes and Python threads for parallel operations.
    285 # On Linux systems we automatically scale the number of processes to match
    286 # the underlying CPU/core count. Given we'll be running multiple concurrent
    287 # processes on a typical multi-core Linux computer, to avoid being too
    288 # aggressive with resources, the default number of threads is reduced from
    289 # the previous value of 24 to 10.
    290 # On Windows and Mac systems parallel multi-processing and multi-threading
    291 # in Python presents various challenges so we retain compatibility with
    292 # the established parallel mode operation, i.e. one process and 24 threads.
    293 if platform.system() == 'Linux':
    294   DEFAULT_PARALLEL_PROCESS_COUNT = multiprocessing.cpu_count()
    295   DEFAULT_PARALLEL_THREAD_COUNT = 10
    296 else:
    297   DEFAULT_PARALLEL_PROCESS_COUNT = 1
    298   DEFAULT_PARALLEL_THREAD_COUNT = 24
    299 
    300 # TODO: Once compiled crcmod is being distributed by major Linux distributions
    301 # revert DEFAULT_PARALLEL_COMPOSITE_UPLOAD_THRESHOLD value to '150M'.
    302 DEFAULT_PARALLEL_COMPOSITE_UPLOAD_THRESHOLD = '0'
    303 DEFAULT_PARALLEL_COMPOSITE_UPLOAD_COMPONENT_SIZE = '50M'
    304 DEFAULT_SLICED_OBJECT_DOWNLOAD_THRESHOLD = '150M'
    305 DEFAULT_SLICED_OBJECT_DOWNLOAD_COMPONENT_SIZE = '200M'
    306 DEFAULT_SLICED_OBJECT_DOWNLOAD_MAX_COMPONENTS = 4
    307 
    308 CONFIG_BOTO_SECTION_CONTENT = """
    309 [Boto]
    310 
    311 # http_socket_timeout specifies the timeout (in seconds) used to tell httplib
    312 # how long to wait for socket timeouts. The default is 70 seconds. Note that
    313 # this timeout only applies to httplib, not to httplib2 (which is used for
    314 # OAuth2 refresh/access token exchanges).
    315 #http_socket_timeout = 70
    316 
    317 # The following two options control the use of a secure transport for requests
    318 # to S3 and Google Cloud Storage. It is highly recommended to set both options
    319 # to True in production environments, especially when using OAuth2 bearer token
    320 # authentication with Google Cloud Storage.
    321 
    322 # Set 'https_validate_certificates' to False to disable server certificate
    323 # checking. The default for this option in the boto library is currently
    324 # 'False' (to avoid breaking apps that depend on invalid certificates); it is
    325 # therefore strongly recommended to always set this option explicitly to True
    326 # in configuration files, to protect against "man-in-the-middle" attacks.
    327 https_validate_certificates = True
    328 
    329 # 'debug' controls the level of debug messages printed: 0 for none, 1
    330 # for basic boto debug, 2 for all boto debug plus HTTP requests/responses.
    331 # Note: 'gsutil -d' sets debug to 2 for that one command run.
    332 #debug = <0, 1, or 2>
    333 
    334 # 'num_retries' controls the number of retry attempts made when errors occur
    335 # during data transfers. The default is 6.
    336 # Note 1: You can cause gsutil to retry failures effectively infinitely by
    337 # setting this value to a large number (like 10000). Doing that could be useful
    338 # in cases where your network connection occasionally fails and is down for an
    339 # extended period of time, because when it comes back up gsutil will continue
    340 # retrying.  However, in general we recommend not setting the value above 10,
    341 # because otherwise gsutil could appear to "hang" due to excessive retries
    342 # (since unless you run gsutil -D you won't see any logged evidence that gsutil
    343 # is retrying).
    344 # Note 2: Don't set this value to 0, as it will cause boto to fail when reusing
    345 # HTTP connections.
    346 #num_retries = <integer value>
    347 
    348 # 'max_retry_delay' controls the max delay (in seconds) between retries. The
    349 # default value is 60, so the backoff sequence will be 1 seconds, 2 seconds, 4,
    350 # 8, 16, 32, and then 60 for all subsequent retries for a given HTTP request.
    351 # Note: At present this value only impacts the XML API and the JSON API uses a
    352 # fixed value of 60.
    353 #max_retry_delay = <integer value>
    354 """
    355 
    356 CONFIG_INPUTLESS_GSUTIL_SECTION_CONTENT = """
    357 [GSUtil]
    358 
    359 # 'resumable_threshold' specifies the smallest file size [bytes] for which
    360 # resumable Google Cloud Storage uploads are attempted. The default is 8388608
    361 # (8 MiB).
    362 #resumable_threshold = %(resumable_threshold)d
    363 
    364 # 'rsync_buffer_lines' specifies the number of lines of bucket or directory
    365 # listings saved in each temp file during sorting. (The complete set is
    366 # split across temp files and separately sorted/merged, to avoid needing to
    367 # fit everything in memory at once.) If you are trying to synchronize very
    368 # large directories/buckets (e.g., containing millions or more objects),
    369 # having too small a value here can cause gsutil to run out of open file
    370 # handles. If that happens, you can try to increase the number of open file
    371 # handles your system allows (e.g., see 'man ulimit' on Linux; see also
    372 # http://docs.python.org/2/library/resource.html). If you can't do that (or
    373 # if you're already at the upper limit), increasing rsync_buffer_lines will
    374 # cause gsutil to use fewer file handles, but at the cost of more memory. With
    375 # rsync_buffer_lines set to 32000 and assuming a typical URL is 100 bytes
    376 # long, gsutil will require approximately 10 MiB of memory while building
    377 # the synchronization state, and will require approximately 60 open file
    378 # descriptors to build the synchronization state over all 1M source and 1M
    379 # destination URLs. Memory and file descriptors are only consumed while
    380 # building the state; once the state is built, it resides in two temp files that
    381 # are read and processed incrementally during the actual copy/delete
    382 # operations.
    383 #rsync_buffer_lines = 32000
    384 
    385 # 'state_dir' specifies the base location where files that
    386 # need a static location are stored, such as pointers to credentials,
    387 # resumable transfer tracker files, and the last software update check.
    388 # By default these files are stored in ~/.gsutil
    389 #state_dir = <file_path>
    390 # gsutil periodically checks whether a new version of the gsutil software is
    391 # available. 'software_update_check_period' specifies the number of days
    392 # between such checks. The default is 30. Setting the value to 0 disables
    393 # periodic software update checks.
    394 #software_update_check_period = 30
    395 
    396 # 'tab_completion_timeout' controls the timeout (in seconds) for tab
    397 # completions that involve remote requests (such as bucket or object names).
    398 # If tab completion does not succeed within this timeout, no tab completion
    399 # suggestions will be returned.
    400 # A value of 0 will disable completions that involve remote requests.
    401 #tab_completion_timeout = 5
    402 
    403 # 'parallel_process_count' and 'parallel_thread_count' specify the number
    404 # of OS processes and Python threads, respectively, to use when executing
    405 # operations in parallel. The default settings should work well as configured,
    406 # however, to enhance performance for transfers involving large numbers of
    407 # files, you may experiment with hand tuning these values to optimize
    408 # performance for your particular system configuration.
    409 # MacOS and Windows users should see
    410 # https://github.com/GoogleCloudPlatform/gsutil/issues/77 before attempting
    411 # to experiment with these values.
    412 #parallel_process_count = %(parallel_process_count)d
    413 #parallel_thread_count = %(parallel_thread_count)d
    414 
    415 # 'parallel_composite_upload_threshold' specifies the maximum size of a file to
    416 # upload in a single stream. Files larger than this threshold will be
    417 # partitioned into component parts and uploaded in parallel and then composed
    418 # into a single object.
    419 # The number of components will be the smaller of
    420 # ceil(file_size / parallel_composite_upload_component_size) and
    421 # MAX_COMPONENT_COUNT. The current value of MAX_COMPONENT_COUNT is
    422 # %(max_component_count)d.
    423 # If 'parallel_composite_upload_threshold' is set to 0, then automatic parallel
    424 # uploads will never occur.
    425 # Setting an extremely low threshold is unadvisable. The vast majority of
    426 # environments will see degraded performance for thresholds below 80M, and it
    427 # is almost never advantageous to have a threshold below 20M.
    428 # 'parallel_composite_upload_component_size' specifies the ideal size of a
    429 # component in bytes, which will act as an upper bound to the size of the
    430 # components if ceil(file_size / parallel_composite_upload_component_size) is
    431 # less than MAX_COMPONENT_COUNT.
    432 # Values can be provided either in bytes or as human-readable values
    433 # (e.g., "150M" to represent 150 mebibytes)
    434 #
    435 # Note: At present parallel composite uploads are disabled by default, because
    436 # using composite objects requires a compiled crcmod (see "gsutil help crcmod"),
    437 # and for operating systems that don't already have this package installed this
    438 # makes gsutil harder to use. Google is actively working with a number of the
    439 # Linux distributions to get crcmod included with the stock distribution. Once
    440 # that is done we will re-enable parallel composite uploads by default in
    441 # gsutil.
    442 #
    443 # Note: Parallel composite uploads should not be used with NEARLINE storage
    444 # class buckets, as doing this would incur an early deletion charge for each
    445 # component object.
    446 #parallel_composite_upload_threshold = %(parallel_composite_upload_threshold)s
    447 #parallel_composite_upload_component_size = %(parallel_composite_upload_component_size)s
    448 
    449 # 'sliced_object_download_threshold' and
    450 # 'sliced_object_download_component_size' have analogous functionality to
    451 # their respective parallel_composite_upload config values.
    452 # 'sliced_object_download_max_components' specifies the maximum number of 
    453 # slices to be used when performing a sliced object download. It is not
    454 # restricted by MAX_COMPONENT_COUNT.
    455 #sliced_object_download_threshold = %(sliced_object_download_threshold)s
    456 #sliced_object_download_component_size = %(sliced_object_download_component_size)s
    457 #sliced_object_download_max_components = %(sliced_object_download_max_components)s
    458 
    459 # 'use_magicfile' specifies if the 'file --mime-type <filename>' command should
    460 # be used to guess content types instead of the default filename extension-based
    461 # mechanism. Available on UNIX and MacOS (and possibly on Windows, if you're
    462 # running Cygwin or some other package that provides implementations of
    463 # UNIX-like commands). When available and enabled use_magicfile should be more
    464 # robust because it analyzes file contents in addition to extensions.
    465 #use_magicfile = False
    466 
    467 # 'content_language' specifies the ISO 639-1 language code of the content, to be
    468 # passed in the Content-Language header. By default no Content-Language is sent.
    469 # See the ISO 639-1 column of
    470 # http://www.loc.gov/standards/iso639-2/php/code_list.php for a list of
    471 # language codes.
    472 content_language = en
    473 
    474 # 'check_hashes' specifies how strictly to require integrity checking for
    475 # downloaded data. Legal values are:
    476 #   '%(hash_fast_else_fail)s' - (default) Only integrity check if the digest
    477 #       will run efficiently (using compiled code), else fail the download.
    478 #   '%(hash_fast_else_skip)s' - Only integrity check if the server supplies a
    479 #       hash and the local digest computation will run quickly, else skip the
    480 #       check.
    481 #   '%(hash_always)s' - Always check download integrity regardless of possible
    482 #       performance costs.
    483 #   '%(hash_never)s' - Don't perform download integrity checks. This setting is
    484 #       not recommended except for special cases such as measuring download
    485 #       performance excluding time for integrity checking.
    486 # This option exists to assist users who wish to download a GCS composite object
    487 # and are unable to install crcmod with the C-extension. CRC32c is the only
    488 # available integrity check for composite objects, and without the C-extension,
    489 # download performance can be significantly degraded by the digest computation.
    490 # This option is ignored for daisy-chain copies, which don't compute hashes but
    491 # instead (inexpensively) compare the cloud source and destination hashes.
    492 #check_hashes = if_fast_else_fail
    493 
    494 # The ability to specify an alternative JSON API version is primarily for cloud
    495 # storage service developers.
    496 #json_api_version = v1
    497 
    498 # Specifies the API to use when interacting with cloud storage providers.  If
    499 # the gsutil command supports this API for the provider, it will be used
    500 # instead of the default.
    501 # Commands typically default to XML for S3 and JSON for GCS.
    502 #prefer_api = json
    503 #prefer_api = xml
    504 
    505 """ % {'hash_fast_else_fail': CHECK_HASH_IF_FAST_ELSE_FAIL,
    506        'hash_fast_else_skip': CHECK_HASH_IF_FAST_ELSE_SKIP,
    507        'hash_always': CHECK_HASH_ALWAYS,
    508        'hash_never': CHECK_HASH_NEVER,
    509        'resumable_threshold': EIGHT_MIB,
    510        'parallel_process_count': DEFAULT_PARALLEL_PROCESS_COUNT,
    511        'parallel_thread_count': DEFAULT_PARALLEL_THREAD_COUNT,
    512        'parallel_composite_upload_threshold': (
    513            DEFAULT_PARALLEL_COMPOSITE_UPLOAD_THRESHOLD),
    514        'parallel_composite_upload_component_size': (
    515            DEFAULT_PARALLEL_COMPOSITE_UPLOAD_COMPONENT_SIZE),
    516        'sliced_object_download_threshold': (
    517            DEFAULT_PARALLEL_COMPOSITE_UPLOAD_THRESHOLD),
    518        'sliced_object_download_component_size': (
    519            DEFAULT_PARALLEL_COMPOSITE_UPLOAD_COMPONENT_SIZE),
    520        'sliced_object_download_max_components': (
    521            DEFAULT_SLICED_OBJECT_DOWNLOAD_MAX_COMPONENTS),
    522        'max_component_count': MAX_COMPONENT_COUNT}
    523 
    524 CONFIG_OAUTH2_CONFIG_CONTENT = """
    525 [OAuth2]
    526 # This section specifies options used with OAuth2 authentication.
    527 
    528 # 'token_cache' specifies how the OAuth2 client should cache access tokens.
    529 # Valid values are:
    530 #  'in_memory': an in-memory cache is used. This is only useful if the boto
    531 #      client instance (and with it the OAuth2 plugin instance) persists
    532 #      across multiple requests.
    533 #  'file_system' : access tokens will be cached in the file system, in files
    534 #      whose names include a key derived from the refresh token the access token
    535 #      based on.
    536 # The default is 'file_system'.
    537 #token_cache = file_system
    538 #token_cache = in_memory
    539 
    540 # 'token_cache_path_pattern' specifies a path pattern for token cache files.
    541 # This option is only relevant if token_cache = file_system.
    542 # The value of this option should be a path, with place-holders '%(key)s' (which
    543 # will be replaced with a key derived from the refresh token the cached access
    544 # token was based on), and (optionally), %(uid)s (which will be replaced with
    545 # the UID of the current user, if available via os.getuid()).
    546 # Note that the config parser itself interpolates '%' placeholders, and hence
    547 # the above placeholders need to be escaped as '%%(key)s'.
    548 # The default value of this option is
    549 #  token_cache_path_pattern = <tmpdir>/oauth2client-tokencache.%%(uid)s.%%(key)s
    550 # where <tmpdir> is the system-dependent default temp directory.
    551 
    552 # The following options specify the OAuth2 client identity and secret that is
    553 # used when requesting and using OAuth2 tokens. If not specified, a default
    554 # OAuth2 client for the gsutil tool is used; for uses of the boto library (with
    555 # OAuth2 authentication plugin) in other client software, it is recommended to
    556 # use a tool/client-specific OAuth2 client. For more information on OAuth2, see
    557 # http://code.google.com/apis/accounts/docs/OAuth2.html
    558 #client_id = <OAuth2 client id>
    559 #client_secret = <OAuth2 client secret>
    560 
    561 # The following options specify the label and endpoint URIs for the OAUth2
    562 # authorization provider being used. Primarily useful for tool developers.
    563 #provider_label = Google
    564 #provider_authorization_uri = https://accounts.google.com/o/oauth2/auth
    565 #provider_token_uri = https://accounts.google.com/o/oauth2/token
    566 
    567 # 'oauth2_refresh_retries' controls the number of retry attempts made when
    568 # rate limiting errors occur for OAuth2 requests to retrieve an access token.
    569 # The default value is 6.
    570 #oauth2_refresh_retries = <integer value>
    571 """
    572 
    573 
    574 class ConfigCommand(Command):
    575   """Implementation of gsutil config command."""
    576 
    577   # Command specification. See base class for documentation.
    578   command_spec = Command.CreateCommandSpec(
    579       'config',
    580       command_name_aliases=['cfg', 'conf', 'configure'],
    581       usage_synopsis=_SYNOPSIS,
    582       min_args=0,
    583       max_args=0,
    584       supported_sub_args='habefwrs:o:',
    585       file_url_ok=False,
    586       provider_url_ok=False,
    587       urls_start_arg=0,
    588   )
    589   # Help specification. See help_provider.py for documentation.
    590   help_spec = Command.HelpSpec(
    591       help_name='config',
    592       help_name_aliases=['cfg', 'conf', 'configure', 'aws', 's3'],
    593       help_type='command_help',
    594       help_one_line_summary=(
    595           'Obtain credentials and create configuration file'),
    596       help_text=_DETAILED_HELP_TEXT,
    597       subcommand_help_text={},
    598   )
    599 
    600   def _OpenConfigFile(self, file_path):
    601     """Creates and opens a configuration file for writing.
    602 
    603     The file is created with mode 0600, and attempts to open existing files will
    604     fail (the latter is important to prevent symlink attacks).
    605 
    606     It is the caller's responsibility to close the file.
    607 
    608     Args:
    609       file_path: Path of the file to be created.
    610 
    611     Returns:
    612       A writable file object for the opened file.
    613 
    614     Raises:
    615       CommandException: if an error occurred when opening the file (including
    616           when the file already exists).
    617     """
    618     flags = os.O_RDWR | os.O_CREAT | os.O_EXCL
    619     # Accommodate Windows; copied from python2.6/tempfile.py.
    620     if hasattr(os, 'O_NOINHERIT'):
    621       flags |= os.O_NOINHERIT
    622     try:
    623       fd = os.open(file_path, flags, 0600)
    624     except (OSError, IOError), e:
    625       raise CommandException('Failed to open %s for writing: %s' %
    626                              (file_path, e))
    627     return os.fdopen(fd, 'w')
    628 
    629   def _CheckPrivateKeyFilePermissions(self, file_path):
    630     """Checks that the file has reasonable permissions for a private key.
    631 
    632     In particular, check that the filename provided by the user is not
    633     world- or group-readable. If either of these are true, we issue a warning
    634     and offer to fix the permissions.
    635 
    636     Args:
    637       file_path: The name of the private key file.
    638     """
    639     if IS_WINDOWS:
    640       # For Windows, this check doesn't work (it actually just checks whether
    641       # the file is read-only). Since Windows files have a complicated ACL
    642       # system, this check doesn't make much sense on Windows anyway, so we
    643       # just don't do it.
    644       return
    645 
    646     st = os.stat(file_path)
    647     if bool((stat.S_IRGRP | stat.S_IROTH) & st.st_mode):
    648       self.logger.warn(
    649           '\nYour private key file is readable by people other than yourself.\n'
    650           'This is a security risk, since anyone with this information can use '
    651           'your service account.\n')
    652       fix_it = raw_input('Would you like gsutil to change the file '
    653                          'permissions for you? (y/N) ')
    654       if fix_it in ('y', 'Y'):
    655         try:
    656           os.chmod(file_path, 0400)
    657           self.logger.info(
    658               '\nThe permissions on your file have been successfully '
    659               'modified.'
    660               '\nThe only access allowed is readability by the user '
    661               '(permissions 0400 in chmod).')
    662         except Exception, _:  # pylint: disable=broad-except
    663           self.logger.warn(
    664               '\nWe were unable to modify the permissions on your file.\n'
    665               'If you would like to fix this yourself, consider running:\n'
    666               '"sudo chmod 400 </path/to/key>" for improved security.')
    667       else:
    668         self.logger.info(
    669             '\nYou have chosen to allow this file to be readable by others.\n'
    670             'If you would like to fix this yourself, consider running:\n'
    671             '"sudo chmod 400 </path/to/key>" for improved security.')
    672 
    673   def _PromptForProxyConfigVarAndMaybeSaveToBotoConfig(self, varname, prompt,
    674                                                        convert_to_bool=False):
    675     """Prompts for one proxy config line, saves to boto.config if not empty.
    676 
    677     Args:
    678       varname: The config variable name.
    679       prompt: The prompt to output to the user.
    680       convert_to_bool: Whether to convert "y/n" to True/False.
    681     """
    682     value = raw_input(prompt)
    683     if value:
    684       if convert_to_bool:
    685         if value == 'y' or value == 'Y':
    686           value = 'True'
    687         else:
    688           value = 'False'
    689       boto.config.set('Boto', varname, value)
    690 
    691   def _PromptForProxyConfig(self):
    692     """Prompts for proxy config data, loads non-empty values into boto.config.
    693     """
    694     self._PromptForProxyConfigVarAndMaybeSaveToBotoConfig(
    695         'proxy', 'What is your proxy host? ')
    696     self._PromptForProxyConfigVarAndMaybeSaveToBotoConfig(
    697         'proxy_port', 'What is your proxy port? ')
    698     self._PromptForProxyConfigVarAndMaybeSaveToBotoConfig(
    699         'proxy_user', 'What is your proxy user (leave blank if not used)? ')
    700     self._PromptForProxyConfigVarAndMaybeSaveToBotoConfig(
    701         'proxy_pass', 'What is your proxy pass (leave blank if not used)? ')
    702     self._PromptForProxyConfigVarAndMaybeSaveToBotoConfig(
    703         'proxy_rdns',
    704         'Should DNS lookups be resolved by your proxy? (Y if your site '
    705         'disallows client DNS lookups)? ',
    706         convert_to_bool=True)
    707 
    708   def _WriteConfigLineMaybeCommented(self, config_file, name, value, desc):
    709     """Writes proxy name/value pair or comment line to config file.
    710 
    711     Writes proxy name/value pair if value is not None.  Otherwise writes
    712     comment line.
    713 
    714     Args:
    715       config_file: File object to which the resulting config file will be
    716           written.
    717       name: The config variable name.
    718       value: The value, or None.
    719       desc: Human readable description (for comment).
    720     """
    721     if not value:
    722       name = '#%s' % name
    723       value = '<%s>' % desc
    724     config_file.write('%s = %s\n' % (name, value))
    725 
    726   def _WriteProxyConfigFileSection(self, config_file):
    727     """Writes proxy section of configuration file.
    728 
    729     Args:
    730       config_file: File object to which the resulting config file will be
    731           written.
    732     """
    733     config = boto.config
    734     config_file.write(
    735         '# To use a proxy, edit and uncomment the proxy and proxy_port lines.\n'
    736         '# If you need a user/password with this proxy, edit and uncomment\n'
    737         '# those lines as well. If your organization also disallows DNS\n'
    738         '# lookups by client machines set proxy_rdns = True\n'
    739         '# If proxy_host and proxy_port are not specified in this file and\n'
    740         '# one of the OS environment variables http_proxy, https_proxy, or\n'
    741         '# HTTPS_PROXY is defined, gsutil will use the proxy server specified\n'
    742         '# in these environment variables, in order of precedence according\n'
    743         '# to how they are listed above.\n')
    744     self._WriteConfigLineMaybeCommented(
    745         config_file, 'proxy', config.get_value('Boto', 'proxy', None),
    746         'proxy host')
    747     self._WriteConfigLineMaybeCommented(
    748         config_file, 'proxy_port', config.get_value('Boto', 'proxy_port', None),
    749         'proxy port')
    750     self._WriteConfigLineMaybeCommented(
    751         config_file, 'proxy_user', config.get_value('Boto', 'proxy_user', None),
    752         'proxy user')
    753     self._WriteConfigLineMaybeCommented(
    754         config_file, 'proxy_pass', config.get_value('Boto', 'proxy_pass', None),
    755         'proxy password')
    756     self._WriteConfigLineMaybeCommented(
    757         config_file, 'proxy_rdns',
    758         config.get_value('Boto', 'proxy_rdns', False),
    759         'let proxy server perform DNS lookups')
    760 
    761   # pylint: disable=dangerous-default-value,too-many-statements
    762   def _WriteBotoConfigFile(self, config_file, launch_browser=True,
    763                            oauth2_scopes=[SCOPE_FULL_CONTROL],
    764                            cred_type=CredTypes.OAUTH2_USER_ACCOUNT):
    765     """Creates a boto config file interactively.
    766 
    767     Needed credentials are obtained interactively, either by asking the user for
    768     access key and secret, or by walking the user through the OAuth2 approval
    769     flow.
    770 
    771     Args:
    772       config_file: File object to which the resulting config file will be
    773           written.
    774       launch_browser: In the OAuth2 approval flow, attempt to open a browser
    775           window and navigate to the approval URL.
    776       oauth2_scopes: A list of OAuth2 scopes to request authorization for, when
    777           using OAuth2.
    778       cred_type: There are three options:
    779         - for HMAC, ask the user for access key and secret
    780         - for OAUTH2_USER_ACCOUNT, walk the user through OAuth2 approval flow
    781           and produce a config with an oauth2_refresh_token credential.
    782         - for OAUTH2_SERVICE_ACCOUNT, prompt the user for OAuth2 for service
    783           account email address and private key file (and if the file is a .p12
    784           file, the password for that file).
    785     """
    786     # Collect credentials
    787     provider_map = {'aws': 'aws', 'google': 'gs'}
    788     uri_map = {'aws': 's3', 'google': 'gs'}
    789     key_ids = {}
    790     sec_keys = {}
    791     service_account_key_is_json = False
    792     if cred_type == CredTypes.OAUTH2_SERVICE_ACCOUNT:
    793       gs_service_key_file = raw_input('What is the full path to your private '
    794                                       'key file? ')
    795       # JSON files have the email address built-in and don't require a password.
    796       try:
    797         with open(gs_service_key_file, 'rb') as key_file_fp:
    798           json.loads(key_file_fp.read())
    799         service_account_key_is_json = True
    800       except ValueError:
    801         if not HAS_CRYPTO:
    802           raise CommandException(
    803               'Service account authentication via a .p12 file requires '
    804               'either\nPyOpenSSL or PyCrypto 2.6 or later. Please install '
    805               'either of these\nto proceed, use a JSON-format key file, or '
    806               'configure a different type of credentials.')
    807 
    808       if not service_account_key_is_json:
    809         gs_service_client_id = raw_input('What is your service account email '
    810                                          'address? ')
    811         gs_service_key_file_password = raw_input(
    812             '\n'.join(textwrap.wrap(
    813                 'What is the password for your service key file [if you '
    814                 'haven\'t set one explicitly, leave this line blank]?')) + ' ')
    815       self._CheckPrivateKeyFilePermissions(gs_service_key_file)
    816     elif cred_type == CredTypes.OAUTH2_USER_ACCOUNT:
    817       oauth2_client = oauth2_helper.OAuth2ClientFromBotoConfig(boto.config,
    818                                                                cred_type)
    819       try:
    820         oauth2_refresh_token = oauth2_helper.OAuth2ApprovalFlow(
    821             oauth2_client, oauth2_scopes, launch_browser)
    822       except (ResponseNotReady, ServerNotFoundError, socket.error):
    823         # TODO: Determine condition to check for in the ResponseNotReady
    824         # exception so we only run proxy config flow if failure was caused by
    825         # request being blocked because it wasn't sent through proxy. (This
    826         # error could also happen if gsutil or the oauth2 client had a bug that
    827         # attempted to incorrectly reuse an HTTP connection, for example.)
    828         sys.stdout.write('\n'.join(textwrap.wrap(
    829             "Unable to connect to accounts.google.com during OAuth2 flow. This "
    830             "can happen if your site uses a proxy. If you are using gsutil "
    831             "through a proxy, please enter the proxy's information; otherwise "
    832             "leave the following fields blank.")) + '\n')
    833         self._PromptForProxyConfig()
    834         oauth2_client = oauth2_helper.OAuth2ClientFromBotoConfig(boto.config,
    835                                                                  cred_type)
    836         oauth2_refresh_token = oauth2_helper.OAuth2ApprovalFlow(
    837             oauth2_client, oauth2_scopes, launch_browser)
    838     elif cred_type == CredTypes.HMAC:
    839       got_creds = False
    840       for provider in provider_map:
    841         if provider == 'google':
    842           key_ids[provider] = raw_input('What is your %s access key ID? ' %
    843                                         provider)
    844           sec_keys[provider] = raw_input('What is your %s secret access key? ' %
    845                                          provider)
    846           got_creds = True
    847           if not key_ids[provider] or not sec_keys[provider]:
    848             raise CommandException(
    849                 'Incomplete credentials provided. Please try again.')
    850       if not got_creds:
    851         raise CommandException('No credentials provided. Please try again.')
    852 
    853     # Write the config file prelude.
    854     config_file.write(CONFIG_PRELUDE_CONTENT.lstrip())
    855     config_file.write(
    856         '# This file was created by gsutil version %s at %s.\n'
    857         % (gslib.VERSION,
    858            datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
    859     config_file.write(
    860         '#\n# You can create additional configuration files by '
    861         'running\n# gsutil config [options] [-o <config-file>]\n\n\n')
    862 
    863     # Write the config file Credentials section.
    864     config_file.write('[Credentials]\n\n')
    865     if cred_type == CredTypes.OAUTH2_SERVICE_ACCOUNT:
    866       config_file.write('# Google OAuth2 service account credentials '
    867                         '(for "gs://" URIs):\n')
    868       config_file.write('gs_service_key_file = %s\n' % gs_service_key_file)
    869       if not service_account_key_is_json:
    870         config_file.write('gs_service_client_id = %s\n'
    871                           % gs_service_client_id)
    872 
    873         if not gs_service_key_file_password:
    874           config_file.write(
    875               '# If you would like to set your password, you can do so using\n'
    876               '# the following commands (replaced with your information):\n'
    877               '# "openssl pkcs12 -in cert1.p12 -out temp_cert.pem"\n'
    878               '# "openssl pkcs12 -export -in temp_cert.pem -out cert2.p12"\n'
    879               '# "rm -f temp_cert.pem"\n'
    880               '# Your initial password is "notasecret" - for more information,'
    881               '\n# please see http://www.openssl.org/docs/apps/pkcs12.html.\n')
    882           config_file.write('#gs_service_key_file_password =\n\n')
    883         else:
    884           config_file.write('gs_service_key_file_password = %s\n\n'
    885                             % gs_service_key_file_password)
    886     elif cred_type == CredTypes.OAUTH2_USER_ACCOUNT:
    887       config_file.write(
    888           '# Google OAuth2 credentials (for "gs://" URIs):\n'
    889           '# The following OAuth2 account is authorized for scope(s):\n')
    890       for scope in oauth2_scopes:
    891         config_file.write('#     %s\n' % scope)
    892       config_file.write(
    893           'gs_oauth2_refresh_token = %s\n\n' % oauth2_refresh_token)
    894     else:
    895       config_file.write(
    896           '# To add Google OAuth2 credentials ("gs://" URIs), '
    897           'edit and uncomment the\n# following line:\n'
    898           '#gs_oauth2_refresh_token = <your OAuth2 refresh token>\n\n')
    899 
    900     for provider in provider_map:
    901       key_prefix = provider_map[provider]
    902       uri_scheme = uri_map[provider]
    903       if provider in key_ids and provider in sec_keys:
    904         config_file.write('# %s credentials ("%s://" URIs):\n' %
    905                           (provider, uri_scheme))
    906         config_file.write('%s_access_key_id = %s\n' %
    907                           (key_prefix, key_ids[provider]))
    908         config_file.write('%s_secret_access_key = %s\n' %
    909                           (key_prefix, sec_keys[provider]))
    910       else:
    911         config_file.write(
    912             '# To add %s credentials ("%s://" URIs), edit and '
    913             'uncomment the\n# following two lines:\n'
    914             '#%s_access_key_id = <your %s access key ID>\n'
    915             '#%s_secret_access_key = <your %s secret access key>\n' %
    916             (provider, uri_scheme, key_prefix, provider, key_prefix,
    917              provider))
    918       host_key = Provider.HostKeyMap[provider]
    919       config_file.write(
    920           '# The ability to specify an alternate storage host and port\n'
    921           '# is primarily for cloud storage service developers.\n'
    922           '# Setting a non-default gs_host only works if prefer_api=xml.\n'
    923           '#%s_host = <alternate storage host address>\n'
    924           '#%s_port = <alternate storage host port>\n'
    925           % (host_key, host_key))
    926       if host_key == 'gs':
    927         config_file.write(
    928             '#%s_json_host = <alternate JSON API storage host address>\n'
    929             '#%s_json_port = <alternate JSON API storage host port>\n\n'
    930             % (host_key, host_key))
    931       config_file.write('\n')
    932 
    933     # Write the config file Boto section.
    934     config_file.write('%s\n' % CONFIG_BOTO_SECTION_CONTENT)
    935     self._WriteProxyConfigFileSection(config_file)
    936 
    937     # Write the config file GSUtil section that doesn't depend on user input.
    938     config_file.write(CONFIG_INPUTLESS_GSUTIL_SECTION_CONTENT)
    939 
    940     # Write the default API version.
    941     config_file.write("""
    942 # 'default_api_version' specifies the default Google Cloud Storage XML API
    943 # version to use. If not set below gsutil defaults to API version 1.
    944 """)
    945     api_version = 2
    946     if cred_type == CredTypes.HMAC: api_version = 1
    947 
    948     config_file.write('default_api_version = %d\n' % api_version)
    949 
    950     # Write the config file GSUtil section that includes the default
    951     # project ID input from the user.
    952     if launch_browser:
    953       sys.stdout.write(
    954           'Attempting to launch a browser to open the Google Cloud Console at '
    955           'URL: %s\n\n'
    956           '[Note: due to a Python bug, you may see a spurious error message '
    957           '"object is not\ncallable [...] in [...] Popen.__del__" which can '
    958           'be ignored.]\n\n' % GOOG_CLOUD_CONSOLE_URI)
    959       sys.stdout.write(
    960           'In your browser you should see the Cloud Console. Find the project '
    961           'you will\nuse, and then copy the Project ID string from the second '
    962           'column. Older projects do\nnot have Project ID strings. For such '
    963           'projects, click the project and then copy the\nProject Number '
    964           'listed under that project.\n\n')
    965       if not webbrowser.open(GOOG_CLOUD_CONSOLE_URI, new=1, autoraise=True):
    966         sys.stdout.write(
    967             'Launching browser appears to have failed; please navigate a '
    968             'browser to the following URL:\n%s\n' % GOOG_CLOUD_CONSOLE_URI)
    969       # Short delay; webbrowser.open on linux insists on printing out a message
    970       # which we don't want to run into the prompt for the auth code.
    971       time.sleep(2)
    972     else:
    973       sys.stdout.write(
    974           '\nPlease navigate your browser to %s,\nthen find the project you '
    975           'will use, and copy the Project ID string from the\nsecond column. '
    976           'Older projects do not have Project ID strings. For such projects,\n'
    977           'click the project and then copy the Project Number listed under '
    978           'that project.\n\n' % GOOG_CLOUD_CONSOLE_URI)
    979     default_project_id = raw_input('What is your project-id? ').strip()
    980     project_id_section_prelude = """
    981 # 'default_project_id' specifies the default Google Cloud Storage project ID to
    982 # use with the 'mb' and 'ls' commands. This default can be overridden by
    983 # specifying the -p option to the 'mb' and 'ls' commands.
    984 """
    985     if not default_project_id:
    986       raise CommandException(
    987           'No default project ID entered. The default project ID is needed by '
    988           'the\nls and mb commands; please try again.')
    989     config_file.write('%sdefault_project_id = %s\n\n\n' %
    990                       (project_id_section_prelude, default_project_id))
    991 
    992     # Write the config file OAuth2 section.
    993     config_file.write(CONFIG_OAUTH2_CONFIG_CONTENT)
    994 
    995   def RunCommand(self):
    996     """Command entry point for the config command."""
    997     scopes = []
    998     cred_type = CredTypes.OAUTH2_USER_ACCOUNT
    999     launch_browser = False
   1000     output_file_name = None
   1001     has_a = False
   1002     has_e = False
   1003     for opt, opt_arg in self.sub_opts:
   1004       if opt == '-a':
   1005         cred_type = CredTypes.HMAC
   1006         has_a = True
   1007       elif opt == '-b':
   1008         launch_browser = True
   1009       elif opt == '-e':
   1010         cred_type = CredTypes.OAUTH2_SERVICE_ACCOUNT
   1011         has_e = True
   1012       elif opt == '-f':
   1013         scopes.append(SCOPE_FULL_CONTROL)
   1014       elif opt == '-o':
   1015         output_file_name = opt_arg
   1016       elif opt == '-r':
   1017         scopes.append(SCOPE_READ_ONLY)
   1018       elif opt == '-s':
   1019         scopes.append(opt_arg)
   1020       elif opt == '-w':
   1021         scopes.append(SCOPE_READ_WRITE)
   1022       else:
   1023         self.RaiseInvalidArgumentException()
   1024 
   1025     if has_e and has_a:
   1026       raise CommandException('Both -a and -e cannot be specified. Please see '
   1027                              '"gsutil help config" for more information.')
   1028 
   1029     if not scopes:
   1030       scopes.append(SCOPE_FULL_CONTROL)
   1031 
   1032     default_config_path_bak = None
   1033     if not output_file_name:
   1034       # Check to see if a default config file name is requested via
   1035       # environment variable. If so, use it, otherwise use the hard-coded
   1036       # default file. Then use the default config file name, if it doesn't
   1037       # exist or can be moved out of the way without clobbering an existing
   1038       # backup file.
   1039       boto_config_from_env = os.environ.get('BOTO_CONFIG', None)
   1040       if boto_config_from_env:
   1041         default_config_path = boto_config_from_env
   1042       else:
   1043         default_config_path = os.path.expanduser(os.path.join('~', '.boto'))
   1044       if not os.path.exists(default_config_path):
   1045         output_file_name = default_config_path
   1046       else:
   1047         default_config_path_bak = default_config_path + '.bak'
   1048         if os.path.exists(default_config_path_bak):
   1049           raise CommandException(
   1050               'Cannot back up existing config '
   1051               'file "%s": backup file exists ("%s").'
   1052               % (default_config_path, default_config_path_bak))
   1053         else:
   1054           try:
   1055             sys.stderr.write(
   1056                 'Backing up existing config file "%s" to "%s"...\n'
   1057                 % (default_config_path, default_config_path_bak))
   1058             os.rename(default_config_path, default_config_path_bak)
   1059           except Exception, e:
   1060             raise CommandException(
   1061                 'Failed to back up existing config '
   1062                 'file ("%s" -> "%s"): %s.'
   1063                 % (default_config_path, default_config_path_bak, e))
   1064           output_file_name = default_config_path
   1065 
   1066     if output_file_name == '-':
   1067       output_file = sys.stdout
   1068     else:
   1069       output_file = self._OpenConfigFile(output_file_name)
   1070       sys.stderr.write('\n'.join(textwrap.wrap(
   1071           'This command will create a boto config file at %s containing your '
   1072           'credentials, based on your responses to the following questions.'
   1073           % output_file_name)) + '\n')
   1074 
   1075     # Catch ^C so we can restore the backup.
   1076     RegisterSignalHandler(signal.SIGINT, _CleanupHandler)
   1077     try:
   1078       self._WriteBotoConfigFile(output_file, launch_browser=launch_browser,
   1079                                 oauth2_scopes=scopes, cred_type=cred_type)
   1080     except Exception as e:
   1081       user_aborted = isinstance(e, AbortException)
   1082       if user_aborted:
   1083         sys.stderr.write('\nCaught ^C; cleaning up\n')
   1084       # If an error occurred during config file creation, remove the invalid
   1085       # config file and restore the backup file.
   1086       if output_file_name != '-':
   1087         output_file.close()
   1088         os.unlink(output_file_name)
   1089         try:
   1090           if default_config_path_bak:
   1091             sys.stderr.write('Restoring previous backed up file (%s)\n' %
   1092                              default_config_path_bak)
   1093             os.rename(default_config_path_bak, output_file_name)
   1094         except Exception as e:
   1095           # Raise the original exception so that we can see what actually went
   1096           # wrong, rather than just finding out that we died before assigning
   1097           # a value to default_config_path_bak.
   1098           raise e
   1099       raise
   1100 
   1101     if output_file_name != '-':
   1102       output_file.close()
   1103       if not boto.config.has_option('Boto', 'proxy'):
   1104         sys.stderr.write('\n' + '\n'.join(textwrap.wrap(
   1105             'Boto config file "%s" created.\nIf you need to use a proxy to '
   1106             'access the Internet please see the instructions in that file.'
   1107             % output_file_name)) + '\n')
   1108 
   1109     return 0
   1110 
   1111 
   1112 def _CleanupHandler(unused_signalnum, unused_handler):
   1113   raise AbortException('User interrupted config command')
   1114