#!/usr/bin/env python # # Copyright 2018 - The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """google SDK tools installer. This class will return the path to the google sdk tools and download them into a temporary dir if they don't exist. The caller is expected to call cleanup when they're done. Example usage: google_sdk = GoogleSDK() google_sdk_bin_path = google_sdk.GetSDKBinPath() # Caller is done. google_sdk.CleanUp() """ import logging import os import platform import shutil import sys import tempfile from six.moves import urllib from acloud import errors from acloud.internal.lib import utils logger = logging.getLogger(__name__) SDK_BIN_PATH = os.path.join("google-cloud-sdk", "bin") GCLOUD_BIN = "gcloud" GCLOUD_COMPONENT_NOT_INSTALLED = "Not Installed" GCP_SDK_VERSION = "209.0.0" GCP_SDK_TOOLS_URL = "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads" LINUX_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-linux-x86_64.tar.gz" % ( GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) LINUX_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-linux-x86.tar.gz" % ( GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) WIN_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-windows-x86_64.zip" % ( GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) WIN_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-windows-x86.zip" % ( GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) MAC_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-darwin-x86_64.tar.gz" % ( GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) MAC_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-darwin-x86.tar.gz" % ( GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) LINUX = "linux" WIN = "windows" MAC = "darwin" def GetSdkUrl(): """Get google SDK tool url. Return: String, a URL of google SDK tools. Raises: OSTypeError when OS type is neither linux, mac, or windows. """ is_64bits = sys.maxsize > 2**32 os_type = platform.system().lower() if is_64bits: if os_type == LINUX: return LINUX_GCP_SDK_64_URL if os_type == MAC: return MAC_GCP_SDK_64_URL if os_type == WIN: return WIN_GCP_SDK_64_URL else: if os_type == LINUX: return LINUX_GCP_SDK_32_URL if os_type == MAC: return MAC_GCP_SDK_32_URL if os_type == WIN: return WIN_GCP_SDK_32_URL raise errors.OSTypeError("no gcloud for os type: %s" % (os_type)) def SDKInstalled(): """Check google SDK tools installed. We'll try to find where gcloud is and assume the other google sdk tools live in the same location. Return: Boolean, return True if gcloud is installed, False otherwise. """ if utils.FindExecutable(GCLOUD_BIN): return True return False class GoogleSDK(): """Google SDK tools installer.""" def __init__(self): """GoogleSDKInstaller initialize. Make sure the GCloud SDK is installed. If not, this function will assist users to install. """ self._tmp_path = None self._tmp_sdk_path = None if not SDKInstalled(): self.DownloadGcloudSDKAndExtract() @staticmethod def InstallGcloudComponent(gcloud_runner, component): """Install gcloud component. Args: gcloud_runner: A GcloudRunner class to run "gcloud" command. component: String, name of gcloud component. """ result = gcloud_runner.RunGcloud([ "components", "list", "--format", "get(state.name)", "--filter", "ID=%s" % component ]) if result.strip() == GCLOUD_COMPONENT_NOT_INSTALLED: gcloud_runner.RunGcloud( ["components", "install", "--quiet", component]) def GetSDKBinPath(self): """Get google SDK tools bin path. We assumed there are google sdk tools somewhere and will raise if we can't find it. The order we're looking for is: 1. Builtin gcloud (usually /usr/bin), we'll return /usr/bin with the assumption other sdk tools live there. 2. Downloaded google sdk (self._tmp_dir), we assumed the caller already downloaded/extracted and set the self._tmp_sdk_path for us to return. We'll make sure it exists prior to returning it. Return: String, return google SDK tools bin path. Raise: NoGoogleSDKDetected if we can't find the sdk path. """ builtin_gcloud = utils.FindExecutable(GCLOUD_BIN) if builtin_gcloud: return os.path.dirname(builtin_gcloud) if os.path.exists(self._tmp_sdk_path): return self._tmp_sdk_path raise errors.NoGoogleSDKDetected("no sdk path.") def DownloadGcloudSDKAndExtract(self): """Download the google SDK tools and decompress it. Download the google SDK from the GCP web. Reference https://cloud.google.com/sdk/docs/downloads-versioned-archives. """ self._tmp_path = tempfile.mkdtemp(prefix="gcloud") url = GetSdkUrl() filename = url[url.rfind("/") + 1:] file_path = os.path.join(self._tmp_path, filename) logger.info("Download file from: %s", url) logger.info("Save the file to: %s", file_path) url_stream = urllib.request.urlopen(url) metadata = url_stream.info() file_size = int(metadata.get("Content-Length")) logger.info("Downloading google SDK: %s bytes.", file_size) with open(file_path, 'wb') as output: output.write(url_stream.read()) utils.Decompress(file_path, self._tmp_path) self._tmp_sdk_path = os.path.join(self._tmp_path, SDK_BIN_PATH) def CleanUp(self): """Clean google sdk tools install folder.""" if self._tmp_path and os.path.exists(self._tmp_path): shutil.rmtree(self._tmp_path)