Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Populate storage class from HEAD Object responses. #3691

Merged
merged 1 commit into from
Mar 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion boto/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ class Provider(object):
# Note that this version header is not to be confused with
# the Google Cloud Storage 'x-goog-api-version' header.
VERSION_ID_HEADER_KEY: GOOG_HEADER_PREFIX + 'version-id',
STORAGE_CLASS_HEADER_KEY: None,
STORAGE_CLASS_HEADER_KEY: GOOG_HEADER_PREFIX + 'storage-class',
MFA_HEADER_KEY: None,
RESTORE_HEADER_KEY: None,
}
Expand Down
1 change: 1 addition & 0 deletions boto/s3/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ def _get_key_internal(self, key_name, headers, query_args_l):
k.handle_version_headers(response)
k.handle_encryption_headers(response)
k.handle_restore_headers(response)
k.handle_storage_class_header(response)
k.handle_addl_headers(response.getheaders())
return k, response
else:
Expand Down
12 changes: 12 additions & 0 deletions boto/s3/key.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,18 @@ def handle_encryption_headers(self, resp):
else:
self.encrypted = None

def handle_storage_class_header(self, resp):
provider = self.bucket.connection.provider
if provider.storage_class_header:
self._storage_class = resp.getheader(
provider.storage_class_header, None)
if (self._storage_class is None and
provider.get_provider_name() == 'aws'):
# S3 docs for HEAD object requests say S3 will return this
# header for all objects except Standard storage class objects.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why you want to populate the storage class in this case, if the S3 docs say they won't populate the header in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the object were of a nondefault class (e.g. STANDARD_IA), this header would be present with that value; only then does S3 add the storage class header to the response. As such, we can assume it's STANDARD when the header is not present. This allows us to avoid calling the _get_storage_class method, which performs an additional self.bucket.list call. Rather than do this (and potentially fail if the user doesn't have list permisson), we can obtain the storage class here.

For context, GSUtil will take advantage of this to populate the object's storage class for the output of gsutil stat <obj_url> and gsutil ls -L <obj_url>.

self._storage_class = 'STANDARD'


def handle_version_headers(self, resp, force=False):
provider = self.bucket.connection.provider
# If the Key object already has a version_id attribute value, it
Expand Down
15 changes: 10 additions & 5 deletions tests/integration/gs/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,15 @@

# Regexp for matching project-private default object ACL.
PROJECT_PRIVATE_RE = ('\s*<AccessControlList>\s*<Entries>\s*<Entry>'
'\s*<Scope type="GroupById"><ID>[0-9a-fA-F]+</ID></Scope>'
'\s*<Scope type="GroupById">\s*<ID>[0-9a-fA-F]+</ID>'
'\s*(<Name>[^<]+</Name>)?\s*</Scope>'
'\s*<Permission>FULL_CONTROL</Permission>\s*</Entry>\s*<Entry>'
'\s*<Scope type="GroupById"><ID>[0-9a-fA-F]+</ID></Scope>'
'\s*<Scope type="GroupById">\s*<ID>[0-9a-fA-F]+</ID>'
'\s*(<Name>[^<]+</Name>)?\s*</Scope>'
'\s*<Permission>FULL_CONTROL</Permission>\s*</Entry>\s*<Entry>'
'\s*<Scope type="GroupById"><ID>[0-9a-fA-F]+</ID></Scope>'
'\s*<Permission>READ</Permission></Entry>\s*</Entries>'
'\s*<Scope type="GroupById">\s*<ID>[0-9a-fA-F]+</ID>'
'\s*(<Name>[^<]+</Name>)?\s*</Scope>'
'\s*<Permission>READ</Permission>\s*</Entry>\s*</Entries>'
'\s*</AccessControlList>\s*')


Expand Down Expand Up @@ -349,7 +352,9 @@ def test_default_object_acls_storage_uri(self):
uri = storage_uri('gs://' + bucket_name)
# get default acl and make sure it's project-private
acl = uri.get_def_acl()
self.assertIsNotNone(re.search(PROJECT_PRIVATE_RE, acl.to_xml()))
self.assertIsNotNone(
re.search(PROJECT_PRIVATE_RE, acl.to_xml()),
'PROJECT_PRIVATE_RE not found in ACL XML:\n' + acl.to_xml())
# set default acl to a canned acl and verify it gets set
uri.set_def_acl('public-read')
acl = uri.get_def_acl()
Expand Down