From e70bcfa2fca41215c6a592e78048030bec96c1b4 Mon Sep 17 00:00:00 2001 From: Dylan Gonzales Date: Thu, 22 Sep 2022 14:35:33 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=A5=B7=20Private=20Endpoint=20Client=20(#?= =?UTF-8?q?41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../static/changelog/iotStorageClient.md | 4 ++ .../static/packages/iotStorageClient.md | 6 ++- .../static/packages/iotStorageClientAsync.md | 6 ++- iot-storage-client/CHANGELOG.md | 4 ++ .../iot/storage/client/_aioclient.py | 10 +++++ .../iot/storage/client/_client.py | 10 +++++ .../iot/storage/client/_helpers.py | 42 +++++++++++++------ .../iot/storage/client/_version.py | 2 +- 8 files changed, 69 insertions(+), 15 deletions(-) diff --git a/docs/public/static/changelog/iotStorageClient.md b/docs/public/static/changelog/iotStorageClient.md index 539de05..d11c7fc 100644 --- a/docs/public/static/changelog/iotStorageClient.md +++ b/docs/public/static/changelog/iotStorageClient.md @@ -4,6 +4,10 @@ title: iot-storage-client Changelog # Release History for iot-storage-client +### 1.2.0 (09/22/2022) + +- Instantiate clients that interact via Azure Private Links/Endpoints + ### 1.1.8 (09/13/2022) - Update documentation diff --git a/docs/public/static/packages/iotStorageClient.md b/docs/public/static/packages/iotStorageClient.md index e4a9488..b88c4af 100644 --- a/docs/public/static/packages/iotStorageClient.md +++ b/docs/public/static/packages/iotStorageClient.md @@ -93,7 +93,7 @@ A wrapper client to interact with the Azure Blob Service at the account level. This client provides operations to list, create and delete storage containers and blobs within the account. ```python -IoTStorageClient(credential_type, location_type, account_name, credential, module=None, host=None, port=None) +IoTStorageClient(credential_type, location_type, account_name, credential, module=None, host=None, port=None, private=False) ``` **Parameters** @@ -126,6 +126,10 @@ IoTStorageClient(credential_type, location_type, account_name, credential, modul The open port of the Azure storage account when it lives on an IoT Edge device. +- `private` Optional[bool] + + Does this client connect to an Azure storage account over a private endpoint? + ### Container Exists Method Check if a container exists. diff --git a/docs/public/static/packages/iotStorageClientAsync.md b/docs/public/static/packages/iotStorageClientAsync.md index 1e3cee3..dc3b734 100644 --- a/docs/public/static/packages/iotStorageClientAsync.md +++ b/docs/public/static/packages/iotStorageClientAsync.md @@ -93,7 +93,7 @@ A wrapper client to interact with the Azure Blob Service asynchronously at the a This client provides operations to list, create and delete storage containers and blobs within the account. ```python -IoTStorageClientAsync(credential_type, location_type, account_name, credential, module=None, host=None, port=None) +IoTStorageClientAsync(credential_type, location_type, account_name, credential, module=None, host=None, port=None, private=False) ``` **Parameters** @@ -126,6 +126,10 @@ IoTStorageClientAsync(credential_type, location_type, account_name, credential, The open port of the Azure storage account when it lives on an IoT Edge device. +- `private` Optional[bool] + + Does this client connect to an Azure storage account over a private endpoint? + ### Container Exists Method Check if a container exists. diff --git a/iot-storage-client/CHANGELOG.md b/iot-storage-client/CHANGELOG.md index 4508234..8face07 100644 --- a/iot-storage-client/CHANGELOG.md +++ b/iot-storage-client/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History +## 1.2.0 (09/22/2022) + +- Instantiate clients that interact via Azure Private Links/Endpoints + ## 1.1.8 (09/13/2022) - Update documentation diff --git a/iot-storage-client/iot/storage/client/_aioclient.py b/iot-storage-client/iot/storage/client/_aioclient.py index 0ee14e1..36bad25 100644 --- a/iot-storage-client/iot/storage/client/_aioclient.py +++ b/iot-storage-client/iot/storage/client/_aioclient.py @@ -37,6 +37,7 @@ class IoTStorageClientAsync: module: Optional[str] = None host: Optional[str] = None port: Optional[str] = None + private: Optional[bool] = False service_client: BlobServiceClient container_client: ContainerClient @@ -51,6 +52,7 @@ def __init__( module: Optional[str] = None, host: Optional[str] = None, port: Optional[str] = None, + private: Optional[bool] = False, ) -> None: self.credential_type = credential_type self.location_type = location_type @@ -59,6 +61,7 @@ def __init__( self.module = module self.host = host self.port = port + self.private = private self.instantiate_service_client() def __repr__(self) -> str: @@ -82,6 +85,7 @@ def instantiate_service_client(self) -> None: if self.location_type == LocationType.CLOUD_BASED: connection_string = generate_cloud_conn_str( account=self.account_name, + private=self.private, account_key=self.credential if self.credential_type == CredentialType.ACCOUNT_KEY else None, @@ -520,6 +524,7 @@ async def generate_file_sas_url( return generate_cloud_sas_url( account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=f"{container_name}/{source}", ) if self.location_type == LocationType.EDGE_BASED: @@ -528,6 +533,7 @@ async def generate_file_sas_url( port=self.port, account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=f"{container_name}/{source}", ) if self.location_type == LocationType.LOCAL_BASED: @@ -536,6 +542,7 @@ async def generate_file_sas_url( port=self.port, account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=f"{container_name}/{source}", ) return None @@ -583,6 +590,7 @@ async def generate_container_sas_url( return generate_cloud_sas_url( account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=container_name, ) if self.location_type == LocationType.EDGE_BASED: @@ -591,6 +599,7 @@ async def generate_container_sas_url( port=self.port, account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=container_name, ) if self.location_type == LocationType.LOCAL_BASED: @@ -599,6 +608,7 @@ async def generate_container_sas_url( port=self.port, account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=container_name, ) return None diff --git a/iot-storage-client/iot/storage/client/_client.py b/iot-storage-client/iot/storage/client/_client.py index 546c3ca..dfb5ea2 100644 --- a/iot-storage-client/iot/storage/client/_client.py +++ b/iot-storage-client/iot/storage/client/_client.py @@ -39,6 +39,7 @@ class IoTStorageClient: module: Optional[str] = None host: Optional[str] = None port: Optional[str] = None + private: Optional[bool] = False service_client: BlobServiceClient container_client: ContainerClient @@ -53,6 +54,7 @@ def __init__( module: Optional[str] = None, host: Optional[str] = None, port: Optional[str] = None, + private: Optional[bool] = False, ) -> None: self.credential_type = credential_type self.location_type = location_type @@ -61,6 +63,7 @@ def __init__( self.module = module self.host = host self.port = port + self.private = private self.instantiate_service_client() def __repr__(self) -> str: @@ -84,6 +87,7 @@ def instantiate_service_client(self) -> None: if self.location_type == LocationType.CLOUD_BASED: connection_string = generate_cloud_conn_str( account=self.account_name, + private=self.private, account_key=self.credential if self.credential_type == CredentialType.ACCOUNT_KEY else None, @@ -505,6 +509,7 @@ def generate_file_sas_url( return generate_cloud_sas_url( account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=f"{container_name}/{source}", ) if self.location_type == LocationType.EDGE_BASED: @@ -513,6 +518,7 @@ def generate_file_sas_url( port=self.port, account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=f"{container_name}/{source}", ) if self.location_type == LocationType.LOCAL_BASED: @@ -521,6 +527,7 @@ def generate_file_sas_url( port=self.port, account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=f"{container_name}/{source}", ) return None @@ -568,6 +575,7 @@ def generate_container_sas_url( return generate_cloud_sas_url( account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=container_name, ) if self.location_type == LocationType.EDGE_BASED: @@ -576,6 +584,7 @@ def generate_container_sas_url( port=self.port, account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=container_name, ) if self.location_type == LocationType.LOCAL_BASED: @@ -584,6 +593,7 @@ def generate_container_sas_url( port=self.port, account=self.account_name, account_sas=sas_token, + private=self.private, blob_path=container_name, ) return None diff --git a/iot-storage-client/iot/storage/client/_helpers.py b/iot-storage-client/iot/storage/client/_helpers.py index c3ecc6b..abd926a 100644 --- a/iot-storage-client/iot/storage/client/_helpers.py +++ b/iot-storage-client/iot/storage/client/_helpers.py @@ -32,23 +32,27 @@ def generate_edge_sas_url( port: str, account: str, account_sas: str, + private: bool, blob_path: Optional[str] = None, ) -> str: """ generate a SAS url for a child gateway device interacting with a parent gateway storage account """ + link = "privatelink.blob" if private else "blob" if not blob_path: - return "http://{host}:{port}/{account}.blob.core.windows.net?{sas}".format( + return "http://{host}:{port}/{account}.{link}.core.windows.net?{sas}".format( host=host, port=port, account=account, + link=link, sas=account_sas, ) - return "http://{host}:{port}/{account}.blob.core.windows.net/{path}?{sas}".format( + return "http://{host}:{port}/{account}.{link}.core.windows.net/{path}?{sas}".format( host=host, port=port, account=account, + link=link, path=blob_path, sas=account_sas, ) @@ -84,6 +88,7 @@ def generate_local_sas_url( port: str, account: str, account_sas: str, + private: bool, blob_path: Optional[str] = None, ) -> str: """ @@ -91,24 +96,30 @@ def generate_local_sas_url( interacting with a locally available storage account (AzureBlobStorageonIoTEdge) """ + link = "privatelink.blob" if private else "blob" if not blob_path: - return "http://{module}:{port}/{account}.blob.core.windows.net?{sas}".format( + return "http://{module}:{port}/{account}.{link}.core.windows.net?{sas}".format( module=module, port=port, account=account, + link=link, + sas=account_sas, + ) + return ( + "http://{module}:{port}/{account}.{link}.core.windows.net/{path}?{sas}".format( + module=module, + port=port, + account=account, + link=link, + path=blob_path, sas=account_sas, ) - return "http://{module}:{port}/{account}.blob.core.windows.net/{path}?{sas}".format( - module=module, - port=port, - account=account, - path=blob_path, - sas=account_sas, ) def generate_cloud_conn_str( account: str, + private: bool, account_key: Optional[str] = None, account_sas: Optional[str] = None, ) -> str: @@ -120,7 +131,10 @@ def generate_cloud_conn_str( return "Please provide your storage account key or SAS token" protocol = "DefaultEndpointsProtocol=https;" - endpoint = f"BlobEndpoint=https://{account}.blob.core.windows.net;" + if private: + endpoint = f"BlobEndpoint=https://{account}.privatelink.blob.core.windows.net;" + else: + endpoint = f"BlobEndpoint=https://{account}.blob.core.windows.net;" if account_key: credential = f"AccountName={account};AccountKey={account_key};" @@ -132,19 +146,23 @@ def generate_cloud_conn_str( def generate_cloud_sas_url( account: str, account_sas: str, + private: bool, blob_path: Optional[str] = None, ) -> str: """ generate a SAS url for an edge gateway device interacting with a cloud storage account """ + link = "privatelink.blob" if private else "blob" if not blob_path: - return "https://{account}.blob.core.windows.net?{sas}".format( + return "https://{account}.{link}.core.windows.net?{sas}".format( account=account, + link=link, sas=account_sas, ) - return "https://{account}.blob.core.windows.net/{path}?{sas}".format( + return "https://{account}.{link}.core.windows.net/{path}?{sas}".format( account=account, + link=link, path=blob_path, sas=account_sas, ) diff --git a/iot-storage-client/iot/storage/client/_version.py b/iot-storage-client/iot/storage/client/_version.py index c1e0af6..90b3199 100644 --- a/iot-storage-client/iot/storage/client/_version.py +++ b/iot-storage-client/iot/storage/client/_version.py @@ -1,3 +1,3 @@ -VERSION = "1.1.8" +VERSION = "1.2.0" __version__ = VERSION