1*800a58d9SAndroid Build Coastguard Worker#!/usr/bin/env python 2*800a58d9SAndroid Build Coastguard Worker# 3*800a58d9SAndroid Build Coastguard Worker# Copyright 2016 - 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"""A client that talks to Google Cloud Storage APIs.""" 17*800a58d9SAndroid Build Coastguard Worker 18*800a58d9SAndroid Build Coastguard Workerimport io 19*800a58d9SAndroid Build Coastguard Workerimport logging 20*800a58d9SAndroid Build Coastguard Worker 21*800a58d9SAndroid Build Coastguard Workerimport apiclient 22*800a58d9SAndroid Build Coastguard Worker 23*800a58d9SAndroid Build Coastguard Workerfrom acloud import errors 24*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import base_cloud_client 25*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import utils 26*800a58d9SAndroid Build Coastguard Worker 27*800a58d9SAndroid Build Coastguard Worker 28*800a58d9SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__) 29*800a58d9SAndroid Build Coastguard Worker 30*800a58d9SAndroid Build Coastguard Worker 31*800a58d9SAndroid Build Coastguard Workerclass StorageClient(base_cloud_client.BaseCloudApiClient): 32*800a58d9SAndroid Build Coastguard Worker """Client that talks to Google Cloud Storages.""" 33*800a58d9SAndroid Build Coastguard Worker 34*800a58d9SAndroid Build Coastguard Worker # API settings, used by BaseCloudApiClient. 35*800a58d9SAndroid Build Coastguard Worker API_NAME = "storage" 36*800a58d9SAndroid Build Coastguard Worker API_VERSION = "v1" 37*800a58d9SAndroid Build Coastguard Worker SCOPE = "https://www.googleapis.com/auth/devstorage.read_write" 38*800a58d9SAndroid Build Coastguard Worker GET_OBJ_MAX_RETRY = 3 39*800a58d9SAndroid Build Coastguard Worker GET_OBJ_RETRY_SLEEP = 5 40*800a58d9SAndroid Build Coastguard Worker 41*800a58d9SAndroid Build Coastguard Worker # Other class variables. 42*800a58d9SAndroid Build Coastguard Worker OBJECT_URL_FMT = "https://storage.googleapis.com/%s/%s" 43*800a58d9SAndroid Build Coastguard Worker 44*800a58d9SAndroid Build Coastguard Worker def Get(self, bucket_name, object_name): 45*800a58d9SAndroid Build Coastguard Worker """Get object in a bucket. 46*800a58d9SAndroid Build Coastguard Worker 47*800a58d9SAndroid Build Coastguard Worker Args: 48*800a58d9SAndroid Build Coastguard Worker bucket_name: String, google cloud storage bucket name. 49*800a58d9SAndroid Build Coastguard Worker object_name: String, full path to the object within the bucket. 50*800a58d9SAndroid Build Coastguard Worker 51*800a58d9SAndroid Build Coastguard Worker Returns: 52*800a58d9SAndroid Build Coastguard Worker A dictronary representing an object resource. 53*800a58d9SAndroid Build Coastguard Worker """ 54*800a58d9SAndroid Build Coastguard Worker request = self.service.objects().get( 55*800a58d9SAndroid Build Coastguard Worker bucket=bucket_name, object=object_name) 56*800a58d9SAndroid Build Coastguard Worker return self.Execute(request) 57*800a58d9SAndroid Build Coastguard Worker 58*800a58d9SAndroid Build Coastguard Worker def List(self, bucket_name, prefix=None): 59*800a58d9SAndroid Build Coastguard Worker """Lists objects in a bucket. 60*800a58d9SAndroid Build Coastguard Worker 61*800a58d9SAndroid Build Coastguard Worker Args: 62*800a58d9SAndroid Build Coastguard Worker bucket_name: String, google cloud storage bucket name. 63*800a58d9SAndroid Build Coastguard Worker prefix: String, Filter results to objects whose names begin with 64*800a58d9SAndroid Build Coastguard Worker this prefix. 65*800a58d9SAndroid Build Coastguard Worker 66*800a58d9SAndroid Build Coastguard Worker Returns: 67*800a58d9SAndroid Build Coastguard Worker A list of google storage objects whose names match the prefix. 68*800a58d9SAndroid Build Coastguard Worker Each element is dictionary that wraps all the information about an object. 69*800a58d9SAndroid Build Coastguard Worker """ 70*800a58d9SAndroid Build Coastguard Worker logger.debug("Listing storage bucket: %s, prefix: %s", bucket_name, 71*800a58d9SAndroid Build Coastguard Worker prefix) 72*800a58d9SAndroid Build Coastguard Worker items = self.ListWithMultiPages( 73*800a58d9SAndroid Build Coastguard Worker api_resource=self.service.objects().list, 74*800a58d9SAndroid Build Coastguard Worker bucket=bucket_name, 75*800a58d9SAndroid Build Coastguard Worker prefix=prefix) 76*800a58d9SAndroid Build Coastguard Worker return items 77*800a58d9SAndroid Build Coastguard Worker 78*800a58d9SAndroid Build Coastguard Worker def Upload(self, local_src, bucket_name, object_name, mime_type): 79*800a58d9SAndroid Build Coastguard Worker """Uploads a file. 80*800a58d9SAndroid Build Coastguard Worker 81*800a58d9SAndroid Build Coastguard Worker Args: 82*800a58d9SAndroid Build Coastguard Worker local_src: string, a local path to a file to be uploaded. 83*800a58d9SAndroid Build Coastguard Worker bucket_name: string, google cloud storage bucket name. 84*800a58d9SAndroid Build Coastguard Worker object_name: string, the name of the remote file in storage. 85*800a58d9SAndroid Build Coastguard Worker mime_type: string, mime-type of the file. 86*800a58d9SAndroid Build Coastguard Worker 87*800a58d9SAndroid Build Coastguard Worker Returns: 88*800a58d9SAndroid Build Coastguard Worker URL to the inserted artifact in storage. 89*800a58d9SAndroid Build Coastguard Worker """ 90*800a58d9SAndroid Build Coastguard Worker logger.info("Uploading file: src: %s, bucket: %s, object: %s", 91*800a58d9SAndroid Build Coastguard Worker local_src, bucket_name, object_name) 92*800a58d9SAndroid Build Coastguard Worker try: 93*800a58d9SAndroid Build Coastguard Worker with io.FileIO(local_src, mode="rb") as upload_file: 94*800a58d9SAndroid Build Coastguard Worker media = apiclient.http.MediaIoBaseUpload(upload_file, mime_type) 95*800a58d9SAndroid Build Coastguard Worker request = self.service.objects().insert( 96*800a58d9SAndroid Build Coastguard Worker bucket=bucket_name, name=object_name, media_body=media) 97*800a58d9SAndroid Build Coastguard Worker response = self.Execute(request) 98*800a58d9SAndroid Build Coastguard Worker logger.info("Uploaded artifact: %s", response["selfLink"]) 99*800a58d9SAndroid Build Coastguard Worker return response 100*800a58d9SAndroid Build Coastguard Worker except OSError as e: 101*800a58d9SAndroid Build Coastguard Worker logger.error("Uploading artifact fails: %s", str(e)) 102*800a58d9SAndroid Build Coastguard Worker raise errors.DriverError(str(e)) 103*800a58d9SAndroid Build Coastguard Worker 104*800a58d9SAndroid Build Coastguard Worker def Delete(self, bucket_name, object_name): 105*800a58d9SAndroid Build Coastguard Worker """Deletes a file. 106*800a58d9SAndroid Build Coastguard Worker 107*800a58d9SAndroid Build Coastguard Worker Args: 108*800a58d9SAndroid Build Coastguard Worker bucket_name: string, google cloud storage bucket name. 109*800a58d9SAndroid Build Coastguard Worker object_name: string, the name of the remote file in storage. 110*800a58d9SAndroid Build Coastguard Worker """ 111*800a58d9SAndroid Build Coastguard Worker logger.info("Deleting file: bucket: %s, object: %s", bucket_name, 112*800a58d9SAndroid Build Coastguard Worker object_name) 113*800a58d9SAndroid Build Coastguard Worker request = self.service.objects().delete( 114*800a58d9SAndroid Build Coastguard Worker bucket=bucket_name, object=object_name) 115*800a58d9SAndroid Build Coastguard Worker self.Execute(request) 116*800a58d9SAndroid Build Coastguard Worker logger.info("Deleted file: bucket: %s, object: %s", bucket_name, 117*800a58d9SAndroid Build Coastguard Worker object_name) 118*800a58d9SAndroid Build Coastguard Worker 119*800a58d9SAndroid Build Coastguard Worker def DeleteFiles(self, bucket_name, object_names): 120*800a58d9SAndroid Build Coastguard Worker """Deletes multiple files. 121*800a58d9SAndroid Build Coastguard Worker 122*800a58d9SAndroid Build Coastguard Worker Args: 123*800a58d9SAndroid Build Coastguard Worker bucket_name: string, google cloud storage bucket name. 124*800a58d9SAndroid Build Coastguard Worker object_names: A list of strings, each of which is a name of a remote file. 125*800a58d9SAndroid Build Coastguard Worker 126*800a58d9SAndroid Build Coastguard Worker Returns: 127*800a58d9SAndroid Build Coastguard Worker A tuple, (deleted, failed, error_msgs) 128*800a58d9SAndroid Build Coastguard Worker deleted: A list of names of objects that have been deleted. 129*800a58d9SAndroid Build Coastguard Worker faild: A list of names of objects that we fail to delete. 130*800a58d9SAndroid Build Coastguard Worker error_msgs: A list of failure messages. 131*800a58d9SAndroid Build Coastguard Worker """ 132*800a58d9SAndroid Build Coastguard Worker deleted = [] 133*800a58d9SAndroid Build Coastguard Worker failed = [] 134*800a58d9SAndroid Build Coastguard Worker error_msgs = [] 135*800a58d9SAndroid Build Coastguard Worker for object_name in object_names: 136*800a58d9SAndroid Build Coastguard Worker try: 137*800a58d9SAndroid Build Coastguard Worker self.Delete(bucket_name, object_name) 138*800a58d9SAndroid Build Coastguard Worker deleted.append(object_name) 139*800a58d9SAndroid Build Coastguard Worker except errors.DriverError as e: 140*800a58d9SAndroid Build Coastguard Worker failed.append(object_name) 141*800a58d9SAndroid Build Coastguard Worker error_msgs.append(str(e)) 142*800a58d9SAndroid Build Coastguard Worker return deleted, failed, error_msgs 143*800a58d9SAndroid Build Coastguard Worker 144*800a58d9SAndroid Build Coastguard Worker def GetUrl(self, bucket_name, object_name): 145*800a58d9SAndroid Build Coastguard Worker """Get information about a file object. 146*800a58d9SAndroid Build Coastguard Worker 147*800a58d9SAndroid Build Coastguard Worker Args: 148*800a58d9SAndroid Build Coastguard Worker bucket_name: string, google cloud storage bucket name. 149*800a58d9SAndroid Build Coastguard Worker object_name: string, name of the file to look for. 150*800a58d9SAndroid Build Coastguard Worker 151*800a58d9SAndroid Build Coastguard Worker Returns: 152*800a58d9SAndroid Build Coastguard Worker Value of "selfLink" field from the response, which represents 153*800a58d9SAndroid Build Coastguard Worker a url to the file. 154*800a58d9SAndroid Build Coastguard Worker 155*800a58d9SAndroid Build Coastguard Worker Raises: 156*800a58d9SAndroid Build Coastguard Worker errors.ResourceNotFoundError: when file is not found. 157*800a58d9SAndroid Build Coastguard Worker """ 158*800a58d9SAndroid Build Coastguard Worker item = utils.RetryExceptionType( 159*800a58d9SAndroid Build Coastguard Worker errors.ResourceNotFoundError, 160*800a58d9SAndroid Build Coastguard Worker self.GET_OBJ_MAX_RETRY, 161*800a58d9SAndroid Build Coastguard Worker self.Get, 162*800a58d9SAndroid Build Coastguard Worker self.GET_OBJ_RETRY_SLEEP, 163*800a58d9SAndroid Build Coastguard Worker utils.DEFAULT_RETRY_BACKOFF_FACTOR, 164*800a58d9SAndroid Build Coastguard Worker bucket_name=bucket_name, 165*800a58d9SAndroid Build Coastguard Worker object_name=object_name) 166*800a58d9SAndroid Build Coastguard Worker return item["selfLink"] 167