1*800a58d9SAndroid Build Coastguard Worker#!/usr/bin/env python 2*800a58d9SAndroid Build Coastguard Worker# 3*800a58d9SAndroid Build Coastguard Worker# Copyright 2018 - The Android Open Source Project 4*800a58d9SAndroid Build Coastguard Worker# 5*800a58d9SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*800a58d9SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*800a58d9SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*800a58d9SAndroid Build Coastguard Worker# 9*800a58d9SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*800a58d9SAndroid Build Coastguard Worker# 11*800a58d9SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*800a58d9SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*800a58d9SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*800a58d9SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*800a58d9SAndroid Build Coastguard Worker# limitations under the License. 16*800a58d9SAndroid Build Coastguard Worker"""Gcloud setup runner.""" 17*800a58d9SAndroid Build Coastguard Worker 18*800a58d9SAndroid Build Coastguard Workerfrom __future__ import print_function 19*800a58d9SAndroid Build Coastguard Workerimport logging 20*800a58d9SAndroid Build Coastguard Workerimport os 21*800a58d9SAndroid Build Coastguard Workerimport re 22*800a58d9SAndroid Build Coastguard Workerimport subprocess 23*800a58d9SAndroid Build Coastguard Worker 24*800a58d9SAndroid Build Coastguard Workerimport six 25*800a58d9SAndroid Build Coastguard Worker 26*800a58d9SAndroid Build Coastguard Workerfrom acloud import errors 27*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import utils 28*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import config 29*800a58d9SAndroid Build Coastguard Workerfrom acloud.setup import base_task_runner 30*800a58d9SAndroid Build Coastguard Workerfrom acloud.setup import google_sdk 31*800a58d9SAndroid Build Coastguard Worker 32*800a58d9SAndroid Build Coastguard Worker 33*800a58d9SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__) 34*800a58d9SAndroid Build Coastguard Worker 35*800a58d9SAndroid Build Coastguard Worker# APIs that need to be enabled for GCP project. 36*800a58d9SAndroid Build Coastguard Worker_ANDROID_BUILD_SERVICE = "androidbuildinternal.googleapis.com" 37*800a58d9SAndroid Build Coastguard Worker_ANDROID_BUILD_MSG = ( 38*800a58d9SAndroid Build Coastguard Worker "This service (%s) help to download images from Android Build. If it isn't " 39*800a58d9SAndroid Build Coastguard Worker "enabled, acloud only supports local images to create AVD." 40*800a58d9SAndroid Build Coastguard Worker % _ANDROID_BUILD_SERVICE) 41*800a58d9SAndroid Build Coastguard Worker_COMPUTE_ENGINE_SERVICE = "compute.googleapis.com" 42*800a58d9SAndroid Build Coastguard Worker_COMPUTE_ENGINE_MSG = ( 43*800a58d9SAndroid Build Coastguard Worker "This service (%s) help to create instance in google cloud platform. If it " 44*800a58d9SAndroid Build Coastguard Worker "isn't enabled, acloud can't work anymore." % _COMPUTE_ENGINE_SERVICE) 45*800a58d9SAndroid Build Coastguard Worker_OPEN_SERVICE_FAILED_MSG = ( 46*800a58d9SAndroid Build Coastguard Worker "\n[Open Service Failed]\n" 47*800a58d9SAndroid Build Coastguard Worker "Service name: %(service_name)s\n" 48*800a58d9SAndroid Build Coastguard Worker "%(service_msg)s\n") 49*800a58d9SAndroid Build Coastguard Worker 50*800a58d9SAndroid Build Coastguard Worker_BUILD_SERVICE_ACCOUNT = "[email protected]" 51*800a58d9SAndroid Build Coastguard Worker_BILLING_ENABLE_MSG = "billingEnabled: true" 52*800a58d9SAndroid Build Coastguard Worker_DEFAULT_SSH_FOLDER = os.path.expanduser("~/.ssh") 53*800a58d9SAndroid Build Coastguard Worker_DEFAULT_SSH_KEY = "acloud_rsa" 54*800a58d9SAndroid Build Coastguard Worker_DEFAULT_SSH_PRIVATE_KEY = os.path.join(_DEFAULT_SSH_FOLDER, 55*800a58d9SAndroid Build Coastguard Worker _DEFAULT_SSH_KEY) 56*800a58d9SAndroid Build Coastguard Worker_DEFAULT_SSH_PUBLIC_KEY = os.path.join(_DEFAULT_SSH_FOLDER, 57*800a58d9SAndroid Build Coastguard Worker _DEFAULT_SSH_KEY + ".pub") 58*800a58d9SAndroid Build Coastguard Worker_ENV_CLOUDSDK_PYTHON = "CLOUDSDK_PYTHON" 59*800a58d9SAndroid Build Coastguard Worker_GCLOUD_COMPONENT_ALPHA = "alpha" 60*800a58d9SAndroid Build Coastguard Worker# Regular expression to get project/zone information. 61*800a58d9SAndroid Build Coastguard Worker_PROJECT_RE = re.compile(r"^project = (?P<project>.+)") 62*800a58d9SAndroid Build Coastguard Worker_ZONE_RE = re.compile(r"^zone = (?P<zone>.+)") 63*800a58d9SAndroid Build Coastguard Worker 64*800a58d9SAndroid Build Coastguard Worker 65*800a58d9SAndroid Build Coastguard Workerdef UpdateConfigFile(config_path, item, value): 66*800a58d9SAndroid Build Coastguard Worker """Update config data. 67*800a58d9SAndroid Build Coastguard Worker 68*800a58d9SAndroid Build Coastguard Worker Case A: config file contain this item. 69*800a58d9SAndroid Build Coastguard Worker In config, "project = A_project". New value is B_project 70*800a58d9SAndroid Build Coastguard Worker Set config "project = B_project". 71*800a58d9SAndroid Build Coastguard Worker Case B: config file didn't contain this item. 72*800a58d9SAndroid Build Coastguard Worker New value is B_project. 73*800a58d9SAndroid Build Coastguard Worker Setup config as "project = B_project". 74*800a58d9SAndroid Build Coastguard Worker 75*800a58d9SAndroid Build Coastguard Worker Args: 76*800a58d9SAndroid Build Coastguard Worker config_path: String, acloud config path. 77*800a58d9SAndroid Build Coastguard Worker item: String, item name in config file. EX: project, zone 78*800a58d9SAndroid Build Coastguard Worker value: String, value of item in config file. 79*800a58d9SAndroid Build Coastguard Worker 80*800a58d9SAndroid Build Coastguard Worker TODO(111574698): Refactor this to minimize writes to the config file. 81*800a58d9SAndroid Build Coastguard Worker TODO(111574698): Use proto method to update config. 82*800a58d9SAndroid Build Coastguard Worker """ 83*800a58d9SAndroid Build Coastguard Worker write_lines = [] 84*800a58d9SAndroid Build Coastguard Worker find_item = False 85*800a58d9SAndroid Build Coastguard Worker write_line = item + ": \"" + value + "\"\n" 86*800a58d9SAndroid Build Coastguard Worker if os.path.isfile(config_path): 87*800a58d9SAndroid Build Coastguard Worker with open(config_path, "r") as cfg_file: 88*800a58d9SAndroid Build Coastguard Worker for read_line in cfg_file.readlines(): 89*800a58d9SAndroid Build Coastguard Worker if read_line.startswith(item + ":"): 90*800a58d9SAndroid Build Coastguard Worker find_item = True 91*800a58d9SAndroid Build Coastguard Worker write_lines.append(write_line) 92*800a58d9SAndroid Build Coastguard Worker else: 93*800a58d9SAndroid Build Coastguard Worker write_lines.append(read_line) 94*800a58d9SAndroid Build Coastguard Worker if not find_item: 95*800a58d9SAndroid Build Coastguard Worker write_lines.append(write_line) 96*800a58d9SAndroid Build Coastguard Worker with open(config_path, "w") as cfg_file: 97*800a58d9SAndroid Build Coastguard Worker cfg_file.writelines(write_lines) 98*800a58d9SAndroid Build Coastguard Worker 99*800a58d9SAndroid Build Coastguard Worker 100*800a58d9SAndroid Build Coastguard Workerdef SetupSSHKeys(config_path, private_key_path, public_key_path): 101*800a58d9SAndroid Build Coastguard Worker """Setup the pair of the ssh key for acloud.config. 102*800a58d9SAndroid Build Coastguard Worker 103*800a58d9SAndroid Build Coastguard Worker User can use the default path: "~/.ssh/acloud_rsa". 104*800a58d9SAndroid Build Coastguard Worker 105*800a58d9SAndroid Build Coastguard Worker Args: 106*800a58d9SAndroid Build Coastguard Worker config_path: String, acloud config path. 107*800a58d9SAndroid Build Coastguard Worker private_key_path: Path to the private key file. 108*800a58d9SAndroid Build Coastguard Worker e.g. ~/.ssh/acloud_rsa 109*800a58d9SAndroid Build Coastguard Worker public_key_path: Path to the public key file. 110*800a58d9SAndroid Build Coastguard Worker e.g. ~/.ssh/acloud_rsa.pub 111*800a58d9SAndroid Build Coastguard Worker """ 112*800a58d9SAndroid Build Coastguard Worker private_key_path = os.path.expanduser(private_key_path) 113*800a58d9SAndroid Build Coastguard Worker if (private_key_path == "" or public_key_path == "" 114*800a58d9SAndroid Build Coastguard Worker or private_key_path == _DEFAULT_SSH_PRIVATE_KEY): 115*800a58d9SAndroid Build Coastguard Worker utils.CreateSshKeyPairIfNotExist(_DEFAULT_SSH_PRIVATE_KEY, 116*800a58d9SAndroid Build Coastguard Worker _DEFAULT_SSH_PUBLIC_KEY) 117*800a58d9SAndroid Build Coastguard Worker UpdateConfigFile(config_path, "ssh_private_key_path", 118*800a58d9SAndroid Build Coastguard Worker _DEFAULT_SSH_PRIVATE_KEY) 119*800a58d9SAndroid Build Coastguard Worker UpdateConfigFile(config_path, "ssh_public_key_path", 120*800a58d9SAndroid Build Coastguard Worker _DEFAULT_SSH_PUBLIC_KEY) 121*800a58d9SAndroid Build Coastguard Worker 122*800a58d9SAndroid Build Coastguard Worker 123*800a58d9SAndroid Build Coastguard Workerdef _InputIsEmpty(input_string): 124*800a58d9SAndroid Build Coastguard Worker """Check input string is empty. 125*800a58d9SAndroid Build Coastguard Worker 126*800a58d9SAndroid Build Coastguard Worker Tool requests user to input client ID & client secret. 127*800a58d9SAndroid Build Coastguard Worker This basic check can detect user input is empty. 128*800a58d9SAndroid Build Coastguard Worker 129*800a58d9SAndroid Build Coastguard Worker Args: 130*800a58d9SAndroid Build Coastguard Worker input_string: String, user input string. 131*800a58d9SAndroid Build Coastguard Worker 132*800a58d9SAndroid Build Coastguard Worker Returns: 133*800a58d9SAndroid Build Coastguard Worker Boolean: True if input is empty, False otherwise. 134*800a58d9SAndroid Build Coastguard Worker """ 135*800a58d9SAndroid Build Coastguard Worker if input_string is None: 136*800a58d9SAndroid Build Coastguard Worker return True 137*800a58d9SAndroid Build Coastguard Worker if input_string == "": 138*800a58d9SAndroid Build Coastguard Worker print("Please enter a non-empty value.") 139*800a58d9SAndroid Build Coastguard Worker return True 140*800a58d9SAndroid Build Coastguard Worker return False 141*800a58d9SAndroid Build Coastguard Worker 142*800a58d9SAndroid Build Coastguard Worker 143*800a58d9SAndroid Build Coastguard Workerclass GoogleSDKBins(): 144*800a58d9SAndroid Build Coastguard Worker """Class to run tools in the Google SDK.""" 145*800a58d9SAndroid Build Coastguard Worker 146*800a58d9SAndroid Build Coastguard Worker def __init__(self, google_sdk_folder): 147*800a58d9SAndroid Build Coastguard Worker """GoogleSDKBins initialize. 148*800a58d9SAndroid Build Coastguard Worker 149*800a58d9SAndroid Build Coastguard Worker Args: 150*800a58d9SAndroid Build Coastguard Worker google_sdk_folder: String, google sdk path. 151*800a58d9SAndroid Build Coastguard Worker """ 152*800a58d9SAndroid Build Coastguard Worker self.gcloud_command_path = os.path.join(google_sdk_folder, "gcloud") 153*800a58d9SAndroid Build Coastguard Worker self.gsutil_command_path = os.path.join(google_sdk_folder, "gsutil") 154*800a58d9SAndroid Build Coastguard Worker self._env = os.environ.copy() 155*800a58d9SAndroid Build Coastguard Worker self._env[_ENV_CLOUDSDK_PYTHON] = "python" 156*800a58d9SAndroid Build Coastguard Worker 157*800a58d9SAndroid Build Coastguard Worker def RunGcloud(self, cmd, **kwargs): 158*800a58d9SAndroid Build Coastguard Worker """Run gcloud command. 159*800a58d9SAndroid Build Coastguard Worker 160*800a58d9SAndroid Build Coastguard Worker Args: 161*800a58d9SAndroid Build Coastguard Worker cmd: String list, command strings. 162*800a58d9SAndroid Build Coastguard Worker Ex: [config], then this function call "gcloud config". 163*800a58d9SAndroid Build Coastguard Worker **kwargs: dictionary of keyword based args to pass to func. 164*800a58d9SAndroid Build Coastguard Worker 165*800a58d9SAndroid Build Coastguard Worker Returns: 166*800a58d9SAndroid Build Coastguard Worker String, return message after execute gcloud command. 167*800a58d9SAndroid Build Coastguard Worker """ 168*800a58d9SAndroid Build Coastguard Worker return utils.CheckOutput([self.gcloud_command_path] + cmd, 169*800a58d9SAndroid Build Coastguard Worker env=self._env, **kwargs) 170*800a58d9SAndroid Build Coastguard Worker 171*800a58d9SAndroid Build Coastguard Worker def RunGsutil(self, cmd, **kwargs): 172*800a58d9SAndroid Build Coastguard Worker """Run gsutil command. 173*800a58d9SAndroid Build Coastguard Worker 174*800a58d9SAndroid Build Coastguard Worker Args: 175*800a58d9SAndroid Build Coastguard Worker cmd : String list, command strings. 176*800a58d9SAndroid Build Coastguard Worker Ex: [list], then this function call "gsutil list". 177*800a58d9SAndroid Build Coastguard Worker **kwargs: dictionary of keyword based args to pass to func. 178*800a58d9SAndroid Build Coastguard Worker 179*800a58d9SAndroid Build Coastguard Worker Returns: 180*800a58d9SAndroid Build Coastguard Worker String, return message after execute gsutil command. 181*800a58d9SAndroid Build Coastguard Worker """ 182*800a58d9SAndroid Build Coastguard Worker return utils.CheckOutput([self.gsutil_command_path] + cmd, 183*800a58d9SAndroid Build Coastguard Worker env=self._env, **kwargs) 184*800a58d9SAndroid Build Coastguard Worker 185*800a58d9SAndroid Build Coastguard Worker 186*800a58d9SAndroid Build Coastguard Workerclass GoogleAPIService(): 187*800a58d9SAndroid Build Coastguard Worker """Class to enable api service in the gcp project.""" 188*800a58d9SAndroid Build Coastguard Worker 189*800a58d9SAndroid Build Coastguard Worker def __init__(self, service_name, error_msg, required=False): 190*800a58d9SAndroid Build Coastguard Worker """GoogleAPIService initialize. 191*800a58d9SAndroid Build Coastguard Worker 192*800a58d9SAndroid Build Coastguard Worker Args: 193*800a58d9SAndroid Build Coastguard Worker service_name: String, name of api service. 194*800a58d9SAndroid Build Coastguard Worker error_msg: String, show messages if api service enable failed. 195*800a58d9SAndroid Build Coastguard Worker required: Boolean, True for service must be enabled for acloud. 196*800a58d9SAndroid Build Coastguard Worker """ 197*800a58d9SAndroid Build Coastguard Worker self._name = service_name 198*800a58d9SAndroid Build Coastguard Worker self._error_msg = error_msg 199*800a58d9SAndroid Build Coastguard Worker self._required = required 200*800a58d9SAndroid Build Coastguard Worker 201*800a58d9SAndroid Build Coastguard Worker def EnableService(self, gcloud_runner): 202*800a58d9SAndroid Build Coastguard Worker """Enable api service. 203*800a58d9SAndroid Build Coastguard Worker 204*800a58d9SAndroid Build Coastguard Worker Args: 205*800a58d9SAndroid Build Coastguard Worker gcloud_runner: A GcloudRunner class to run "gcloud" command. 206*800a58d9SAndroid Build Coastguard Worker """ 207*800a58d9SAndroid Build Coastguard Worker try: 208*800a58d9SAndroid Build Coastguard Worker gcloud_runner.RunGcloud(["services", "enable", self._name], 209*800a58d9SAndroid Build Coastguard Worker stderr=subprocess.STDOUT) 210*800a58d9SAndroid Build Coastguard Worker except subprocess.CalledProcessError as error: 211*800a58d9SAndroid Build Coastguard Worker self.ShowFailMessages(error.output) 212*800a58d9SAndroid Build Coastguard Worker 213*800a58d9SAndroid Build Coastguard Worker def ShowFailMessages(self, error): 214*800a58d9SAndroid Build Coastguard Worker """Show fail messages. 215*800a58d9SAndroid Build Coastguard Worker 216*800a58d9SAndroid Build Coastguard Worker Show the fail messages to hint users the impact if the api service 217*800a58d9SAndroid Build Coastguard Worker isn't enabled. 218*800a58d9SAndroid Build Coastguard Worker 219*800a58d9SAndroid Build Coastguard Worker Args: 220*800a58d9SAndroid Build Coastguard Worker error: String of error message when opening api service failed. 221*800a58d9SAndroid Build Coastguard Worker """ 222*800a58d9SAndroid Build Coastguard Worker msg_color = (utils.TextColors.FAIL if self._required else 223*800a58d9SAndroid Build Coastguard Worker utils.TextColors.WARNING) 224*800a58d9SAndroid Build Coastguard Worker utils.PrintColorString( 225*800a58d9SAndroid Build Coastguard Worker error + _OPEN_SERVICE_FAILED_MSG % { 226*800a58d9SAndroid Build Coastguard Worker "service_name": self._name, 227*800a58d9SAndroid Build Coastguard Worker "service_msg": self._error_msg} 228*800a58d9SAndroid Build Coastguard Worker , msg_color) 229*800a58d9SAndroid Build Coastguard Worker 230*800a58d9SAndroid Build Coastguard Worker @property 231*800a58d9SAndroid Build Coastguard Worker def name(self): 232*800a58d9SAndroid Build Coastguard Worker """Return name.""" 233*800a58d9SAndroid Build Coastguard Worker return self._name 234*800a58d9SAndroid Build Coastguard Worker 235*800a58d9SAndroid Build Coastguard Worker 236*800a58d9SAndroid Build Coastguard Workerclass GcpTaskRunner(base_task_runner.BaseTaskRunner): 237*800a58d9SAndroid Build Coastguard Worker """Runner to setup google cloud user information.""" 238*800a58d9SAndroid Build Coastguard Worker 239*800a58d9SAndroid Build Coastguard Worker WELCOME_MESSAGE_TITLE = "Setup google cloud user information" 240*800a58d9SAndroid Build Coastguard Worker WELCOME_MESSAGE = ( 241*800a58d9SAndroid Build Coastguard Worker "This step will walk you through gcloud SDK installation." 242*800a58d9SAndroid Build Coastguard Worker "Then configure gcloud user information." 243*800a58d9SAndroid Build Coastguard Worker "Finally enable some gcloud API services.") 244*800a58d9SAndroid Build Coastguard Worker 245*800a58d9SAndroid Build Coastguard Worker def __init__(self, config_path): 246*800a58d9SAndroid Build Coastguard Worker """Initialize parameters. 247*800a58d9SAndroid Build Coastguard Worker 248*800a58d9SAndroid Build Coastguard Worker Load config file to get current values. 249*800a58d9SAndroid Build Coastguard Worker 250*800a58d9SAndroid Build Coastguard Worker Args: 251*800a58d9SAndroid Build Coastguard Worker config_path: String, acloud config path. 252*800a58d9SAndroid Build Coastguard Worker """ 253*800a58d9SAndroid Build Coastguard Worker # pylint: disable=invalid-name 254*800a58d9SAndroid Build Coastguard Worker config_mgr = config.AcloudConfigManager(config_path) 255*800a58d9SAndroid Build Coastguard Worker cfg = config_mgr.Load() 256*800a58d9SAndroid Build Coastguard Worker self.config_path = config_mgr.user_config_path 257*800a58d9SAndroid Build Coastguard Worker self.project = cfg.project 258*800a58d9SAndroid Build Coastguard Worker self.zone = cfg.zone 259*800a58d9SAndroid Build Coastguard Worker self.ssh_private_key_path = cfg.ssh_private_key_path 260*800a58d9SAndroid Build Coastguard Worker self.ssh_public_key_path = cfg.ssh_public_key_path 261*800a58d9SAndroid Build Coastguard Worker self.stable_host_image_name = cfg.stable_host_image_name 262*800a58d9SAndroid Build Coastguard Worker self.client_id = cfg.client_id 263*800a58d9SAndroid Build Coastguard Worker self.client_secret = cfg.client_secret 264*800a58d9SAndroid Build Coastguard Worker self.service_account_name = cfg.service_account_name 265*800a58d9SAndroid Build Coastguard Worker self.service_account_private_key_path = cfg.service_account_private_key_path 266*800a58d9SAndroid Build Coastguard Worker self.service_account_json_private_key_path = cfg.service_account_json_private_key_path 267*800a58d9SAndroid Build Coastguard Worker 268*800a58d9SAndroid Build Coastguard Worker def ShouldRun(self): 269*800a58d9SAndroid Build Coastguard Worker """Check if we actually need to run GCP setup. 270*800a58d9SAndroid Build Coastguard Worker 271*800a58d9SAndroid Build Coastguard Worker We'll only do the gcp setup if certain fields in the cfg are empty. 272*800a58d9SAndroid Build Coastguard Worker 273*800a58d9SAndroid Build Coastguard Worker Returns: 274*800a58d9SAndroid Build Coastguard Worker True if reqired config fields are empty, False otherwise. 275*800a58d9SAndroid Build Coastguard Worker """ 276*800a58d9SAndroid Build Coastguard Worker # We need to ensure the config has the proper auth-related fields set, 277*800a58d9SAndroid Build Coastguard Worker # so config requires just 1 of the following: 278*800a58d9SAndroid Build Coastguard Worker # 1. client id/secret 279*800a58d9SAndroid Build Coastguard Worker # 2. service account name/private key path 280*800a58d9SAndroid Build Coastguard Worker # 3. service account json private key path 281*800a58d9SAndroid Build Coastguard Worker if ((not self.client_id or not self.client_secret) 282*800a58d9SAndroid Build Coastguard Worker and (not self.service_account_name or not self.service_account_private_key_path) 283*800a58d9SAndroid Build Coastguard Worker and not self.service_account_json_private_key_path): 284*800a58d9SAndroid Build Coastguard Worker return True 285*800a58d9SAndroid Build Coastguard Worker 286*800a58d9SAndroid Build Coastguard Worker # If a project isn't set, then we need to run setup. 287*800a58d9SAndroid Build Coastguard Worker return not self.project 288*800a58d9SAndroid Build Coastguard Worker 289*800a58d9SAndroid Build Coastguard Worker def _Run(self): 290*800a58d9SAndroid Build Coastguard Worker """Run GCP setup task.""" 291*800a58d9SAndroid Build Coastguard Worker self._SetupGcloudInfo() 292*800a58d9SAndroid Build Coastguard Worker SetupSSHKeys(self.config_path, self.ssh_private_key_path, 293*800a58d9SAndroid Build Coastguard Worker self.ssh_public_key_path) 294*800a58d9SAndroid Build Coastguard Worker 295*800a58d9SAndroid Build Coastguard Worker def _SetupGcloudInfo(self): 296*800a58d9SAndroid Build Coastguard Worker """Setup Gcloud user information. 297*800a58d9SAndroid Build Coastguard Worker 1. Setup Gcloud SDK tools. 298*800a58d9SAndroid Build Coastguard Worker 2. Setup Gcloud project. 299*800a58d9SAndroid Build Coastguard Worker a. Setup Gcloud project and zone. 300*800a58d9SAndroid Build Coastguard Worker b. Setup Client ID and Client secret. 301*800a58d9SAndroid Build Coastguard Worker c. Setup Google Cloud Storage bucket. 302*800a58d9SAndroid Build Coastguard Worker 3. Enable Gcloud API services. 303*800a58d9SAndroid Build Coastguard Worker """ 304*800a58d9SAndroid Build Coastguard Worker google_sdk_init = google_sdk.GoogleSDK() 305*800a58d9SAndroid Build Coastguard Worker try: 306*800a58d9SAndroid Build Coastguard Worker google_sdk_runner = GoogleSDKBins(google_sdk_init.GetSDKBinPath()) 307*800a58d9SAndroid Build Coastguard Worker google_sdk_init.InstallGcloudComponent(google_sdk_runner, 308*800a58d9SAndroid Build Coastguard Worker _GCLOUD_COMPONENT_ALPHA) 309*800a58d9SAndroid Build Coastguard Worker self._SetupProject(google_sdk_runner) 310*800a58d9SAndroid Build Coastguard Worker self._EnableGcloudServices(google_sdk_runner) 311*800a58d9SAndroid Build Coastguard Worker self._CreateStableHostImage() 312*800a58d9SAndroid Build Coastguard Worker finally: 313*800a58d9SAndroid Build Coastguard Worker google_sdk_init.CleanUp() 314*800a58d9SAndroid Build Coastguard Worker 315*800a58d9SAndroid Build Coastguard Worker def _CreateStableHostImage(self): 316*800a58d9SAndroid Build Coastguard Worker """Create the stable host image.""" 317*800a58d9SAndroid Build Coastguard Worker # Write default stable_host_image_name with unused value. 318*800a58d9SAndroid Build Coastguard Worker # TODO(113091773): An additional step to create the host image. 319*800a58d9SAndroid Build Coastguard Worker if not self.stable_host_image_name: 320*800a58d9SAndroid Build Coastguard Worker UpdateConfigFile(self.config_path, "stable_host_image_name", "") 321*800a58d9SAndroid Build Coastguard Worker 322*800a58d9SAndroid Build Coastguard Worker 323*800a58d9SAndroid Build Coastguard Worker def _NeedProjectSetup(self): 324*800a58d9SAndroid Build Coastguard Worker """Confirm project setup should run or not. 325*800a58d9SAndroid Build Coastguard Worker 326*800a58d9SAndroid Build Coastguard Worker If the project settings (project name and zone) are blank (either one), 327*800a58d9SAndroid Build Coastguard Worker we'll run the project setup flow. If they are set, we'll check with 328*800a58d9SAndroid Build Coastguard Worker the user if they want to update them. 329*800a58d9SAndroid Build Coastguard Worker 330*800a58d9SAndroid Build Coastguard Worker Returns: 331*800a58d9SAndroid Build Coastguard Worker Boolean: True if we need to setup the project, False otherwise. 332*800a58d9SAndroid Build Coastguard Worker """ 333*800a58d9SAndroid Build Coastguard Worker user_question = ( 334*800a58d9SAndroid Build Coastguard Worker "Your default Project/Zone settings are:\n" 335*800a58d9SAndroid Build Coastguard Worker "project:[%s]\n" 336*800a58d9SAndroid Build Coastguard Worker "zone:[%s]\n" 337*800a58d9SAndroid Build Coastguard Worker "Would you like to update them?[y/N]: \n") % (self.project, self.zone) 338*800a58d9SAndroid Build Coastguard Worker 339*800a58d9SAndroid Build Coastguard Worker if not self.project or not self.zone: 340*800a58d9SAndroid Build Coastguard Worker logger.info("Project or zone is empty. Start to run setup process.") 341*800a58d9SAndroid Build Coastguard Worker return True 342*800a58d9SAndroid Build Coastguard Worker return utils.GetUserAnswerYes(user_question) 343*800a58d9SAndroid Build Coastguard Worker 344*800a58d9SAndroid Build Coastguard Worker def _NeedClientIDSetup(self, project_changed): 345*800a58d9SAndroid Build Coastguard Worker """Confirm client setup should run or not. 346*800a58d9SAndroid Build Coastguard Worker 347*800a58d9SAndroid Build Coastguard Worker If project changed, client ID must also have to change. 348*800a58d9SAndroid Build Coastguard Worker So tool will force to run setup function. 349*800a58d9SAndroid Build Coastguard Worker If client ID or client secret is empty, tool force to run setup function. 350*800a58d9SAndroid Build Coastguard Worker If project didn't change and config hold user client ID/secret, tool 351*800a58d9SAndroid Build Coastguard Worker would skip client ID setup. 352*800a58d9SAndroid Build Coastguard Worker 353*800a58d9SAndroid Build Coastguard Worker Args: 354*800a58d9SAndroid Build Coastguard Worker project_changed: Boolean, True for project changed. 355*800a58d9SAndroid Build Coastguard Worker 356*800a58d9SAndroid Build Coastguard Worker Returns: 357*800a58d9SAndroid Build Coastguard Worker Boolean: True for run setup function. 358*800a58d9SAndroid Build Coastguard Worker """ 359*800a58d9SAndroid Build Coastguard Worker if project_changed: 360*800a58d9SAndroid Build Coastguard Worker logger.info("Your project changed. Start to run setup process.") 361*800a58d9SAndroid Build Coastguard Worker return True 362*800a58d9SAndroid Build Coastguard Worker if not self.client_id or not self.client_secret: 363*800a58d9SAndroid Build Coastguard Worker logger.info("Client ID or client secret is empty. Start to run setup process.") 364*800a58d9SAndroid Build Coastguard Worker return True 365*800a58d9SAndroid Build Coastguard Worker logger.info("Project was unchanged and client ID didn't need to changed.") 366*800a58d9SAndroid Build Coastguard Worker return False 367*800a58d9SAndroid Build Coastguard Worker 368*800a58d9SAndroid Build Coastguard Worker def _SetupProject(self, gcloud_runner): 369*800a58d9SAndroid Build Coastguard Worker """Setup gcloud project information. 370*800a58d9SAndroid Build Coastguard Worker 371*800a58d9SAndroid Build Coastguard Worker Setup project and zone. 372*800a58d9SAndroid Build Coastguard Worker Setup client ID and client secret. 373*800a58d9SAndroid Build Coastguard Worker Make sure billing account enabled in project. 374*800a58d9SAndroid Build Coastguard Worker 375*800a58d9SAndroid Build Coastguard Worker Args: 376*800a58d9SAndroid Build Coastguard Worker gcloud_runner: A GcloudRunner class to run "gcloud" command. 377*800a58d9SAndroid Build Coastguard Worker """ 378*800a58d9SAndroid Build Coastguard Worker project_changed = False 379*800a58d9SAndroid Build Coastguard Worker if self._NeedProjectSetup(): 380*800a58d9SAndroid Build Coastguard Worker project_changed = self._UpdateProject(gcloud_runner) 381*800a58d9SAndroid Build Coastguard Worker if self._NeedClientIDSetup(project_changed): 382*800a58d9SAndroid Build Coastguard Worker self._SetupClientIDSecret() 383*800a58d9SAndroid Build Coastguard Worker self._CheckBillingEnable(gcloud_runner) 384*800a58d9SAndroid Build Coastguard Worker 385*800a58d9SAndroid Build Coastguard Worker def _UpdateProject(self, gcloud_runner): 386*800a58d9SAndroid Build Coastguard Worker """Setup gcloud project name and zone name and check project changed. 387*800a58d9SAndroid Build Coastguard Worker 388*800a58d9SAndroid Build Coastguard Worker Run "gcloud init" to handle gcloud project setup. 389*800a58d9SAndroid Build Coastguard Worker Then "gcloud list" to get user settings information include "project" & "zone". 390*800a58d9SAndroid Build Coastguard Worker Record project_changed for next setup steps. 391*800a58d9SAndroid Build Coastguard Worker 392*800a58d9SAndroid Build Coastguard Worker Args: 393*800a58d9SAndroid Build Coastguard Worker gcloud_runner: A GcloudRunner class to run "gcloud" command. 394*800a58d9SAndroid Build Coastguard Worker 395*800a58d9SAndroid Build Coastguard Worker Returns: 396*800a58d9SAndroid Build Coastguard Worker project_changed: True for project settings changed. 397*800a58d9SAndroid Build Coastguard Worker """ 398*800a58d9SAndroid Build Coastguard Worker project_changed = False 399*800a58d9SAndroid Build Coastguard Worker gcloud_runner.RunGcloud(["init"]) 400*800a58d9SAndroid Build Coastguard Worker gcp_config_list_out = gcloud_runner.RunGcloud(["config", "list"]) 401*800a58d9SAndroid Build Coastguard Worker for line in gcp_config_list_out.splitlines(): 402*800a58d9SAndroid Build Coastguard Worker project_match = _PROJECT_RE.match(line) 403*800a58d9SAndroid Build Coastguard Worker if project_match: 404*800a58d9SAndroid Build Coastguard Worker project = project_match.group("project") 405*800a58d9SAndroid Build Coastguard Worker project_changed = (self.project != project) 406*800a58d9SAndroid Build Coastguard Worker self.project = project 407*800a58d9SAndroid Build Coastguard Worker continue 408*800a58d9SAndroid Build Coastguard Worker zone_match = _ZONE_RE.match(line) 409*800a58d9SAndroid Build Coastguard Worker if zone_match: 410*800a58d9SAndroid Build Coastguard Worker self.zone = zone_match.group("zone") 411*800a58d9SAndroid Build Coastguard Worker continue 412*800a58d9SAndroid Build Coastguard Worker UpdateConfigFile(self.config_path, "project", self.project) 413*800a58d9SAndroid Build Coastguard Worker UpdateConfigFile(self.config_path, "zone", self.zone) 414*800a58d9SAndroid Build Coastguard Worker return project_changed 415*800a58d9SAndroid Build Coastguard Worker 416*800a58d9SAndroid Build Coastguard Worker def _SetupClientIDSecret(self): 417*800a58d9SAndroid Build Coastguard Worker """Setup Client ID / Client Secret in config file. 418*800a58d9SAndroid Build Coastguard Worker 419*800a58d9SAndroid Build Coastguard Worker User can use input new values for Client ID and Client Secret. 420*800a58d9SAndroid Build Coastguard Worker """ 421*800a58d9SAndroid Build Coastguard Worker print("Please generate a new client ID/secret by following the instructions here:") 422*800a58d9SAndroid Build Coastguard Worker print("https://support.google.com/cloud/answer/6158849?hl=en") 423*800a58d9SAndroid Build Coastguard Worker # TODO: Create markdown readme instructions since the link isn't too helpful. 424*800a58d9SAndroid Build Coastguard Worker self.client_id = None 425*800a58d9SAndroid Build Coastguard Worker self.client_secret = None 426*800a58d9SAndroid Build Coastguard Worker while _InputIsEmpty(self.client_id): 427*800a58d9SAndroid Build Coastguard Worker self.client_id = str(six.moves.input("Enter Client ID: ").strip()) 428*800a58d9SAndroid Build Coastguard Worker while _InputIsEmpty(self.client_secret): 429*800a58d9SAndroid Build Coastguard Worker self.client_secret = str(six.moves.input("Enter Client Secret: ").strip()) 430*800a58d9SAndroid Build Coastguard Worker UpdateConfigFile(self.config_path, "client_id", self.client_id) 431*800a58d9SAndroid Build Coastguard Worker UpdateConfigFile(self.config_path, "client_secret", self.client_secret) 432*800a58d9SAndroid Build Coastguard Worker 433*800a58d9SAndroid Build Coastguard Worker def _CheckBillingEnable(self, gcloud_runner): 434*800a58d9SAndroid Build Coastguard Worker """Check billing enabled in gcp project. 435*800a58d9SAndroid Build Coastguard Worker 436*800a58d9SAndroid Build Coastguard Worker The billing info get by gcloud alpha command. Here is one example: 437*800a58d9SAndroid Build Coastguard Worker $ gcloud alpha billing projects describe project_name 438*800a58d9SAndroid Build Coastguard Worker billingAccountName: billingAccounts/011BXX-A30XXX-9XXXX 439*800a58d9SAndroid Build Coastguard Worker billingEnabled: true 440*800a58d9SAndroid Build Coastguard Worker name: projects/project_name/billingInfo 441*800a58d9SAndroid Build Coastguard Worker projectId: project_name 442*800a58d9SAndroid Build Coastguard Worker 443*800a58d9SAndroid Build Coastguard Worker Args: 444*800a58d9SAndroid Build Coastguard Worker gcloud_runner: A GcloudRunner class to run "gcloud" command. 445*800a58d9SAndroid Build Coastguard Worker 446*800a58d9SAndroid Build Coastguard Worker Raises: 447*800a58d9SAndroid Build Coastguard Worker NoBillingError: gcp project doesn't enable billing account. 448*800a58d9SAndroid Build Coastguard Worker """ 449*800a58d9SAndroid Build Coastguard Worker billing_info = gcloud_runner.RunGcloud( 450*800a58d9SAndroid Build Coastguard Worker ["alpha", "billing", "projects", "describe", self.project]) 451*800a58d9SAndroid Build Coastguard Worker if _BILLING_ENABLE_MSG not in billing_info: 452*800a58d9SAndroid Build Coastguard Worker raise errors.NoBillingError( 453*800a58d9SAndroid Build Coastguard Worker "Please set billing account to project(%s) by following the " 454*800a58d9SAndroid Build Coastguard Worker "instructions here: " 455*800a58d9SAndroid Build Coastguard Worker "https://cloud.google.com/billing/docs/how-to/modify-project" 456*800a58d9SAndroid Build Coastguard Worker % self.project) 457*800a58d9SAndroid Build Coastguard Worker 458*800a58d9SAndroid Build Coastguard Worker @staticmethod 459*800a58d9SAndroid Build Coastguard Worker def _EnableGcloudServices(gcloud_runner): 460*800a58d9SAndroid Build Coastguard Worker """Enable 3 Gcloud API services. 461*800a58d9SAndroid Build Coastguard Worker 462*800a58d9SAndroid Build Coastguard Worker 1. Android build service 463*800a58d9SAndroid Build Coastguard Worker 2. Compute engine service 464*800a58d9SAndroid Build Coastguard Worker To avoid confuse user, we don't show messages for services processing 465*800a58d9SAndroid Build Coastguard Worker messages. e.g. "Waiting for async operation operations ...." 466*800a58d9SAndroid Build Coastguard Worker 467*800a58d9SAndroid Build Coastguard Worker Args: 468*800a58d9SAndroid Build Coastguard Worker gcloud_runner: A GcloudRunner class to run "gcloud" command. 469*800a58d9SAndroid Build Coastguard Worker """ 470*800a58d9SAndroid Build Coastguard Worker google_apis = [ 471*800a58d9SAndroid Build Coastguard Worker GoogleAPIService(_ANDROID_BUILD_SERVICE, _ANDROID_BUILD_MSG), 472*800a58d9SAndroid Build Coastguard Worker GoogleAPIService(_COMPUTE_ENGINE_SERVICE, _COMPUTE_ENGINE_MSG, required=True) 473*800a58d9SAndroid Build Coastguard Worker ] 474*800a58d9SAndroid Build Coastguard Worker enabled_services = gcloud_runner.RunGcloud( 475*800a58d9SAndroid Build Coastguard Worker ["services", "list", "--enabled", "--format", "value(NAME)"], 476*800a58d9SAndroid Build Coastguard Worker stderr=subprocess.STDOUT).splitlines() 477*800a58d9SAndroid Build Coastguard Worker 478*800a58d9SAndroid Build Coastguard Worker for service in google_apis: 479*800a58d9SAndroid Build Coastguard Worker if service.name not in enabled_services: 480*800a58d9SAndroid Build Coastguard Worker service.EnableService(gcloud_runner) 481