File size: 12,333 Bytes
4a701b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# Copyright 2013 Google Inc. All Rights Reserved.

"""Common bootstrapping functionality used by the wrapper scripts."""

# Disables import order warning and unused import.  Setup changes the python
# path so cloud sdk imports will actually work, so it must come first.
# pylint: disable=C6203
# pylint: disable=W0611
from __future__ import absolute_import
from __future__ import unicode_literals

# Python 3 is strict about imports and we use this file in different ways, which
# makes sub-imports difficult. In general, when a script is executed, that
# directory is put on the PYTHONPATH. The issue is that some of the wrapper
# scripts are executed from within the bootstrapping/ directory and some are
# executed from within the bin/ directory.
# pylint: disable=g-statement-before-imports
if '.' in __name__:
  # Here, __name__ will be bootstrapping.bootstrapping. This indicates that this
  # file was loaded as a member of package bootstrapping. This in turn indicates
  # that the main file that was executed was not in the bootstrapping directory,
  # so bin/ is on the path and bootstrapping is considered a python package.
  # Do an import of setup from this current package.
  from . import setup  # pylint:disable=g-import-not-at-top
else:
  # In this case, __name__ is bootstrapping, which indicates that the main
  # script was executed from within this directory meaning that Python doesn't
  # consider this a package but rather the root of the PYTHONPATH. We can't do
  # the above import because since we are not in a package, the '.' doesn't
  # refer to anything. Just do a direct import which will find setup on the
  # PYTHONPATH (which is just this directory).
  import setup  # pylint:disable=g-import-not-at-top

import gcloud
import sys
# Reorder sys.path if needed right now before more modules loaded and cached
sys.path = gcloud.reorder_sys_path(sys.path)

# pylint: disable=g-import-not-at-top
import json
# pylint: enable=g-import-not-at-top
import os
import platform

from googlecloudsdk.core import config
from googlecloudsdk.core import execution_utils
from googlecloudsdk.core import metrics
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_attr
from googlecloudsdk.core.credentials import store as c_store
from googlecloudsdk.core.updater import local_state
from googlecloudsdk.core.updater import update_manager
from googlecloudsdk.core.util import encoding
from googlecloudsdk.core.util import files
from googlecloudsdk.core.util import platforms
from six.moves import input


BOOTSTRAPPING_DIR = os.path.dirname(os.path.realpath(__file__))
BIN_DIR = os.path.dirname(BOOTSTRAPPING_DIR)
SDK_ROOT = os.path.dirname(BIN_DIR)


def DisallowIncompatiblePythonVersions():
  if not platforms.PythonVersion().IsCompatible():
    sys.exit(1)


def GetDecodedArgv():
  return [console_attr.Decode(arg) for arg in sys.argv]


def _FullPath(tool_dir, exec_name):
  return os.path.join(SDK_ROOT, tool_dir, exec_name)


def ExecutePythonTool(tool_dir, exec_name, *args):
  """Execute the given python script with the given args and command line.

  Args:
    tool_dir: the directory the tool is located in
    exec_name: additional path to the executable under the tool_dir
    *args: args for the command
  """
  py_path = None  # Let execution_utils resolve the path.
  # Gsutil allows users to set the desired Python interpreter using a separate
  # environment variable, so as to allow users to run gsutil using Python 3
  # without forcing the rest of the Cloud SDK to use Python 3 (as it would
  # likely break at the time this comment was written).
  extra_popen_kwargs = {}
  if exec_name == 'gsutil':
    gsutil_py = encoding.GetEncodedValue(os.environ, 'CLOUDSDK_GSUTIL_PYTHON')
    # Since PY3, Python closes open FDs in child processes, since we need them
    # open for completions to work we set the close_fds kwarg to Popen.
    extra_popen_kwargs['close_fds'] = False
    if gsutil_py:
      py_path = gsutil_py

  if exec_name == 'bq.py':
    bq_py = encoding.GetEncodedValue(os.environ, 'CLOUDSDK_BQ_PYTHON')
    if bq_py:
      py_path = bq_py

  if exec_name == 'dev_appserver.py':
    if platforms.OperatingSystem.Current() != platforms.OperatingSystem.WINDOWS:
      # Hard code the python to 'python2' which should exist on most Unix based
      # systems.
      py_path = 'python2'
    else:
      # On windows attempt to use the bundled PY2 component.
      bundled_py2 = os.path.join(
          SDK_ROOT, 'platform', 'bundledpython2', 'python.exe')
      if os.path.exists(bundled_py2):
        py_path = bundled_py2

    # Set this ENV var to explicitly select the python binary to use for
    # dev_appserver.py
    devapp_env_py = encoding.GetEncodedValue(os.environ,
                                             'CLOUDSDK_DEVAPPSERVER_PYTHON')
    if devapp_env_py:
      py_path = devapp_env_py

  _ExecuteTool(
      execution_utils.ArgsForPythonTool(
          _FullPath(tool_dir, exec_name), *args, python=py_path),
      **extra_popen_kwargs)


