Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Blob][STG73]Blob Tags #11418

Merged
merged 7 commits into from
Jun 10, 2020
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: 2 additions & 0 deletions sdk/storage/azure-storage-blob/azure/storage/blob/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
CorsRule,
ContainerProperties,
BlobProperties,
FilteredBlob,
LeaseProperties,
ContentSettings,
CopyProperties,
Expand Down Expand Up @@ -192,6 +193,7 @@ def download_blob_from_url(
'CorsRule',
'ContainerProperties',
'BlobProperties',
'FilteredBlob',
'LeaseProperties',
'ContentSettings',
'CopyProperties',
Expand Down
147 changes: 145 additions & 2 deletions sdk/storage/azure-storage-blob/azure/storage/blob/_blob_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@
QueryRequest,
CpkInfo)
from ._serialize import get_modify_conditions, get_source_conditions, get_cpk_scope_info, get_api_version, \
get_quick_query_serialization_info
serialize_blob_tags_header, serialize_blob_tags, get_quick_query_serialization_info
from ._deserialize import get_page_ranges_result, deserialize_blob_properties, deserialize_blob_stream
from ._quick_query_helper import QuickQueryReader
from ._upload_helpers import (
upload_block_blob,
upload_append_blob,
upload_page_blob)
from ._models import BlobType, BlobBlock
from ._models import BlobType, BlobBlock, BlobProperties
from ._download import StorageStreamDownloader
from ._lease import BlobLeaseClient, get_access_conditions

Expand Down Expand Up @@ -358,6 +358,7 @@ def _upload_blob_options( # pylint:disable=too-many-statements
blob_content_language=content_settings.content_language,
blob_content_disposition=content_settings.content_disposition
)
kwargs['blob_tags_string'] = serialize_blob_tags_header(kwargs.pop('tags', None))
kwargs['stream'] = stream
kwargs['length'] = length
kwargs['overwrite'] = overwrite
Expand Down Expand Up @@ -399,6 +400,16 @@ def upload_blob( # pylint: disable=too-many-locals
:param metadata:
Name-value pairs associated with the blob as metadata.
:type metadata: dict(str, str)
:keyword tags:
Name-value pairs associated with the blob as tag. Tags are case-sensitive.
The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
and tag values must be between 0 and 256 characters.
Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)

.. versionadded:: 12.4.0

