1#!/usr/bin/env python
2#
3# Copyright 2013 Google Inc. All Rights Reserved.
4#
5
6"""A convenience wrapper for starting gsutil."""
7
8from __future__ import absolute_import
9from __future__ import unicode_literals
10import os
11
12
13import bootstrapping
14from googlecloudsdk.calliope import exceptions
15from googlecloudsdk.core import config
16from googlecloudsdk.core import context_aware
17from googlecloudsdk.core import metrics
18from googlecloudsdk.core import properties
19from googlecloudsdk.core.credentials import gce as c_gce
20from googlecloudsdk.core.util import encoding
21
22
23def _MaybeAddBotoOption(args, section, name, value):
24  if value is None:
25    return
26  args.append('-o')
27  args.append('{section}:{name}={value}'.format(
28      section=section, name=name, value=value))
29
30
31def main():
32  """Launches gsutil."""
33
34  args = []
35
36  project, account = bootstrapping.GetActiveProjectAndAccount()
37  pass_credentials = (
38      properties.VALUES.core.pass_credentials_to_gsutil.GetBool() and
39      not properties.VALUES.auth.disable_credentials.GetBool())
40
41  _MaybeAddBotoOption(args, 'GSUtil', 'default_project_id', project)
42
43  if pass_credentials:
44    # Allow gsutil to only check for the '1' string value, as is done
45    # with regard to the 'CLOUDSDK_WRAPPER' environment variable.
46    encoding.SetEncodedValue(
47        os.environ, 'CLOUDSDK_CORE_PASS_CREDENTIALS_TO_GSUTIL', '1')
48
49    if account in c_gce.Metadata().Accounts():
50      # Tell gsutil that it should obtain credentials from the GCE metadata
51      # server for the instance's configured service account.
52      _MaybeAddBotoOption(args, 'GoogleCompute', 'service_account', 'default')
53      # For auth'n debugging purposes, allow gsutil to reason about whether the
54      # configured service account was set in a boto file or passed from here.
55      encoding.SetEncodedValue(
56          os.environ, 'CLOUDSDK_PASSED_GCE_SERVICE_ACCOUNT_TO_GSUTIL', '1')
57    else:
58      legacy_config_path = config.Paths().LegacyCredentialsGSUtilPath(account)
59      # We construct a BOTO_PATH that tacks the config containing our
60      # credentials options onto the end of the list of config paths. We ensure
61      # the other credential options are loaded first so that ours will take
62      # precedence and overwrite them.
63      boto_config = encoding.GetEncodedValue(os.environ, 'BOTO_CONFIG', '')
64      boto_path = encoding.GetEncodedValue(os.environ, 'BOTO_PATH', '')
65      if boto_config:
66        boto_path = os.pathsep.join([boto_config, legacy_config_path])
67      elif boto_path:
68        boto_path = os.pathsep.join([boto_path, legacy_config_path])
69      else:
70        path_parts = ['/etc/boto.cfg',
71                      os.path.expanduser(os.path.join('~', '.boto')),
72                      legacy_config_path]
73        boto_path = os.pathsep.join(path_parts)
74
75      encoding.SetEncodedValue(os.environ, 'BOTO_CONFIG', None)
76      encoding.SetEncodedValue(os.environ, 'BOTO_PATH', boto_path)
77
78  # Tell gsutil whether gcloud analytics collection is enabled.
79  encoding.SetEncodedValue(
80      os.environ, 'GA_CID', metrics.GetCIDIfMetricsEnabled())
81
82  # Set proxy settings. Note that if these proxy settings are configured in a
83  # boto config file, the options here will be loaded afterward, overriding
84  # them.
85  proxy_params = properties.VALUES.proxy
86  proxy_address = proxy_params.address.Get()
87  if proxy_address:
88    _MaybeAddBotoOption(args, 'Boto', 'proxy', proxy_address)
89    _MaybeAddBotoOption(args, 'Boto', 'proxy_port', proxy_params.port.Get())
90    _MaybeAddBotoOption(args, 'Boto', 'proxy_rdns', proxy_params.rdns.GetBool())
91    _MaybeAddBotoOption(args, 'Boto', 'proxy_user', proxy_params.username.Get())
92    _MaybeAddBotoOption(args, 'Boto', 'proxy_pass', proxy_params.password.Get())
93
94  # Set SSL-related settings.
95  disable_ssl = properties.VALUES.auth.disable_ssl_validation.GetBool()
96  _MaybeAddBotoOption(args, 'Boto', 'https_validate_certificates',
97                      None if disable_ssl is None else not disable_ssl)
98  _MaybeAddBotoOption(args, 'Boto', 'ca_certificates_file',
99                      properties.VALUES.core.custom_ca_certs_file.Get())
100
101  # Sync device certificate settings for mTLS.
102  context_config = context_aware.Config()
103  _MaybeAddBotoOption(args, 'Credentials', 'use_client_certificate',
104                      context_config.use_client_certificate)
105  if context_config.use_client_certificate:
106    # Don't need to pass mTLS data if gsutil shouldn't be using it.
107    _MaybeAddBotoOption(args, 'Credentials', 'cert_provider_command',
108                        context_config.cert_provider_command)
109
110  # Note that the original args to gsutil will be appended after the args we've
111  # supplied here.
112  bootstrapping.ExecutePythonTool('platform/gsutil', 'gsutil', *args)
113
114
115if __name__ == '__main__':
116  try:
117    version = bootstrapping.ReadFileContents('platform/gsutil', 'VERSION')
118    bootstrapping.CommandStart('gsutil', version=version)
119
120    blocked_commands = {
121        'update': 'To update, run: gcloud components update',
122    }
123
124    argv = bootstrapping.GetDecodedArgv()
125    bootstrapping.WarnAndExitOnBlockedCommand(argv, blocked_commands)
126
127    # Don't call bootstrapping.PreRunChecks because anonymous access is
128    # supported for some endpoints. gsutil will output the appropriate
129    # error message upon receiving an authentication error.
130    bootstrapping.CheckUpdates('gsutil')
131    main()
132  except Exception as e:  # pylint: disable=broad-except
133    exceptions.HandleError(e, 'gsutil')
134