def ExecuteJarTool(java_bin, jar_dir, jar_name, classname, flags=None, *args):
  """Execute a given jar with the given args and command line.

  Args:
    java_bin: str, path to the system Java binary
    jar_dir: str, the directory the jar is located in
    jar_name: str, file name of the jar under tool_dir
    classname: str, name of the main class in the jar
    flags: [str], flags for the java binary
    *args: args for the command
  """
  flags = flags or []
  jar_path = _FullPath(jar_dir, jar_name)
  classname_arg = [classname] if classname else []
  java_args = ['-cp', jar_path] + flags + classname_arg + list(args)
  _ExecuteTool(
      execution_utils.ArgsForExecutableTool(java_bin, *java_args))


def ExecuteJavaClass(java_bin,
                     jar_dir,
                     main_jar,
                     main_class,
                     java_flags=None,
                     main_args=None):
  """Execute a given java class within a directory of jars.

  Args:
    java_bin: str, path to the system Java binary
    jar_dir: str, directory of jars to put on class path
    main_jar: str, main jar (placed first on class path)
    main_class: str, name of the main class in the jar
    java_flags: [str], flags for the java binary
    main_args: args for the command
  """
  java_flags = java_flags or []
  main_args = main_args or []
  jar_dir_path = os.path.join(SDK_ROOT, jar_dir, '*')
  main_jar_path = os.path.join(SDK_ROOT, jar_dir, main_jar)
  classpath = main_jar_path + os.pathsep + jar_dir_path
  java_args = (['-cp', classpath]
               + list(java_flags)
               + [main_class]
               + list(main_args))
  _ExecuteTool(execution_utils.ArgsForExecutableTool(java_bin, *java_args))


def ExecuteShellTool(tool_dir, exec_name, *args):
  """Execute the given bash script with the given args.

  Args:
    tool_dir: the directory the tool is located in
    exec_name: additional path to the executable under the tool_dir
    *args: args for the command
  """
  _ExecuteTool(
      execution_utils.ArgsForExecutableTool(_FullPath(tool_dir, exec_name),
                                            *args))


def ExecuteCMDTool(tool_dir, exec_name, *args):
  """Execute the given batch file with the given args.

  Args:
    tool_dir: the directory the tool is located in
    exec_name: additional path to the executable under the tool_dir
    *args: args for the command
  """
  _ExecuteTool(
      execution_utils.ArgsForCMDTool(_FullPath(tool_dir, exec_name), *args))


def _GetToolEnv():
  env = dict(os.environ)
  encoding.SetEncodedValue(env, 'CLOUDSDK_WRAPPER', '1')
  encoding.SetEncodedValue(env, 'CLOUDSDK_VERSION', config.CLOUD_SDK_VERSION)
  encoding.SetEncodedValue(env, 'CLOUDSDK_PYTHON',
                           execution_utils.GetPythonExecutable())
  return env