:paramtype tags: dict(str, str)
:keyword bool overwrite: Whether the blob to be uploaded should overwrite the current data.
If True, upload_blob will overwrite the existing data. If set to False, the
operation will fail with ResourceExistsError. The exception to the above is with Append
Expand Down Expand Up @@ -1103,6 +1114,9 @@ def _create_page_blob_options( # type: ignore
headers['x-ms-access-tier'] = premium_page_blob_tier.value # type: ignore
except AttributeError:
headers['x-ms-access-tier'] = premium_page_blob_tier # type: ignore

blob_tags_string = serialize_blob_tags_header(kwargs.pop('tags', None))

options = {
'content_length': 0,
'blob_content_length': size,
Expand All @@ -1113,6 +1127,7 @@ def _create_page_blob_options( # type: ignore
'modified_access_conditions': mod_conditions,
'cpk_scope_info': cpk_scope_info,
'cpk_info': cpk_info,
'blob_tags_string': blob_tags_string,
'cls': return_response_headers,
'headers': headers}
options.update(kwargs)
Expand Down Expand Up @@ -1142,6 +1157,16 @@ def create_page_blob( # type: ignore
A page blob tier value to set the blob to. The tier correlates to the size of the
blob and number of allowed IOPS. This is only applicable to page blobs on
premium storage accounts.
:keyword tags:
Name-value pairs associated with the blob as tag. Tags are case-sensitive.
The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
and tag values must be between 0 and 256 characters.
Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)

.. versionadded:: 12.4.0

:paramtype tags: dict(str, str)
:keyword int sequence_number:
Only for Page blobs. The sequence number is a user-controlled value that you can use to
track requests. The value of the sequence number must be between 0
Expand Down Expand Up @@ -1223,6 +1248,7 @@ def _create_append_blob_options(self, content_settings=None, metadata=None, **kw
raise ValueError("Customer provided encryption key must be used over HTTPS.")
cpk_info = CpkInfo(encryption_key=cpk.key_value, encryption_key_sha256=cpk.key_hash,
encryption_algorithm=cpk.algorithm)
blob_tags_string = serialize_blob_tags_header(kwargs.pop('tags', None))

options = {
'content_length': 0,
Expand All @@ -1232,6 +1258,7 @@ def _create_append_blob_options(self, content_settings=None, metadata=None, **kw
'modified_access_conditions': mod_conditions,
'cpk_scope_info': cpk_scope_info,
'cpk_info': cpk_info,
'blob_tags_string': blob_tags_string,
'cls': return_response_headers,
'headers': headers}
options.update(kwargs)
Expand All @@ -1248,6 +1275,16 @@ def create_append_blob(self, content_settings=None, metadata=None, **kwargs):
:param metadata:
Name-value pairs associated with the blob as metadata.
:type metadata: dict(str, str)
:keyword tags:
Name-value pairs associated with the blob as tag. Tags are case-sensitive.
The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
and tag values must be between 0 and 256 characters.
Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)

.. versionadded:: 12.4.0

:paramtype tags: dict(str, str)
:keyword lease:
Required if the blob has an active lease. Value can be a BlobLeaseClient object
or the lease ID as a string.
Expand Down Expand Up @@ -1410,10 +1447,13 @@ def _start_copy_from_url_options(self, source_url, metadata=None, incremental_co

timeout = kwargs.pop('timeout', None)
dest_mod_conditions = get_modify_conditions(kwargs)
blob_tags_string = serialize_blob_tags_header(kwargs.pop('tags', None))

options = {
'copy_source': source_url,
'timeout': timeout,
'modified_access_conditions': dest_mod_conditions,
'blob_tags_string': blob_tags_string,
'headers': headers,
'cls': return_response_headers,
}
Expand Down Expand Up @@ -1485,6 +1525,16 @@ def start_copy_from_url(self, source_url, metadata=None, incremental_copy=False,
the previously copied snapshot are transferred to the destination.
The copied snapshots are complete copies of the original snapshot and
can be read or copied from as usual. Defaults to False.
:keyword tags:
Name-value pairs associated with the blob as tag. Tags are case-sensitive.
The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
and tag values must be between 0 and 256 characters.
Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)

.. versionadded:: 12.4.0

:paramtype tags: dict(str, str)
:keyword ~datetime.datetime source_if_modified_since:
A DateTime value. Azure expects the date value passed in to be UTC.
If timezone is included, any non-UTC datetimes will be converted to UTC.
Expand Down Expand Up @@ -1997,6 +2047,7 @@ def _commit_block_list_options( # type: ignore
encryption_algorithm=cpk.algorithm)

tier = kwargs.pop('standard_blob_tier', None)
blob_tags_string = serialize_blob_tags_header(kwargs.pop('tags', None))

options = {
'blocks': block_lookup,
Expand All @@ -2009,6 +2060,7 @@ def _commit_block_list_options( # type: ignore
'cpk_scope_info': cpk_scope_info,
'cpk_info': cpk_info,
'tier': tier.value if tier else None,
'blob_tags_string': blob_tags_string,
'headers': headers
}
options.update(kwargs)
Expand All @@ -2033,6 +2085,16 @@ def commit_block_list( # type: ignore
:param metadata:
Name-value pairs associated with the blob as metadata.
:type metadata: dict[str, str]
:keyword tags:
Name-value pairs associated with the blob as tag. Tags are case-sensitive.
The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
and tag values must be between 0 and 256 characters.
Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)

.. versionadded:: 12.4.0

:paramtype tags: dict(str, str)
:keyword lease:
Required if the blob has an active lease. Value can be a BlobLeaseClient object
or the lease ID as a string.
Expand Down Expand Up @@ -2124,6 +2186,87 @@ def set_premium_page_blob_tier(self, premium_page_blob_tier, **kwargs):
except StorageErrorException as error:
process_storage_error(error)

def _set_blob_tags_options(self, tags=None, **kwargs):
# type: (Optional[Dict[str, str]], **Any) -> Dict[str, Any]
tags = serialize_blob_tags(tags)

options = {
'tags': tags,
'cls': return_response_headers}
options.update(kwargs)
return options

@distributed_trace
def set_blob_tags(self, tags=None, **kwargs):
# type: (Optional[Dict[str, str]], **Any) -> Dict[str, Any]
"""The Set Tags operation enables users to set tags on a blob or specific blob version, but not snapshot.
Each call to this operation replaces all existing tags attached to the blob. To remove all
tags from the blob, call this operation with no tags set.

.. versionadded:: 12.4.0
This operation was introduced in API version '2019-12-12'.

:param tags:
Name-value pairs associated with the blob as tag. Tags are case-sensitive.
The tag set may contain at most 10 tags. Tag keys must be between 1 and 128 characters,
and tag values must be between 0 and 256 characters.
Valid tag key and value characters include: lowercase and uppercase letters, digits (0-9),
space (` `), plus (+), minus (-), period (.), solidus (/), colon (:), equals (=), underscore (_)
:type tags: dict(str, str)
:keyword str version_id:
The version id parameter is an opaque DateTime
value that, when present, specifies the version of the blob to add tags to.
:keyword bool validate_content:
annatisch marked this conversation as resolved.
Show resolved Hide resolved
If true, calculates an MD5 hash of the tags content. The storage
service checks the hash of the content that has arrived
with the hash that was sent. This is primarily valuable for detecting
bitflips on the wire if using http instead of https, as https (the default),
will already validate. Note that this MD5 hash is not stored with the
blob.
:keyword int timeout:
The timeout parameter is expressed in seconds.
:returns: Blob-updated property dict (Etag and last modified)
:rtype: Dict[str, Any]
"""
options = self._set_blob_tags_options(tags=tags, **kwargs)
try:
return self._client.blob.set_tags(**options)
except StorageErrorException as error:
process_storage_error(error)

def _get_blob_tags_options(self, **kwargs):
# type: (**Any) -> Dict[str, str]

options = {
'version_id': kwargs.pop('version_id', None),
'snapshot': self.snapshot,
xiafu-msft marked this conversation as resolved.
Show resolved Hide resolved
'timeout': kwargs.pop('timeout', None),
'cls': return_headers_and_deserialized}
return options

@distributed_trace
def get_blob_tags(self, **kwargs):
# type: (**Any) -> Dict[str, str]
"""The Get Tags operation enables users to get tags on a blob or specific blob version, or snapshot.

.. versionadded:: 12.4.0
This operation was introduced in API version '2019-12-12'.

:keyword str version_id:
The version id parameter is an opaque DateTime
value that, when present, specifies the version of the blob to add tags to.
:keyword int timeout:
The timeout parameter is expressed in seconds.
:returns: Key value pairs of blob tags.
:rtype: Dict[str, str]
"""
options = self._get_blob_tags_options(**kwargs)
try:
_, tags = self._client.blob.get_tags(**options)
return BlobProperties._parse_tags(tags) # pylint: disable=protected-access
except StorageErrorException as error:
process_storage_error(error)

def _get_page_ranges_options( # type: ignore
self, offset=None, # type: Optional[int]
length=None, # type: Optional[int]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from ._generated.models import StorageErrorException, StorageServiceProperties, KeyInfo
from ._container_client import ContainerClient
from ._blob_client import BlobClient
from ._models import ContainerPropertiesPaged
from ._models import ContainerPropertiesPaged, FilteredBlobPaged
from ._serialize import get_api_version
from ._deserialize import service_stats_deserialize, service_properties_deserialize

Expand Down Expand Up @@ -426,6 +426,37 @@ def list_containers(
page_iterator_class=ContainerPropertiesPaged
)

@distributed_trace
def find_blobs_by_tags(self, filter_expression, **kwargs):
# type: (str, **Any) -> ItemPaged[FilteredBlob]
"""The Filter Blobs operation enables callers to list blobs across all
containers whose tags match a given search expression. Filter blobs
searches across all containers within a storage account but can be
scoped within the expression to a single container.

:param str filter_expression:
The expression to find blobs whose tags matches the specified condition.
eg. "yourtagname='firsttag' and yourtagname2='secondtag'"
To specify a container, eg. "@container=’containerName’ and Name = ‘C’"
:keyword int results_per_page:
The max result per page when paginating.
:keyword int timeout:
The timeout parameter is expressed in seconds.
:returns: An iterable (auto-paging) response of BlobProperties.
:rtype: ~azure.core.paging.ItemPaged[~azure.storage.blob.FilteredBlob]
"""

results_per_page = kwargs.pop('results_per_page', None)
timeout = kwargs.pop('timeout', None)
command = functools.partial(
self._client.service.filter_blobs,
where=filter_expression,
timeout=timeout,
**kwargs)
return ItemPaged(
command, results_per_page=results_per_page,
page_iterator_class=FilteredBlobPaged)

@distributed_trace
def create_container(
self, name, # type: str
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,17 +634,17 @@ def set_container_access_policy(

@distributed_trace
def list_blobs(self, name_starts_with=None, include=None, **kwargs):
# type: (Optional[str], Optional[Any], **Any) -> ItemPaged[BlobProperties]
# type: (Optional[str], Optional[Union[str, List[str]]], **Any) -> ItemPaged[BlobProperties]
"""Returns a generator to list the blobs under the specified container.
The generator will lazily follow the continuation tokens returned by
the service.

:param str name_starts_with:
Filters the results to return only blobs whose names
begin with the specified prefix.
:param list[str] include:
:param list[str] or str include:
xiafu-msft marked this conversation as resolved.
Show resolved Hide resolved
Specifies one or more additional datasets to include in the response.
Options include: 'snapshots', 'metadata', 'uncommittedblobs', 'copy', 'deleted'.
Options include: 'snapshots', 'metadata', 'uncommittedblobs', 'copy', 'deleted', 'tags'.
:keyword int timeout:
The timeout parameter is expressed in seconds.
:returns: An iterable (auto-paging) response of BlobProperties.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
# regenerated.
# --------------------------------------------------------------------------

VERSION = "2019-12-12"
VERSION = "2019-10-10"

Loading