diff --git a/gsutil.py b/gsutil.py index d57cafb94..eb339bc41 100755 --- a/gsutil.py +++ b/gsutil.py @@ -29,6 +29,9 @@ DEFAULT_BIN_DIR = os.path.join(THIS_DIR, 'external_bin', 'gsutil') DEFAULT_FALLBACK_GSUTIL = os.path.join( THIS_DIR, 'third_party', 'gsutil', 'gsutil') +IS_WINDOWS = os.name == 'nt' + + class InvalidGsutilError(Exception): pass @@ -126,8 +129,17 @@ def run_gsutil(force_version, fallback, target, args, clean=False): else: gsutil_bin = fallback disable_update = ['-o', 'GSUtil:software_update_check_period=0'] - cmd = [sys.executable, gsutil_bin] + disable_update + args - return subprocess.call(cmd) + + # Run "gsutil" through "vpython". We need to do this because on GCE instances, + # expectations are made about Python having access to "google-compute-engine" + # and "boto" packages that are not met with non-system Python (e.g., bundles). + cmd = [ + 'vpython', + '-vpython-spec', os.path.join(THIS_DIR, 'gsutil.vpython'), + '--', + gsutil_bin + ] + disable_update + args + return subprocess.call(cmd, shell=IS_WINDOWS) def parse_args(): diff --git a/gsutil.vpython b/gsutil.vpython new file mode 100644 index 000000000..caadd5cf1 --- /dev/null +++ b/gsutil.vpython @@ -0,0 +1,102 @@ +# vpython VirtualEnv protobuf for "gsutil". +# +# See: +# https://chromium.googlesource.com/infra/luci/luci-go/+/master/vpython/api/vpython/spec.proto +# +# This is a "vpython" VirtualEnv specification applied to invocations of "gsutil" +# by the bootstrap wrapper, "gsutil.py". It ensures that any Python distribution +# has the expected Python packages installed. +# +# This is specifically relevant on Google Compute Engine invocations of +# "gsutil", where a stock system-deployed file in "/etc/" explicitly specifies +# that the "google_compute_engine" and "boto" Python packages are available. + +wheel < + name: "infra/python/wheels/google_compute_engine-py2_py3" + version: "version:2.3.7" +> +wheel < + name: "infra/python/wheels/boto-py2_py3" + version: "version:2.47.0" +> + +# "gsutil" on non-GCE can require PyOpenSSL, which, in turn, requires +# "cryptography". + +wheel: < + name: "infra/python/wheels/pyopenssl-py2_py3" + version: "version:17.2.0" +> + +## +# BEGIN "cryptography" dependencies. +## + +wheel: < + name: "infra/python/wheels/cryptography/${platform}_${py_python}_${py_abi}" + version: "version:2.0.3" +> + +wheel: < + name: "infra/python/wheels/appdirs-py2_py3" + version: "version:1.4.3" +> + +wheel: < + name: "infra/python/wheels/asn1crypto-py2_py3" + version: "version:0.22.0" +> + +wheel: < + name: "infra/python/wheels/enum34-py2" + version: "version:1.1.6" +> + +wheel: < + name: "infra/python/wheels/cffi/${platform}_${py_python}_${py_abi}" + version: "version:1.10.0" +> + +wheel: < + name: "infra/python/wheels/idna-py2_py3" + version: "version:2.5" +> + +wheel: < + name: "infra/python/wheels/ipaddress-py2" + version: "version:1.0.18" +> + +wheel: < + name: "infra/python/wheels/packaging-py2_py3" + version: "version:16.8" +> + +wheel: < + name: "infra/python/wheels/pyasn1-py2_py3" + version: "version:0.2.3" +> + +wheel: < + name: "infra/python/wheels/pycparser-py2_py3" + version: "version:2.17" +> + +wheel: < + name: "infra/python/wheels/pyparsing-py2_py3" + version: "version:2.2.0" +> + +wheel: < + name: "infra/python/wheels/setuptools-py2_py3" + version: "version:34.3.2" +> + +wheel: < + name: "infra/python/wheels/six-py2_py3" + version: "version:1.10.0" +> + +## +# END "cryptography" dependencies. +##