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