1# -*- coding: utf-8 -*- # 2# Copyright 2020 Google LLC. All Rights Reserved. 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"""Classes for cloud/file references yielded by storage iterators.""" 16 17from __future__ import absolute_import 18from __future__ import division 19from __future__ import unicode_literals 20 21import collections 22import json 23 24from googlecloudsdk.command_lib.storage import errors 25 26 27class Resource(object): 28 """Base class for a reference to one fully expanded iterator result. 29 30 This allows polymorphic iteration over wildcard-iterated URLs. The 31 reference contains a fully expanded URL string containing no wildcards and 32 referring to exactly one entity (if a wildcard is contained, it is assumed 33 this is part of the raw string and should never be treated as a wildcard). 34 35 Each reference represents a Bucket, Object, or Prefix. For filesystem URLs, 36 Objects represent files and Prefixes represent directories. 37 38 The metadata_object member contains the underlying object as it was retrieved. 39 It is populated by the calling iterator, which may only request certain 40 fields to reduce the number of server requests. 41 42 For filesystem and prefix URLs, metadata_object is not populated. 43 44 Attributes: 45 TYPE_STRING (str): String representing the resource's content type. 46 storage_url (StorageUrl): A StorageUrl object representing the resource. 47 """ 48 TYPE_STRING = 'resource' 49 50 def __init__(self, storage_url_object): 51 """Initialize the Resource object. 52 53 Args: 54 storage_url_object (StorageUrl): A StorageUrl object representing the 55 resource. 56 """ 57 self.storage_url = storage_url_object 58 59 def __str__(self): 60 return self.storage_url.url_string 61 62 def __eq__(self, other): 63 return ( 64 isinstance(other, self.__class__) and 65 self.storage_url == other.storage_url 66 ) 67 68 def is_container(self): 69 raise NotImplementedError('is_container must be overridden.') 70 71 72class CloudResource(Resource): 73 """For Resource classes with CloudUrl's. 74 75 Attributes: 76 TYPE_STRING (str): String representing the resource's content type. 77 scheme (storage_url.ProviderPrefix): Prefix indicating what cloud provider 78 hosts the bucket. 79 storage_url (StorageUrl): A StorageUrl object representing the resource. 80 """ 81 TYPE_STRING = 'cloud_resource' 82 83 @property 84 def scheme(self): 85 # TODO(b/168690302): Stop using string scheme in storage_url.py. 86 return self.storage_url.scheme 87 88 def get_json_dump(self): 89 raise NotImplementedError('get_json_dump must be overridden.') 90 91 92class BucketResource(CloudResource): 93 """Class representing a bucket. 94 95 Attributes: 96 TYPE_STRING (str): String representing the resource's content type. 97 storage_url (StorageUrl): A StorageUrl object representing the bucket. 98 name (str): Name of bucket. 99 scheme (storage_url.ProviderPrefix): Prefix indicating what cloud provider 100 hosts the bucket. 101 etag (str): HTTP version identifier. 102 metadata (object | dict): Cloud-provider specific data type for holding 103 bucket metadata. 104 """ 105 TYPE_STRING = 'cloud_bucket' 106 107 def __init__(self, storage_url_object, etag=None, metadata=None): 108 """Initializes resource. Args are a subset of attributes.""" 109 super(BucketResource, self).__init__(storage_url_object) 110 self.etag = etag 111 self.metadata = metadata 112 113 @property 114 def name(self): 115 return self.storage_url.bucket_name 116 117 def __eq__(self, other): 118 return ( 119 super(BucketResource, self).__eq__(other) and 120 self.etag == other.etag and 121 self.metadata == other.metadata 122 ) 123 124 def is_container(self): 125 return True 126 127 def get_json_dump(self): 128 super(BucketResource).get_json_dump() 129 130 131class ObjectResource(CloudResource): 132 """Class representing a cloud object confirmed to exist. 133 134 Attributes: 135 TYPE_STRING (str): String representing the resource's content type. 136 storage_url (StorageUrl): A StorageUrl object representing the object. 137 creation_time (datetime|None): Time the object was created. 138 etag (str|None): HTTP version identifier. 139 md5_hash (bytes): Base64-encoded digest of md5 hash. 140 metageneration (int|None): Generation object's metadata. 141 metadata (object|dict|None): Cloud-specific metadata type. 142 size (int|None): Size of object in bytes. 143 scheme (storage_url.ProviderPrefix): Prefix indicating what cloud provider 144 hosts the object. 145 bucket (str): Bucket that contains the object. 146 name (str): Name of object. 147 generation (str|None): Generation (or "version") of the underlying object. 148 """ 149 TYPE_STRING = 'cloud_object' 150 151 def __init__(self, 152 storage_url_object, 153 creation_time=None, 154 etag=None, 155 md5_hash=None, 156 metadata=None, 157 metageneration=None, 158 size=None): 159 """Initializes resource. Args are a subset of attributes.""" 160 super(ObjectResource, self).__init__(storage_url_object) 161 self.creation_time = creation_time 162 self.etag = etag 163 self.md5_hash = md5_hash 164 self.metageneration = metageneration 165 self.metadata = metadata 166 self.size = size 167 168 @property 169 def bucket(self): 170 return self.storage_url.bucket_name 171 172 @property 173 def name(self): 174 return self.storage_url.object_name 175 176 @property 177 def generation(self): 178 return self.storage_url.generation 179 180 def __eq__(self, other): 181 return (super(ObjectResource, self).__eq__(other) and 182 self.etag == other.etag and self.generation == other.generation and 183 self.md5_hash == other.md5_hash and self.metadata == other.metadata) 184 185 def is_container(self): 186 return False 187 188 def get_json_dump(self): 189 super(ObjectResource).get_json_dump() 190 191 192class PrefixResource(Resource): 193 """Class representing a cloud object. 194 195 Attributes: 196 TYPE_STRING (str): String representing the resource's content type. 197 storage_url (StorageUrl): A StorageUrl object representing the prefix. 198 prefix (str): A string representing the prefix. 199 """ 200 TYPE_STRING = 'prefix' 201 202 def __init__(self, storage_url_object, prefix): 203 """Initialize the PrefixResource object. 204 205 Args: 206 storage_url_object (StorageUrl): A StorageUrl object representing the 207 prefix. 208 prefix (str): A string representing the prefix. 209 """ 210 super(PrefixResource, self).__init__(storage_url_object) 211 self.prefix = prefix 212 213 def is_container(self): 214 return True 215 216 def get_json_dump(self): 217 return json.dumps(collections.OrderedDict([ 218 ('url', self.storage_url.versionless_url_string), 219 ('type', self.TYPE_STRING), 220 ]), indent=2) 221 222 223class FileObjectResource(Resource): 224 """Wrapper for a filesystem file. 225 226 Attributes: 227 TYPE_STRING (str): String representing the resource's content type. 228 storage_url (StorageUrl): A StorageUrl object representing the resource. 229 md5_hash (bytes): Base64-encoded digest of md5 hash. 230 """ 231 TYPE_STRING = 'file_object' 232 233 def __init__(self, storage_url_object, md5_hash=None): 234 """Initializes resource. Args are a subset of attributes.""" 235 super(FileObjectResource, self).__init__(storage_url_object) 236 self.md5_hash = md5_hash 237 238 def is_container(self): 239 return False 240 241 242class FileDirectoryResource(Resource): 243 """Wrapper for a File system directory.""" 244 TYPE_STRING = 'file_directory' 245 246 def is_container(self): 247 return True 248 249 250class UnknownResource(Resource): 251 """Represents a resource that may or may not exist.""" 252 TYPE_STRING = 'unknown' 253 254 def is_container(self): 255 raise errors.ValueCannotBeDeterminedError( 256 'Unknown whether or not UnknownResource is a container.') 257