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 30import logging 31import os 32import platform 33import shutil 34import sys 35import tempfile 36from six.moves import urllib 37 38from acloud import errors 39from acloud.internal.lib import utils 40 41 42logger = logging.getLogger(__name__) 43 44SDK_BIN_PATH = os.path.join("google-cloud-sdk", "bin") 45GCLOUD_BIN = "gcloud" 46GCLOUD_COMPONENT_NOT_INSTALLED = "Not Installed" 47GCP_SDK_VERSION = "209.0.0" 48GCP_SDK_TOOLS_URL = "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads" 49LINUX_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-linux-x86_64.tar.gz" % ( 50 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 51LINUX_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-linux-x86.tar.gz" % ( 52 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 53WIN_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-windows-x86_64.zip" % ( 54 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 55WIN_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-windows-x86.zip" % ( 56 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 57MAC_GCP_SDK_64_URL = "%s/google-cloud-sdk-%s-darwin-x86_64.tar.gz" % ( 58 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 59MAC_GCP_SDK_32_URL = "%s/google-cloud-sdk-%s-darwin-x86.tar.gz" % ( 60 GCP_SDK_TOOLS_URL, GCP_SDK_VERSION) 61LINUX = "linux" 62WIN = "windows" 63MAC = "darwin" 64 65 66def GetSdkUrl(): 67 """Get google SDK tool url. 68 69 Return: 70 String, a URL of google SDK tools. 71 72 Raises: 73 OSTypeError when OS type is neither linux, mac, or windows. 74 """ 75 is_64bits = sys.maxsize > 2**32 76 os_type = platform.system().lower() 77 if is_64bits: 78 if os_type == LINUX: 79 return LINUX_GCP_SDK_64_URL 80 elif os_type == MAC: 81 return MAC_GCP_SDK_64_URL 82 elif os_type == WIN: 83 return WIN_GCP_SDK_64_URL 84 else: 85 if os_type == LINUX: 86 return LINUX_GCP_SDK_32_URL 87 elif os_type == MAC: 88 return MAC_GCP_SDK_32_URL 89 elif os_type == WIN: 90 return WIN_GCP_SDK_32_URL 91 raise errors.OSTypeError("no gcloud for os type: %s" % (os_type)) 92 93 94def SDKInstalled(): 95 """Check google SDK tools installed. 96 97 We'll try to find where gcloud is and assume the other google sdk tools 98 live in the same location. 99 100 Return: 101 Boolean, return True if gcloud is installed, False otherwise. 102 """ 103 if utils.FindExecutable(GCLOUD_BIN): 104 return True 105 return False 106 107 108class GoogleSDK(object): 109 """Google SDK tools installer.""" 110 111 def __init__(self): 112 """GoogleSDKInstaller initialize. 113 114 Make sure the GCloud SDK is installed. If not, this function will assist 115 users to install. 116 """ 117 self._tmp_path = None 118 self._tmp_sdk_path = None 119 if not SDKInstalled(): 120 self.DownloadGcloudSDKAndExtract() 121 122 @staticmethod 123 def InstallGcloudComponent(gcloud_runner, component): 124 """Install gcloud component. 125 126 Args: 127 gcloud_runner: A GcloudRunner class to run "gcloud" command. 128 component: String, name of gcloud component. 129 """ 130 result = gcloud_runner.RunGcloud([ 131 "components", "list", "--format", "get(state.name)", "--filter", 132 "ID=%s" % component 133 ]) 134 if result.strip() == GCLOUD_COMPONENT_NOT_INSTALLED: 135 gcloud_runner.RunGcloud( 136 ["components", "install", "--quiet", component]) 137 138 def GetSDKBinPath(self): 139 """Get google SDK tools bin path. 140 141 We assumed there are google sdk tools somewhere and will raise if we 142 can't find it. The order we're looking for is: 143 1. Builtin gcloud (usually /usr/bin), we'll return /usr/bin with the 144 assumption other sdk tools live there. 145 2. Downloaded google sdk (self._tmp_dir), we assumed the caller already 146 downloaded/extracted and set the self._tmp_sdk_path for us to return. 147 We'll make sure it exists prior to returning it. 148 149 Return: 150 String, return google SDK tools bin path. 151 152 Raise: 153 NoGoogleSDKDetected if we can't find the sdk path. 154 """ 155 builtin_gcloud = utils.FindExecutable(GCLOUD_BIN) 156 if builtin_gcloud: 157 return os.path.dirname(builtin_gcloud) 158 elif os.path.exists(self._tmp_sdk_path): 159 return self._tmp_sdk_path 160 raise errors.NoGoogleSDKDetected("no sdk path.") 161 162 def DownloadGcloudSDKAndExtract(self): 163 """Download the google SDK tools and decompress it. 164 165 Download the google SDK from the GCP web. 166 Reference https://cloud.google.com/sdk/docs/downloads-versioned-archives. 167 """ 168 self._tmp_path = tempfile.mkdtemp(prefix="gcloud") 169 url = GetSdkUrl() 170 filename = url[url.rfind("/") + 1:] 171 file_path = os.path.join(self._tmp_path, filename) 172 logger.info("Download file from: %s", url) 173 logger.info("Save the file to: %s", file_path) 174 url_stream = urllib.request.urlopen(url) 175 metadata = url_stream.info() 176 file_size = int(metadata.get("Content-Length")) 177 logger.info("Downloading google SDK: %s bytes.", file_size) 178 with open(file_path, 'wb') as output: 179 output.write(url_stream.read()) 180 utils.Decompress(file_path, self._tmp_path) 181 self._tmp_sdk_path = os.path.join(self._tmp_path, SDK_BIN_PATH) 182 183 def CleanUp(self): 184 """Clean google sdk tools install folder.""" 185 if self._tmp_path and os.path.exists(self._tmp_path): 186 shutil.rmtree(self._tmp_path) 187