def _ExecuteTool(args, **extra_popen_kwargs):
  """Executes a new tool with the given args, plus the args from the cmdline.

  Args:
    args: [str], The args of the command to execute.
    **extra_popen_kwargs: [dict], kwargs to be unpacked in Popen call for tool.
  """
  execution_utils.Exec(
      args + sys.argv[1:], env=_GetToolEnv(), **extra_popen_kwargs)


def GetDefaultInstalledComponents():
  """Gets the list of components to install by default.

  Returns:
    list(str), The component ids that should be installed.  It will return []
    if there are no default components, or if there is any error in reading
    the file with the defaults.
  """
  default_components_file = os.path.join(BOOTSTRAPPING_DIR,
                                         '.default_components')
  try:
    with open(default_components_file) as f:
      return json.load(f)
  # pylint:disable=bare-except, If the file does not exist or is malformed,
  # we don't want to expose this as an error.  Setup will just continue
  # without installing any components by default and will tell the user how
  # to install the components they want manually.
  except:
    pass
  return []


def WarnAndExitOnBlockedCommand(args, blocked_commands):
  """Block certain subcommands, warn the user, and exit.

  Args:
    args: the command line arguments, including the 0th argument which is
      the program name.
    blocked_commands: a map of blocked commands to the messages that should be
      printed when they're run.
  """
  bad_arg = None
  for arg in args[1:]:
    # Flags are skipped and --flag=value are skipped. It is possible for
    # '--flag value' to result in a false positive if value happens to be a
    # blocked command.
    if arg and arg[0] == '-':
      continue
    if arg in blocked_commands:
      bad_arg = arg
      break

  blocked = bad_arg is not None

  if blocked:
    sys.stderr.write('It looks like you are trying to run "%s %s".\n'
                     % (args[0], bad_arg))
    sys.stderr.write('The "%s" command is no longer needed with the '
                     'Cloud SDK.\n' % bad_arg)
    sys.stderr.write(blocked_commands[bad_arg] + '\n')
    answer = input('Really run this command? (y/N) ')
    if answer not in ['y', 'Y']:
      sys.exit(1)


def CheckUpdates(command_path):
  """Check for updates and inform the user.

  Args:
    command_path: str, The '.' separated path of the command that is currently
      being run (i.e. gcloud.foo.bar).
  """
  try:
    update_manager.UpdateManager.PerformUpdateCheck(command_path=command_path)
  # pylint:disable=broad-except, We never want this to escape, ever. Only
  # messages printed should reach the user.
  except Exception:
    pass


def CommandStart(command_name, component_id=None, version=None):
  """Logs that the given command is being executed.

  Args:
    command_name: str, The name of the command being executed.
    component_id: str, The component id that this command belongs to.  Used for
      version information if version was not specified.
    version: str, Directly use this version instead of deriving it from
      component.
  """
  if version is None and component_id:
    version = local_state.InstallationState.VersionForInstalledComponent(
        component_id)
  metrics.Executions(command_name, version)


def GetActiveProjectAndAccount():
  """Get the active project name and account for the active credentials.

  For use with wrapping legacy tools that take projects and credentials on
  the command line.

  Returns:
    (str, str), A tuple whose first element is the project, and whose second
    element is the account.
  """
  project_name = properties.VALUES.core.project.Get(validate=False)
  account = properties.VALUES.core.account.Get(validate=False)
  return (project_name, account)


def GetActiveImpersonateServiceAccount():
  """Get the active impersonate_service_account property.

  For use with wrapping legacy tools that take impersonate_service_account on
  the command line.

  Returns:
    str, The name of the service account to impersonate.
  """
  return properties.VALUES.auth.impersonate_service_account.Get(validate=False)


def ReadFileContents(*path_parts):
  """Returns file content at specified relative path wrt SDK root path."""
  return files.ReadFileContents(os.path.join(SDK_ROOT, *path_parts)).strip()


# Register some other sources for credentials and project.
c_store.GceCredentialProvider().Register()