1# 2# Copyright (C) 2017 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17import logging 18import os 19import re 20import zipfile 21 22from host_controller.build import build_provider 23from host_controller.utils.gcp import gcs_utils 24from vts.utils.python.common import cmd_utils 25 26_GCLOUD_AUTH_ENV_KEY = "run_gcs_key" 27 28 29class BuildProviderGCS(build_provider.BuildProvider): 30 """A build provider for GCS (Google Cloud Storage).""" 31 32 def __init__(self): 33 super(BuildProviderGCS, self).__init__() 34 if _GCLOUD_AUTH_ENV_KEY in os.environ: 35 gcloud_path = BuildProviderGCS.GetGcloudPath() 36 if gcloud_path is not None: 37 auth_cmd = "%s auth activate-service-account --key-file=%s" % ( 38 gcloud_path, os.environ[_GCLOUD_AUTH_ENV_KEY]) 39 _, stderr, ret_code = cmd_utils.ExecuteOneShellCommand( 40 auth_cmd) 41 if ret_code == 0: 42 logging.info(stderr) 43 else: 44 logging.error(stderr) 45 46 @staticmethod 47 def GetGcloudPath(): 48 """Returns the gcloud file path if found; None otherwise.""" 49 sh_stdout, _, ret_code = cmd_utils.ExecuteOneShellCommand( 50 "which gcloud") 51 if ret_code == 0: 52 return sh_stdout.strip() 53 else: 54 logging.error("`gcloud` doesn't exist on the host; " 55 "please install Google Cloud SDK before retrying.") 56 return None 57 58 def Fetch(self, path, full_device_images=False, set_suite_as=None): 59 """Fetches Android device artifact file(s) from GCS. 60 61 Args: 62 path: string, the path of a directory which keeps artifacts. 63 set_suite_as: string, the test suite name to use for the given 64 artifact. Used when the file name does not follow 65 the standard "android-*ts.zip" file name pattern. 66 67 Returns: 68 a dict containing the device image info. 69 a dict containing the test suite package info. 70 a dict containing the info about custom tool files. 71 """ 72 if not path.startswith("gs://"): 73 path = "gs://" + re.sub("^/*", "", path) 74 path = re.sub("/*$", "", path) 75 gsutil_path = gcs_utils.GetGsutilPath() 76 if gsutil_path: 77 temp_dir_path = self.CreateNewTmpDir() 78 # IsGcsFile returns False if path is directory or doesn't exist. 79 # cp command returns non-zero if path doesn't exist. 80 if not gcs_utils.IsGcsFile(gsutil_path, path): 81 dest_path = temp_dir_path 82 if "latest.zip" in path: 83 gsutil_ls_path = re.sub("latest.zip", "*.zip", path) 84 lines_gsutil_ls = gcs_utils.List(gsutil_path, gsutil_ls_path) 85 if lines_gsutil_ls: 86 lines_gsutil_ls.sort() 87 path = lines_gsutil_ls[-1] 88 copy_command = "%s cp %s %s" % (gsutil_path, path, 89 temp_dir_path) 90 else: 91 logging.error( 92 "There is no file(s) that matches the URL %s.", 93 path) 94 return (self.GetDeviceImage(), 95 self.GetTestSuitePackage(), 96 self.GetAdditionalFile()) 97 else: 98 copy_command = "%s cp -r %s/* %s" % (gsutil_path, path, 99 temp_dir_path) 100 else: 101 dest_path = os.path.join(temp_dir_path, os.path.basename(path)) 102 copy_command = "%s cp %s %s" % (gsutil_path, path, 103 temp_dir_path) 104 105 _, _, ret_code = cmd_utils.ExecuteOneShellCommand(copy_command) 106 if ret_code == 0: 107 self.SetFetchedFile(dest_path, temp_dir_path, 108 full_device_images, set_suite_as) 109 else: 110 logging.error("Error in copy file from GCS (code %s).", 111 ret_code) 112 return (self.GetDeviceImage(), self.GetTestSuitePackage(), 113 self.GetAdditionalFile()) 114