azure_environment.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. """Implementation of AzureEnvironment class."""
  2. from azure.core.exceptions import HttpResponseError # type: ignore
  3. from azure.identity import DefaultAzureCredential # type: ignore
  4. from azure.storage.blob import BlobClient, BlobServiceClient # type: ignore
  5. from typing_extensions import Self
  6. from ..errors import LaunchError
  7. from ..utils import AZURE_BLOB_REGEX
  8. from .abstract import AbstractEnvironment
  9. class AzureEnvironment(AbstractEnvironment):
  10. """AzureEnvironment is a helper for accessing Azure resources."""
  11. def __init__(
  12. self,
  13. ) -> None:
  14. """Initialize an AzureEnvironment."""
  15. @classmethod
  16. def from_config(cls, config: dict, verify: bool = True) -> Self:
  17. """Create an AzureEnvironment from a config dict."""
  18. return cls()
  19. @classmethod
  20. def get_credentials(cls) -> DefaultAzureCredential:
  21. """Get Azure credentials."""
  22. try:
  23. return DefaultAzureCredential()
  24. except Exception as e:
  25. raise LaunchError(
  26. f"Could not get Azure credentials. Please make sure you have "
  27. f"configured your Azure CLI correctly.\n{e}"
  28. ) from e
  29. async def upload_file(self, source: str, destination: str) -> None:
  30. """Upload a file to Azure blob storage.
  31. Arguments:
  32. source (str): The path to the file to upload.
  33. destination (str): The destination path in Azure blob storage. Ex:
  34. https://<storage_account>.blob.core.windows.net/<storage_container>/<path>
  35. Raise:
  36. LaunchError: If the file could not be uploaded.
  37. """
  38. storage_account, storage_container, path = self.parse_uri(destination)
  39. _err_prefix = f"Could not upload file {source} to Azure blob {destination}"
  40. creds = self.get_credentials()
  41. try:
  42. client = BlobClient(
  43. f"https://{storage_account}.blob.core.windows.net",
  44. storage_container,
  45. path,
  46. credential=creds,
  47. )
  48. with open(source, "rb") as f:
  49. client.upload_blob(f, overwrite=True)
  50. except HttpResponseError as e:
  51. raise LaunchError(f"{_err_prefix}: {e.message}") from e
  52. except Exception as e:
  53. raise LaunchError(f"{_err_prefix}: {e.__class__.__name__}: {e}") from e
  54. async def upload_dir(self, source: str, destination: str) -> None:
  55. """Upload a directory to Azure blob storage."""
  56. raise NotImplementedError()
  57. async def verify_storage_uri(self, uri: str) -> None:
  58. """Verify that the given blob storage prefix exists.
  59. Args:
  60. uri (str): The URI to verify.
  61. """
  62. creds = self.get_credentials()
  63. storage_account, storage_container, _ = self.parse_uri(uri)
  64. try:
  65. client = BlobServiceClient(
  66. f"https://{storage_account}.blob.core.windows.net",
  67. credential=creds,
  68. )
  69. client.get_container_client(storage_container)
  70. except Exception as e:
  71. raise LaunchError(
  72. f"Could not verify storage URI {uri} in container {storage_container}."
  73. ) from e
  74. async def verify(self) -> None:
  75. """Verify that the AzureEnvironment is valid."""
  76. self.get_credentials()
  77. @staticmethod
  78. def parse_uri(uri: str) -> tuple[str, str, str]:
  79. """Parse an Azure blob storage URI into a storage account and container.
  80. Args:
  81. uri (str): The URI to parse.
  82. Returns:
  83. Tuple[str, str, prefix]: The storage account, container, and path.
  84. """
  85. match = AZURE_BLOB_REGEX.match(uri)
  86. if match is None:
  87. raise LaunchError(f"Could not parse Azure blob URI {uri}.")
  88. return match.group(1), match.group(2), match.group(3)