diff options
| author | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
|---|---|---|
| committer | S. Solomon Darnell | 2025-03-28 21:52:21 -0500 |
| commit | 4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch) | |
| tree | ee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry | |
| parent | cc961e04ba734dd72309fb548a2f97d67d578813 (diff) | |
| download | gn-ai-master.tar.gz | |
Diffstat (limited to '.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry')
4 files changed, 526 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/__init__.py b/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/__init__.py new file mode 100644 index 00000000..fdf8caba --- /dev/null +++ b/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/__init__.py @@ -0,0 +1,5 @@ +# --------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# --------------------------------------------------------- + +__path__ = __import__("pkgutil").extend_path(__path__, __name__) diff --git a/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/registry.py b/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/registry.py new file mode 100644 index 00000000..a01e70d3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/registry.py @@ -0,0 +1,231 @@ +# --------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# --------------------------------------------------------- + +# pylint: disable=protected-access + +from os import PathLike +from pathlib import Path +from typing import IO, Any, AnyStr, Dict, List, Optional, Union + +from azure.ai.ml._restclient.v2022_10_01_preview.models import ManagedServiceIdentity as RestManagedServiceIdentity +from azure.ai.ml._restclient.v2022_10_01_preview.models import ( + ManagedServiceIdentityType as RestManagedServiceIdentityType, +) +from azure.ai.ml._restclient.v2022_10_01_preview.models import Registry as RestRegistry +from azure.ai.ml._restclient.v2022_10_01_preview.models import RegistryProperties +from azure.ai.ml._utils.utils import dump_yaml_to_file +from azure.ai.ml.constants._common import BASE_PATH_CONTEXT_KEY, PARAMS_OVERRIDE_KEY +from azure.ai.ml.entities._assets.intellectual_property import IntellectualProperty +from azure.ai.ml.entities._credentials import IdentityConfiguration +from azure.ai.ml.entities._resource import Resource +from azure.ai.ml.entities._util import load_from_dict + +from .registry_support_classes import RegistryRegionDetails + +CONTAINER_REGISTRY = "container_registry" +REPLICATION_LOCATIONS = "replication_locations" +INTELLECTUAL_PROPERTY = "intellectual_property" + + +class Registry(Resource): + def __init__( + self, + *, + name: str, + location: str, + identity: Optional[IdentityConfiguration] = None, + tags: Optional[Dict[str, str]] = None, + public_network_access: Optional[str] = None, + discovery_url: Optional[str] = None, + intellectual_property: Optional[IntellectualProperty] = None, + managed_resource_group: Optional[str] = None, + mlflow_registry_uri: Optional[str] = None, + replication_locations: Optional[List[RegistryRegionDetails]], + **kwargs: Any, + ): + """Azure ML registry. + + :param name: Name of the registry. Must be globally unique and is immutable. + :type name: str + :param location: The location this registry resource is located in. + :type location: str + :param identity: registry's System Managed Identity + :type identity: ManagedServiceIdentity + :param tags: Tags of the registry. + :type tags: dict + :param public_network_access: Whether to allow public endpoint connectivity. + :type public_network_access: str + :param discovery_url: Backend service base url for the registry. + :type discovery_url: str + :param intellectual_property: **Experimental** Intellectual property publisher. + :type intellectual_property: ~azure.ai.ml.entities.IntellectualProperty + :param managed_resource_group: Managed resource group created for the registry. + :type managed_resource_group: str + :param mlflow_registry_uri: Ml flow tracking uri for the registry. + :type mlflow_registry_uri: str + :param region_details: Details of each region the registry is in. + :type region_details: List[RegistryRegionDetails] + :param kwargs: A dictionary of additional configuration parameters. + :type kwargs: dict + """ + + super().__init__(name=name, tags=tags, **kwargs) + + # self.display_name = name # Do we need a top-level visible name value? + self.location = location + self.identity = identity + self.replication_locations = replication_locations + self.public_network_access = public_network_access + self.intellectual_property = intellectual_property + self.managed_resource_group = managed_resource_group + self.discovery_url = discovery_url + self.mlflow_registry_uri = mlflow_registry_uri + self.container_registry = None + + def dump( + self, + dest: Union[str, PathLike, IO[AnyStr]], + **kwargs: Any, + ) -> None: + """Dump the registry spec into a file in yaml format. + + :param dest: Path to a local file as the target, new file will be created, raises exception if the file exists. + :type dest: str + """ + yaml_serialized = self._to_dict() + dump_yaml_to_file(dest, yaml_serialized, default_flow_style=False) + + # The internal structure of the registry object is closer to how it's + # represented by the registry API, which differs from how registries + # are represented in YAML. This function converts those differences. + def _to_dict(self) -> Dict: + # JIT import to avoid experimental warnings on unrelated calls + from azure.ai.ml._schema.registry.registry import RegistrySchema + + schema = RegistrySchema(context={BASE_PATH_CONTEXT_KEY: "./"}) + + # Grab the first acr account of the first region and set that + # as the system-wide container registry. + # Although support for multiple ACRs per region, as well as + # different ACRs per region technically exist according to the + # API schema, we do not want to surface that as an option, + # since the use cases for variable/multiple ACRs are extremely + # limited, and would probably just confuse most users. + if self.replication_locations and len(self.replication_locations) > 0: + if self.replication_locations[0].acr_config and len(self.replication_locations[0].acr_config) > 0: + self.container_registry = self.replication_locations[0].acr_config[0] # type: ignore[assignment] + + res: dict = schema.dump(self) + return res + + @classmethod + def _load( + cls, + data: Optional[Dict] = None, + yaml_path: Optional[Union[PathLike, str]] = None, + params_override: Optional[list] = None, + **kwargs: Any, + ) -> "Registry": + data = data or {} + params_override = params_override or [] + context = { + BASE_PATH_CONTEXT_KEY: Path(yaml_path).parent if yaml_path else Path("./"), + PARAMS_OVERRIDE_KEY: params_override, + } + # JIT import to avoid experimental warnings on unrelated calls + from azure.ai.ml._schema.registry.registry import RegistrySchema + + loaded_schema = load_from_dict(RegistrySchema, data, context, **kwargs) + cls._convert_yaml_dict_to_entity_input(loaded_schema) + return Registry(**loaded_schema) + + @classmethod + def _from_rest_object(cls, rest_obj: RestRegistry) -> Optional["Registry"]: + if not rest_obj: + return None + real_registry = rest_obj.properties + + # Convert from api name region_details to user-shown name "replication locations" + replication_locations = [] + if real_registry and real_registry.region_details: + replication_locations = [ + RegistryRegionDetails._from_rest_object(details) for details in real_registry.region_details + ] + identity = None + if rest_obj.identity and isinstance(rest_obj.identity, RestManagedServiceIdentity): + identity = IdentityConfiguration._from_rest_object(rest_obj.identity) + return Registry( + name=rest_obj.name, + identity=identity, + id=rest_obj.id, + tags=rest_obj.tags, + location=rest_obj.location, + public_network_access=real_registry.public_network_access, + discovery_url=real_registry.discovery_url, + intellectual_property=( + IntellectualProperty(publisher=real_registry.intellectual_property_publisher) + if real_registry.intellectual_property_publisher + else None + ), + managed_resource_group=real_registry.managed_resource_group, + mlflow_registry_uri=real_registry.ml_flow_registry_uri, + replication_locations=replication_locations, # type: ignore[arg-type] + ) + + # There are differences between what our registry validation schema + # accepts, and how we actually represent things internally. + # This is mostly due to the compromise required to balance + # the actual shape of registries as they're defined by + # autorest with how the spec wanted users to be able to + # configure them. This function should eventually be + @classmethod + def _convert_yaml_dict_to_entity_input( + cls, + input: Dict, # pylint: disable=redefined-builtin + ) -> None: + # pop container_registry value. + global_acr_exists = False + if CONTAINER_REGISTRY in input: + acr_input = input.pop(CONTAINER_REGISTRY) + global_acr_exists = True + for region_detail in input[REPLICATION_LOCATIONS]: + # Apply container_registry as acr_config of each region detail + if global_acr_exists: + if not hasattr(region_detail, "acr_details") or len(region_detail.acr_details) == 0: + region_detail.acr_config = [acr_input] # pylint: disable=(possibly-used-before-assignment + + def _to_rest_object(self) -> RestRegistry: + """Build current parameterized schedule instance to a registry object before submission. + + :return: Rest registry. + :rtype: RestRegistry + """ + identity = RestManagedServiceIdentity(type=RestManagedServiceIdentityType.SYSTEM_ASSIGNED) + replication_locations = [] + if self.replication_locations: + replication_locations = [details._to_rest_object() for details in self.replication_locations] + # Notes about this construction. + # RestRegistry.properties.tags: this property exists due to swagger inheritance + # issues, don't actually use it, use top level RestRegistry.tags instead + # RestRegistry.properties.managed_resource_group_tags: Registries create a + # managed resource group to manage their internal sub-resources. + # We always want the tags on this MRG to match those of the registry itself + # to keep janitor policies aligned. + return RestRegistry( + name=self.name, + location=self.location, + identity=identity, + tags=self.tags, + properties=RegistryProperties( + public_network_access=self.public_network_access, + discovery_url=self.discovery_url, + intellectual_property_publisher=( + (self.intellectual_property.publisher) if self.intellectual_property else None + ), + managed_resource_group=self.managed_resource_group, + ml_flow_registry_uri=self.mlflow_registry_uri, + region_details=replication_locations, + managed_resource_group_tags=self.tags, + ), + ) diff --git a/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/registry_support_classes.py b/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/registry_support_classes.py new file mode 100644 index 00000000..810c5df5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/registry_support_classes.py @@ -0,0 +1,273 @@ +# --------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# --------------------------------------------------------- +# pylint:disable=protected-access,no-else-return + +from copy import deepcopy +from functools import reduce +from typing import List, Optional, Union + +from azure.ai.ml._exception_helper import log_and_raise_error +from azure.ai.ml._restclient.v2022_10_01_preview.models import AcrDetails as RestAcrDetails +from azure.ai.ml._restclient.v2022_10_01_preview.models import ArmResourceId as RestArmResourceId +from azure.ai.ml._restclient.v2022_10_01_preview.models import RegistryRegionArmDetails as RestRegistryRegionArmDetails +from azure.ai.ml._restclient.v2022_10_01_preview.models import StorageAccountDetails as RestStorageAccountDetails +from azure.ai.ml._restclient.v2022_10_01_preview.models import SystemCreatedAcrAccount as RestSystemCreatedAcrAccount +from azure.ai.ml._restclient.v2022_10_01_preview.models import ( + SystemCreatedStorageAccount as RestSystemCreatedStorageAccount, +) +from azure.ai.ml._restclient.v2022_10_01_preview.models import UserCreatedAcrAccount as RestUserCreatedAcrAccount +from azure.ai.ml.constants._registry import StorageAccountType +from azure.ai.ml.exceptions import ErrorCategory, ErrorTarget, ValidationErrorType, ValidationException + +from .util import _make_rest_user_storage_from_id + + +# This exists despite not being used by the schema validator because this entire +# class is an output only value from the API. +class SystemCreatedAcrAccount: + def __init__( + self, + *, + acr_account_sku: str, + arm_resource_id: Optional[str] = None, + ): + """Azure ML ACR account. + + :param acr_account_sku: The storage account service tier. Currently + only Premium is a valid option for registries. + :type acr_account_sku: str + :param arm_resource_id: Resource ID of the ACR account. + :type arm_resource_id: str. Default value is None. + """ + self.acr_account_sku = acr_account_sku + self.arm_resource_id = arm_resource_id + + # acr should technically be a union between str and SystemCreatedAcrAccount, + # but python doesn't accept self class references apparently. + # Class method instead of normal function to accept possible + # string input. + @classmethod + def _to_rest_object(cls, acr: Union[str, "SystemCreatedAcrAccount"]) -> RestAcrDetails: + if hasattr(acr, "acr_account_sku") and acr.acr_account_sku is not None: + # SKU enum requires input to be a capitalized word, + # so we format the input to be acceptable as long as spelling is + # correct. + acr_account_sku = acr.acr_account_sku.capitalize() + # We DO NOT want to set the arm_resource_id. The backend provides very + # unhelpful errors if you provide an empty/null/invalid resource ID, + # and ignores the value otherwise. It's better to avoid setting it in + # the conversion in this direction at all. + return RestAcrDetails( + system_created_acr_account=RestSystemCreatedAcrAccount( + acr_account_sku=acr_account_sku, + ) + ) + else: + return RestAcrDetails( + user_created_acr_account=RestUserCreatedAcrAccount(arm_resource_id=RestArmResourceId(resource_id=acr)) + ) + + @classmethod + def _from_rest_object(cls, rest_obj: RestAcrDetails) -> Optional["Union[str, SystemCreatedAcrAccount]"]: + if not rest_obj: + return None + if hasattr(rest_obj, "system_created_acr_account") and rest_obj.system_created_acr_account is not None: + resource_id = None + if rest_obj.system_created_acr_account.arm_resource_id: + resource_id = rest_obj.system_created_acr_account.arm_resource_id.resource_id + return SystemCreatedAcrAccount( + acr_account_sku=rest_obj.system_created_acr_account.acr_account_sku, + arm_resource_id=resource_id, + ) + elif hasattr(rest_obj, "user_created_acr_account") and rest_obj.user_created_acr_account is not None: + res: Optional[str] = rest_obj.user_created_acr_account.arm_resource_id.resource_id + return res + else: + return None + + +class SystemCreatedStorageAccount: + def __init__( + self, + *, + storage_account_hns: bool, + storage_account_type: Optional[StorageAccountType], + arm_resource_id: Optional[str] = None, + replicated_ids: Optional[List[str]] = None, + replication_count: int = 1, + ): + """ + :param arm_resource_id: Resource ID of the storage account. + :type arm_resource_id: str + :param storage_account_hns: Whether or not this storage account + has hierarchical namespaces enabled. + :type storage_account_hns: bool + :param storage_account_type: Allowed values: "Standard_LRS", + "Standard_GRS, "Standard_RAGRS", "Standard_ZRS", "Standard_GZRS", + "Standard_RAGZRS", "Premium_LRS", "Premium_ZRS" + :type storage_account_type: StorageAccountType + :param replication_count: The number of replicas of this storage account + that should be created. Defaults to 1. Values less than 1 are invalid. + :type replication_count: int + :param replicated_ids: If this storage was replicated, then this is a + list of all storage IDs with these settings for this registry. + Defaults to none for un-replicated storage accounts. + :type replicated_ids: List[str] + """ + self.arm_resource_id = arm_resource_id + self.storage_account_hns = storage_account_hns + self.storage_account_type = storage_account_type + self.replication_count = replication_count + self.replicated_ids = replicated_ids + + +# Per-region information for registries. +class RegistryRegionDetails: + def __init__( + self, + *, + acr_config: Optional[List[Union[str, SystemCreatedAcrAccount]]] = None, + location: Optional[str] = None, + storage_config: Optional[Union[List[str], SystemCreatedStorageAccount]] = None, + ): + """Details for each region a registry is in. + + :param acr_details: List of ACR account details. Each value can either be a + single string representing the arm_resource_id of a user-created + acr_details object, or a entire SystemCreatedAcrAccount object. + :type acr_details: List[Union[str, SystemCreatedAcrAccount]] + :param location: The location where the registry exists. + :type location: str + :param storage_account_details: List of storage accounts. Each value + can either be a single string representing the arm_resource_id of + a user-created storage account, or an entire + SystemCreatedStorageAccount object. + :type storage_account_details: Union[List[str], SystemCreatedStorageAccount] + """ + self.acr_config = acr_config + self.location = location + self.storage_config = storage_config + + @classmethod + def _from_rest_object(cls, rest_obj: RestRegistryRegionArmDetails) -> Optional["RegistryRegionDetails"]: + if not rest_obj: + return None + converted_acr_details = [] + if rest_obj.acr_details: + converted_acr_details = [SystemCreatedAcrAccount._from_rest_object(acr) for acr in rest_obj.acr_details] + storages: Optional[Union[List[str], SystemCreatedStorageAccount]] = [] + if rest_obj.storage_account_details: + storages = cls._storage_config_from_rest_object(rest_obj.storage_account_details) + + return RegistryRegionDetails( + acr_config=converted_acr_details, # type: ignore[arg-type] + location=rest_obj.location, + storage_config=storages, + ) + + def _to_rest_object(self) -> RestRegistryRegionArmDetails: + converted_acr_details = [] + if self.acr_config: + converted_acr_details = [SystemCreatedAcrAccount._to_rest_object(acr) for acr in self.acr_config] + storages = [] + if self.storage_config: + storages = self._storage_config_to_rest_object() + return RestRegistryRegionArmDetails( + acr_details=converted_acr_details, + location=self.location, + storage_account_details=storages, + ) + + def _storage_config_to_rest_object(self) -> List[RestStorageAccountDetails]: + storage = self.storage_config + # storage_config can either be a single system-created storage account, + # or list of user-inputted id's. + if ( + storage is not None + and not isinstance(storage, list) + and hasattr(storage, "storage_account_type") + and storage.storage_account_type is not None + ): + # We DO NOT want to set the arm_resource_id. The backend provides very + # unhelpful errors if you provide an empty/null/invalid resource ID, + # and ignores the value otherwise. It's better to avoid setting it in + # the conversion in this direction at all. + # We don't bother processing storage_account_type because the + # rest version is case insensitive. + account = RestStorageAccountDetails( + system_created_storage_account=RestSystemCreatedStorageAccount( + storage_account_hns_enabled=storage.storage_account_hns, + storage_account_type=storage.storage_account_type, + ) + ) + # duplicate this value based on the replication_count + count = storage.replication_count + if count < 1: + raise ValueError(f"Replication count cannot be less than 1. Value was: {count}.") + return [deepcopy(account) for _ in range(0, count)] + elif storage is not None and not isinstance(storage, SystemCreatedStorageAccount) and len(storage) > 0: + return [_make_rest_user_storage_from_id(user_id=user_id) for user_id in storage] + else: + return [] + + @classmethod + def _storage_config_from_rest_object( + cls, rest_configs: Optional[List] + ) -> Optional[Union[List[str], SystemCreatedStorageAccount]]: + if not rest_configs: + return None + num_configs = len(rest_configs) + if num_configs == 0: + return None + system_created_count = reduce( + # TODO: Bug Item number: 2883323 + lambda x, y: int(x) + int(y), # type: ignore + [ + hasattr(config, "system_created_storage_account") and config.system_created_storage_account is not None + for config in rest_configs + ], + ) + # configs should be mono-typed. Either they're all system created + # or all user created. + if system_created_count == num_configs: + # System created case - assume all elements are duplicates + # of a single storage configuration. + # Convert back into a single local representation by + # combining id's into a list, and using the first element's + # account type and hns. + first_config = rest_configs[0].system_created_storage_account + resource_id = None + if first_config.arm_resource_id: + resource_id = first_config.arm_resource_id.resource_id + # account for ids of duplicated if they exist + replicated_ids = None + if num_configs > 1: + replicated_ids = [ + config.system_created_storage_account.arm_resource_id.resource_id for config in rest_configs + ] + return SystemCreatedStorageAccount( + storage_account_hns=first_config.storage_account_hns_enabled, + storage_account_type=( + (StorageAccountType(first_config.storage_account_type.lower())) + if first_config.storage_account_type + else None + ), + arm_resource_id=resource_id, + replication_count=num_configs, + replicated_ids=replicated_ids, + ) + elif system_created_count == 0: + return [config.user_created_storage_account.arm_resource_id.resource_id for config in rest_configs] + else: + msg = f"""tried reading in a registry whose storage accounts were not + mono-managed or user-created. {system_created_count} out of {num_configs} were managed.""" + err = ValidationException( + message=msg, + target=ErrorTarget.REGISTRY, + no_personal_data_message=msg, + error_category=ErrorCategory.USER_ERROR, + error_type=ValidationErrorType.INVALID_VALUE, + ) + log_and_raise_error(err) + return None diff --git a/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/util.py b/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/util.py new file mode 100644 index 00000000..18f56169 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_registry/util.py @@ -0,0 +1,17 @@ +# --------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# --------------------------------------------------------- + +from azure.ai.ml._restclient.v2022_10_01_preview.models import ArmResourceId as RestArmResourceId +from azure.ai.ml._restclient.v2022_10_01_preview.models import StorageAccountDetails as RestStorageAccountDetails +from azure.ai.ml._restclient.v2022_10_01_preview.models import ( + UserCreatedStorageAccount as RestUserCreatedStorageAccount, +) + + +def _make_rest_user_storage_from_id(*, user_id: str) -> RestStorageAccountDetails: + return RestStorageAccountDetails( + user_created_storage_account=RestUserCreatedStorageAccount( + arm_resource_id=RestArmResourceId(resource_id=user_id) + ) + ) |
