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