Skip to content

Commit 29fb141

Browse files
brownma-msronaldshaw-worksingankit
authored
Adding cloud configuration via CLI keyword args (Azure#28780)
* Adding cloud configuration via CLI keyword args * Fixing a couple comments * updated sdk changes to accommodate the cli differences and changed function to throw exception instead of returning false * fixed test and lint error * updated tests based on PR suggestions. Fixed lint errors. Removed changelog updates * Some last minute fixes * Small test fix * Update CHANGELOG.md * retrigger checks * Updating some formatting for the Black test * Updating some formatting for the Black test again --------- Co-authored-by: Ronald Shaw <[email protected]> Co-authored-by: Ankit Singhal <[email protected]>
1 parent 54d158a commit 29fb141

File tree

5 files changed

+272
-27
lines changed

5 files changed

+272
-27
lines changed

sdk/ml/azure-ai-ml/CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
## 1.5.0 (Unreleased)
44

55
### Features Added
6-
76
- Added support for `tags` on Compute Resources.
87

98
### Bugs Fixed

sdk/ml/azure-ai-ml/azure/ai/ml/_azure_environments.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ class EndpointURLS: # pylint: disable=too-few-public-methods,no-init
3333
REGISTRY_DISCOVERY_ENDPOINT = "registry_discovery_endpoint"
3434

3535

36+
class CloudArgumentKeys:
37+
CLOUD_METADATA = "cloud_metadata"
38+
39+
3640
_environments = {
3741
AzureEnvironments.ENV_DEFAULT: {
3842
EndpointURLS.AZURE_PORTAL_ENDPOINT: "https://portal.azure.com/",
@@ -60,8 +64,6 @@ class EndpointURLS: # pylint: disable=too-few-public-methods,no-init
6064
},
6165
}
6266

63-
_requests_pipeline = None
64-
6567

6668
def _get_cloud(cloud: str):
6769
if cloud in _environments:
@@ -120,7 +122,6 @@ def _get_base_url_from_metadata(cloud_name: Optional[str] = None, is_local_mfe:
120122
base_url = None
121123
if is_local_mfe:
122124
base_url = _get_mfe_url_override()
123-
124125
if base_url is None:
125126
cloud_details = _get_cloud_details(cloud_name)
126127
base_url = cloud_details.get(EndpointURLS.RESOURCE_MANAGER_ENDPOINT).strip("/")
@@ -224,7 +225,8 @@ def _get_registry_discovery_url(cloud, cloud_suffix=""):
224225
return _environments[cloud_name].registry_url
225226

226227
registry_discovery_region = os.environ.get(
227-
ArmConstants.REGISTRY_DISCOVERY_REGION_ENV_NAME, ArmConstants.REGISTRY_DISCOVERY_DEFAULT_REGION
228+
ArmConstants.REGISTRY_DISCOVERY_REGION_ENV_NAME,
229+
ArmConstants.REGISTRY_DISCOVERY_DEFAULT_REGION,
228230
)
229231
registry_discovery_region_default = "https://{}{}.api.azureml.{}/".format(
230232
cloud_name.lower(), registry_discovery_region, cloud_suffix
@@ -244,7 +246,10 @@ def _get_clouds_by_metadata_url(metadata_url):
244246
with client.send_request(HttpRequest("GET", metadata_url)) as meta_response:
245247
arm_cloud_dict = meta_response.json()
246248
cli_cloud_dict = _convert_arm_to_cli(arm_cloud_dict)
247-
module_logger.debug("Finish : Loading cloud metadata from the url specified by %s", metadata_url)
249+
module_logger.debug(
250+
"Finish : Loading cloud metadata from the url specified by %s",
251+
metadata_url,
252+
)
248253
return cli_cloud_dict
249254
except Exception as ex: # pylint: disable=broad-except
250255
module_logger.warning(
@@ -283,3 +288,20 @@ def _convert_arm_to_cli(arm_cloud_metadata):
283288
module_logger.warning("Property on cloud not found in arm cloud metadata: %s", ex)
284289
continue
285290
return cli_cloud_metadata_dict
291+
292+
293+
def _add_cloud_to_environments(kwargs):
294+
cloud_name = kwargs["cloud"]
295+
if cloud_name in _environments:
296+
raise AttributeError(f"Cannot overwrite existing cloud: {cloud_name}")
297+
cloud_metadata = kwargs[CloudArgumentKeys.CLOUD_METADATA]
298+
if cloud_metadata is None:
299+
raise LookupError(f"{CloudArgumentKeys.CLOUD_METADATA} not present in kwargs, no environment to add!")
300+
_environments[kwargs["cloud"]] = {
301+
EndpointURLS.AZURE_PORTAL_ENDPOINT: cloud_metadata[EndpointURLS.AZURE_PORTAL_ENDPOINT],
302+
EndpointURLS.RESOURCE_MANAGER_ENDPOINT: cloud_metadata[EndpointURLS.RESOURCE_MANAGER_ENDPOINT],
303+
EndpointURLS.ACTIVE_DIRECTORY_ENDPOINT: cloud_metadata[EndpointURLS.ACTIVE_DIRECTORY_ENDPOINT],
304+
EndpointURLS.AML_RESOURCE_ID: cloud_metadata[EndpointURLS.AML_RESOURCE_ID],
305+
EndpointURLS.STORAGE_ENDPOINT: cloud_metadata[EndpointURLS.STORAGE_ENDPOINT],
306+
EndpointURLS.REGISTRY_DISCOVERY_ENDPOINT: cloud_metadata[EndpointURLS.REGISTRY_DISCOVERY_ENDPOINT],
307+
}

sdk/ml/azure-ai-ml/azure/ai/ml/_ml_client.py

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,47 @@
1616
from azure.core.polling import LROPoller
1717

1818
from azure.ai.ml._azure_environments import (
19+
CloudArgumentKeys,
1920
_get_base_url_from_metadata,
2021
_get_cloud_information_from_metadata,
2122
_get_default_cloud_name,
2223
_get_registry_discovery_endpoint_from_metadata,
2324
_set_cloud,
25+
_add_cloud_to_environments,
2426
)
2527
from azure.ai.ml._file_utils.file_utils import traverse_up_path_and_find_file
26-
from azure.ai.ml._restclient.registry_discovery import AzureMachineLearningWorkspaces as ServiceClientRegistryDiscovery
28+
from azure.ai.ml._restclient.registry_discovery import (
29+
AzureMachineLearningWorkspaces as ServiceClientRegistryDiscovery,
30+
)
2731
from azure.ai.ml._restclient.v2020_09_01_dataplanepreview import (
2832
AzureMachineLearningWorkspaces as ServiceClient092020DataplanePreview,
2933
)
30-
from azure.ai.ml._restclient.v2022_01_01_preview import AzureMachineLearningWorkspaces as ServiceClient012022Preview
31-
from azure.ai.ml._restclient.v2022_02_01_preview import AzureMachineLearningWorkspaces as ServiceClient022022Preview
32-
from azure.ai.ml._restclient.v2022_05_01 import AzureMachineLearningWorkspaces as ServiceClient052022
33-
from azure.ai.ml._restclient.v2022_10_01 import AzureMachineLearningWorkspaces as ServiceClient102022
34-
from azure.ai.ml._restclient.v2022_10_01_preview import AzureMachineLearningWorkspaces as ServiceClient102022Preview
35-
from azure.ai.ml._restclient.v2022_12_01_preview import AzureMachineLearningWorkspaces as ServiceClient122022Preview
36-
from azure.ai.ml._restclient.v2023_02_01_preview import AzureMachineLearningWorkspaces as ServiceClient022023Preview
37-
from azure.ai.ml._scope_dependent_operations import OperationConfig, OperationsContainer, OperationScope
34+
from azure.ai.ml._restclient.v2022_01_01_preview import (
35+
AzureMachineLearningWorkspaces as ServiceClient012022Preview,
36+
)
37+
from azure.ai.ml._restclient.v2022_02_01_preview import (
38+
AzureMachineLearningWorkspaces as ServiceClient022022Preview,
39+
)
40+
from azure.ai.ml._restclient.v2022_05_01 import (
41+
AzureMachineLearningWorkspaces as ServiceClient052022,
42+
)
43+
from azure.ai.ml._restclient.v2022_10_01 import (
44+
AzureMachineLearningWorkspaces as ServiceClient102022,
45+
)
46+
from azure.ai.ml._restclient.v2022_10_01_preview import (
47+
AzureMachineLearningWorkspaces as ServiceClient102022Preview,
48+
)
49+
from azure.ai.ml._restclient.v2022_12_01_preview import (
50+
AzureMachineLearningWorkspaces as ServiceClient122022Preview,
51+
)
52+
from azure.ai.ml._restclient.v2023_02_01_preview import (
53+
AzureMachineLearningWorkspaces as ServiceClient022023Preview,
54+
)
55+
from azure.ai.ml._scope_dependent_operations import (
56+
OperationConfig,
57+
OperationsContainer,
58+
OperationScope,
59+
)
3860

3961
# from azure.ai.ml._telemetry.logging_handler import get_appinsights_log_handler
4062
from azure.ai.ml._user_agent import USER_AGENT
@@ -166,7 +188,21 @@ def __init__(
166188
show_progress = kwargs.pop("show_progress", True)
167189
self._operation_config = OperationConfig(show_progress=show_progress)
168190

169-
cloud_name = kwargs.get("cloud", _get_default_cloud_name())
191+
if "cloud" in kwargs:
192+
cloud_name = kwargs["cloud"]
193+
if CloudArgumentKeys.CLOUD_METADATA in kwargs:
194+
try:
195+
_add_cloud_to_environments(kwargs)
196+
except AttributeError as e:
197+
module_logger.debug("Cloud already exists: %s", e)
198+
except LookupError as e:
199+
module_logger.debug("Missing keyword: %s", e)
200+
else:
201+
module_logger.debug("%s key not found in kwargs", CloudArgumentKeys.CLOUD_METADATA)
202+
else:
203+
module_logger.debug("cloud key not found in kwargs")
204+
cloud_name = _get_default_cloud_name()
205+
170206
self._cloud = cloud_name
171207
_set_cloud(cloud_name)
172208
if "cloud" not in kwargs:
@@ -187,7 +223,10 @@ def __init__(
187223
credential=self._credential, base_url=base_url, **kwargs_registry
188224
)
189225
registry_discovery = RegistryDiscovery(
190-
self._credential, registry_name, self._service_client_registry_discovery_client, **kwargs_registry
226+
self._credential,
227+
registry_name,
228+
self._service_client_registry_discovery_client,
229+
**kwargs_registry,
191230
)
192231
self._service_client_10_2021_dataplanepreview = registry_discovery.get_registry_service_client()
193232
subscription_id = registry_discovery.subscription_id
@@ -802,7 +841,15 @@ def create_or_update(
802841
# R = valid inputs/outputs for begin_create_or_update
803842
# Each entry here requires a registered _begin_create_or_update function below
804843
R = TypeVar(
805-
"R", Workspace, Registry, Compute, OnlineDeployment, OnlineEndpoint, BatchDeployment, BatchEndpoint, JobSchedule
844+
"R",
845+
Workspace,
846+
Registry,
847+
Compute,
848+
OnlineDeployment,
849+
OnlineEndpoint,
850+
BatchDeployment,
851+
BatchEndpoint,
852+
JobSchedule,
806853
)
807854

808855
def begin_create_or_update(
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# coding: utf-8
2+
3+
# -------------------------------------------------------------------------
4+
# Copyright (c) Microsoft Corporation. All rights reserved.
5+
# Licensed under the MIT License. See License.txt in the project root for
6+
# license information.
7+
# --------------------------------------------------------------------------
8+
9+
"""
10+
FILE: ml_samples_cloud_configurations.py
11+
DESCRIPTION:
12+
These samples demonstrate different ways to configure clouds for the MLClient.
13+
USAGE:
14+
python ml_samples_cloud_configurations.py
15+
16+
"""
17+
18+
import os
19+
from azure.ai.ml import MLClient
20+
from azure.ai.ml.constants._common import ArmConstants
21+
from azure.ai.ml.constants._common import AZUREML_CLOUD_ENV_NAME
22+
from azure.identity import AzureAuthorityHosts, DefaultAzureCredential
23+
24+
25+
class CloudConfigurationOptions(object):
26+
def ml_cloud_config_from_environment_arm(self):
27+
subscription_id = "AZURE_SUBSCRIPTION_ID"
28+
resource_group = "RESOURCE_GROUP_NAME"
29+
credential = DefaultAzureCredential(authority=AzureAuthorityHosts.AZURE_PUBLIC_CLOUD)
30+
31+
# This environment variable should be set when running python. If it is set, and the configured cloud is not
32+
# one of the default ones in _azure_environments.py, it will follow the url to find the cloud config. If it
33+
# does not find it anywhere, it will throw an "Unknown cloud environment" error.
34+
os.environ[
35+
ArmConstants.METADATA_URL_ENV_NAME
36+
] = "https://management.azure.com/metadata/endpoints?api-version=2019-05-01"
37+
kwargs = {"cloud": "AzureCloud"}
38+
39+
ml_client = MLClient(
40+
credential=credential,
41+
subscription_id=subscription_id,
42+
resource_group_name=resource_group,
43+
workspace_name="test-ws1",
44+
**kwargs,
45+
)
46+
# The client will use the cloud that we passed in
47+
print("Client is using cloud:", ml_client._cloud)
48+
# In specifying this cloud, we've also set the environment variable to match it
49+
print("Cloud name environment variable:", os.environ[AZUREML_CLOUD_ENV_NAME])
50+
51+
def ml_cloud_config_from_keyword_args(self):
52+
subscription_id = "AZURE_SUBSCRIPTION_ID"
53+
resource_group = "RESOURCE_GROUP_NAME"
54+
credential = DefaultAzureCredential(authority=AzureAuthorityHosts.AZURE_PUBLIC_CLOUD)
55+
56+
# All of these configurations are needed in the kwargs, otherwise the SDK will not recognize the configuration
57+
# and will throw an "Unknown cloud environment" error.
58+
kwargs = {
59+
"cloud": "TestCloud",
60+
"azure_portal": "https://test.portal.azure.com/",
61+
"resource_manager": "https://test.management.azure.com/",
62+
"active_directory": "https://test.login.microsoftonline.com/",
63+
"aml_resource_id": "https://test.ml.azure.com/",
64+
"storage_endpoint": "test.core.windows.net",
65+
"registry_discovery_endpoint": "https://test.eastus.api.azureml.ms/",
66+
}
67+
ml_client = MLClient(
68+
credential=credential,
69+
subscription_id=subscription_id,
70+
resource_group_name=resource_group,
71+
workspace_name="test-ws1",
72+
**kwargs,
73+
)
74+
# The client will use the cloud that we passed in
75+
print("Client is using cloud:", ml_client._cloud)
76+
# In configuring this cloud, we've also set the environment variable to match it
77+
print("Cloud name environment variable:", os.environ[AZUREML_CLOUD_ENV_NAME])
78+
79+
80+
if __name__ == "__main__":
81+
sample = CloudConfigurationOptions()
82+
sample.ml_cloud_config_from_environment_arm()
83+
sample.ml_cloud_config_from_keyword_args()

0 commit comments

Comments
 (0)