1#!/usr/bin/env python 2# 3# Copyright 2018 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16"""google SDK tools installer. 17 18This class will return the path to the google sdk tools and download them into 19a temporary dir if they don't exist. The caller is expected to call cleanup 20when they're done. 21 22Example usage: 23google_sdk = GoogleSDK() 24google_sdk_bin_path = google_sdk.GetSDKBinPath() 25 26# Caller is done. 27google_sdk.CleanUp() 28""" 29 30from distutils.spawn import find_executable 31import logging 32import os 33import platform 34import shutil 35import sys 36import tempfile 37import urllib2 38 39from acloud import errors 40from acloud.internal.lib import utils 41 42SDK_BIN_PATH = os.path.join("google-cloud-sdk", "bin") 43GCLOUD_BIN = "gcloud" 44GCP_SDK_VERSION = "209.0.0" 45GCP_SDK_TOOLS_URL = "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads" 46LINUX_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-linux-x86_64.tar.gz" % ( 47 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 48LINUX_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-linux-x86.tar.gz" % ( 49 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 50WIN_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-windows-x86_64.zip" % ( 51 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 52WIN_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-windows-x86.zip" % ( 53 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 54MAC_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-darwin-x86_64.tar.gz" % ( 55 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 56MAC_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-darwin-x86.tar.gz" % ( 57 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 58LINUX = "linux" 59WIN = "windows" 60MAC = "darwin" 61 62logger = logging.getLogger(__name__) 63 64 65def GetSdkUrl(): 66 """Get google SDK tool url. 67 68 Return: 69 String, a URL of google SDK tools. 70 71 Raises: 72 OSTypeError when OS type is neither linux, mac, or windows. 73 """ 74 is_64bits = sys.maxsize > 2**32 75 os_type = platform.system().lower() 76 if is_64bits: 77 if os_type == LINUX: 78 return LINUX_GCP_SDK_64_URL 79 elif os_type == MAC: 80 return MAC_GCP_SDK_64_URL 81 elif os_type == WIN: 82 return WIN_GCP_SDK_64_URL 83 else: 84 if os_type == LINUX: 85 return LINUX_GCP_SDK_32_URL 86 elif os_type == MAC: 87 return MAC_GCP_SDK_32_URL 88 elif os_type == WIN: 89 return WIN_GCP_SDK_32_URL 90 raise errors.OSTypeError("no gcloud for os type: %s" % (os_type)) 91 92 93def SDKInstalled(): 94 """Check google SDK tools installed. 95 96 We'll try to find where gcloud is and assume the other google sdk tools 97 live in the same location. 98 99 Return: 100 Boolean, return True if gcloud is installed, False otherwise. 101 """ 102 if find_executable(GCLOUD_BIN): 103 return True 104 return False 105 106 107class GoogleSDK(object): 108 """Google SDK tools installer.""" 109 110 def __init__(self): 111 """GoogleSDKInstaller initialize. 112 113 Make sure the GCloud SDK is installed. If not, this function will assist 114 users to install. 115 """ 116 self._tmp_path = None 117 self._tmp_sdk_path = None 118 if not SDKInstalled(): 119 self.DownloadGcloudSDKAndExtract() 120 121 def GetSDKBinPath(self): 122 """Get google SDK tools bin path. 123 124 We assumed there are google sdk tools somewhere and will raise if we 125 can't find it. The order we're looking for is: 126 1. Builtin gcloud (usually /usr/bin), we'll return /usr/bin with the 127 assumption other sdk tools live there. 128 2. Downloaded google sdk (self._tmp_dir), we assumed the caller already 129 downloaded/extracted and set the self._tmp_sdk_path for us to return. 130 We'll make sure it exists prior to returning it. 131 132 Return: 133 String, return google SDK tools bin path. 134 135 Raise: 136 NoGoogleSDKDetected if we can't find the sdk path. 137 """ 138 builtin_gcloud = find_executable(GCLOUD_BIN) 139 if builtin_gcloud: 140 return os.path.dirname(builtin_gcloud) 141 elif os.path.exists(self._tmp_sdk_path): 142 return self._tmp_sdk_path 143 raise errors.NoGoogleSDKDetected("no sdk path.") 144 145 def DownloadGcloudSDKAndExtract(self): 146 """Download the google SDK tools and decompress it. 147 148 Download the google SDK from the GCP web. 149 Reference https://cloud.google.com/sdk/docs/downloads-versioned-archives. 150 """ 151 self._tmp_path = tempfile.mkdtemp(prefix="gcloud") 152 url = GetSdkUrl() 153 filename = url[url.rfind("/") + 1:] 154 file_path = os.path.join(self._tmp_path, filename) 155 logger.info("Download file from: %s", url) 156 logger.info("Save the file to: %s", file_path) 157 url_stream = urllib2.urlopen(url) 158 metadata = url_stream.info() 159 file_size = int(metadata.getheaders("Content-Length")[0]) 160 logger.info("Downloading google SDK: %s bytes.", file_size) 161 with open(file_path, 'wb') as output: 162 output.write(url_stream.read()) 163 utils.Decompress(file_path, self._tmp_path) 164 self._tmp_sdk_path = os.path.join(self._tmp_path, SDK_BIN_PATH) 165 166 def CleanUp(self): 167 """Clean google sdk tools install folder.""" 168 if self._tmp_path and os.path.exists(self._tmp_path): 169 shutil.rmtree(self._tmp_path) 170