azureml-registry-tools 0.1.0a2__tar.gz → 0.1.0a3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {azureml_registry_tools-0.1.0a2/azureml_registry_tools.egg-info → azureml_registry_tools-0.1.0a3}/PKG-INFO +2 -1
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/_rest_client/arm_client.py +3 -24
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/_rest_client/base_rest_client.py +63 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/_rest_client/registry_management_client.py +31 -26
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3/azureml_registry_tools.egg-info}/PKG-INFO +2 -1
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml_registry_tools.egg-info/requires.txt +1 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/setup.py +2 -1
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/LICENSE.txt +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/__init__.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/__init__.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/_cli/__init__.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/_cli/registry_syndication_cli.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/_cli/repo2registry_cli.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/_rest_client/__init__.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/mgmt/__init__.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/mgmt/create_manifest.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/mgmt/syndication_manifest.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/tools/__init__.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/tools/config.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/tools/create_or_update_assets.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/tools/registry_utils.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/tools/repo2registry_config.py +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml_registry_tools.egg-info/SOURCES.txt +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml_registry_tools.egg-info/dependency_links.txt +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml_registry_tools.egg-info/entry_points.txt +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml_registry_tools.egg-info/top_level.txt +0 -0
- {azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: azureml-registry-tools
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.0a3
|
4
4
|
Summary: AzureML Registry tools and CLI
|
5
5
|
Author: Microsoft Corp
|
6
6
|
License: https://aka.ms/azureml-sdk-license
|
@@ -8,6 +8,7 @@ Requires-Python: >=3.9,<3.14
|
|
8
8
|
License-File: LICENSE.txt
|
9
9
|
Requires-Dist: azure-identity<2.0
|
10
10
|
Requires-Dist: ruamel-yaml<0.19,>=0.17.21
|
11
|
+
Requires-Dist: diskcache~=5.6
|
11
12
|
Requires-Dist: azure-ai-ml<2.0
|
12
13
|
Requires-Dist: azureml-assets<2.0
|
13
14
|
Dynamic: author
|
@@ -1,16 +1,14 @@
|
|
1
1
|
# ---------------------------------------------------------
|
2
2
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
3
3
|
# ---------------------------------------------------------
|
4
|
-
from
|
5
|
-
from .base_rest_client import BaseRestClient
|
6
|
-
import time
|
4
|
+
from .base_rest_client import BaseAzureRestClient
|
7
5
|
from json.decoder import JSONDecodeError
|
8
6
|
|
9
7
|
DEFAULT_API_VERSION = "2025-04-01" # Default API version for Azure Resource Manager
|
10
8
|
|
11
9
|
|
12
|
-
class ArmClient(
|
13
|
-
"""Simple Azure Resource Manager (ARM) client leveraging
|
10
|
+
class ArmClient(BaseAzureRestClient):
|
11
|
+
"""Simple Azure Resource Manager (ARM) client leveraging BaseAzureRestClient for GET, PATCH, PUT, and DELETE operations.
|
14
12
|
|
15
13
|
Handles authentication via Bearer token (pass as api_key) and supports standard ARM resource operations.
|
16
14
|
"""
|
@@ -25,27 +23,8 @@ class ArmClient(BaseRestClient):
|
|
25
23
|
backoff_factor (int): Backoff factor for retries.
|
26
24
|
"""
|
27
25
|
base_url = "https://management.azure.com"
|
28
|
-
self._credential = None
|
29
|
-
self._token_expires_on = None
|
30
|
-
if api_key is None:
|
31
|
-
# Use DefaultAzureCredential for authentication if no API key is provided
|
32
|
-
self._credential = DefaultAzureCredential()
|
33
|
-
token = self._credential.get_token("https://management.azure.com/.default")
|
34
|
-
api_key = token.token
|
35
|
-
self._token_expires_on = token.expires_on
|
36
26
|
super().__init__(base_url, api_key=api_key, max_retries=max_retries, backoff_factor=backoff_factor)
|
37
27
|
|
38
|
-
def _refresh_api_key_if_needed(self) -> None:
|
39
|
-
"""Refresh the API key if using DefaultAzureCredential and the token is close to expiration."""
|
40
|
-
if self._credential is not None:
|
41
|
-
now = int(time.time())
|
42
|
-
# Refresh if less than 10 minutes (600 seconds) left
|
43
|
-
if not self._token_expires_on or self._token_expires_on - now < 600:
|
44
|
-
token = self._credential.get_token("https://management.azure.com/.default")
|
45
|
-
self.api_key = token.token
|
46
|
-
self.session.headers.update({'Authorization': f'Bearer {self.api_key}'})
|
47
|
-
self._token_expires_on = token.expires_on
|
48
|
-
|
49
28
|
def get_resource(self, resource_id, api_version=DEFAULT_API_VERSION, **kwargs) -> object:
|
50
29
|
"""
|
51
30
|
Get an ARM resource by its resource ID.
|
@@ -4,6 +4,11 @@
|
|
4
4
|
import requests
|
5
5
|
import time
|
6
6
|
import logging
|
7
|
+
from azure.identity import DefaultAzureCredential
|
8
|
+
from diskcache import Cache
|
9
|
+
import tempfile
|
10
|
+
|
11
|
+
ENABLE_CACHE = True # Set to False to disable diskcache usage for API key
|
7
12
|
|
8
13
|
|
9
14
|
class BaseRestClient:
|
@@ -136,3 +141,61 @@ class BaseRestClient:
|
|
136
141
|
requests.Response: The HTTP response object.
|
137
142
|
"""
|
138
143
|
return self._request_with_retry('DELETE', url, **kwargs)
|
144
|
+
|
145
|
+
|
146
|
+
class BaseAzureRestClient(BaseRestClient):
|
147
|
+
"""Base Azure REST client that handles Azure authentication and token refresh, with diskcache for API key."""
|
148
|
+
|
149
|
+
if ENABLE_CACHE:
|
150
|
+
_cache_dir = tempfile.gettempdir() + '/.azureml_registry_token_cache'
|
151
|
+
_cache = Cache(_cache_dir)
|
152
|
+
else:
|
153
|
+
_cache = None
|
154
|
+
|
155
|
+
def __init__(self, base_url: str, api_key: str = None, max_retries: int = 5, backoff_factor: int = 1):
|
156
|
+
"""
|
157
|
+
Initialize the BaseAzureRestClient.
|
158
|
+
|
159
|
+
Args:
|
160
|
+
base_url (str): The base URL for the REST API.
|
161
|
+
api_key (str, optional): Bearer token for authentication. If None, uses DefaultAzureCredential.
|
162
|
+
max_retries (int): Maximum number of retries for failed requests.
|
163
|
+
backoff_factor (int): Backoff factor for retry delays.
|
164
|
+
"""
|
165
|
+
self._credential = None
|
166
|
+
self._token_expires_on = None
|
167
|
+
cache_key = f"azureml_api_key_{base_url}"
|
168
|
+
cache_expiry_key = f"azureml_api_key_expiry_{base_url}"
|
169
|
+
cached_token = None
|
170
|
+
cached_expiry = None
|
171
|
+
now = int(time.time())
|
172
|
+
if ENABLE_CACHE and self._cache is not None:
|
173
|
+
cached_token = self._cache.get(cache_key)
|
174
|
+
cached_expiry = self._cache.get(cache_expiry_key)
|
175
|
+
if api_key is None and cached_token and cached_expiry and cached_expiry > now:
|
176
|
+
api_key = cached_token
|
177
|
+
self._token_expires_on = cached_expiry
|
178
|
+
super().__init__(base_url, api_key=api_key, max_retries=max_retries, backoff_factor=backoff_factor)
|
179
|
+
# Only after super().__init__ is self.session available
|
180
|
+
if api_key is None:
|
181
|
+
self._credential = DefaultAzureCredential()
|
182
|
+
self._refresh_api_key_if_needed()
|
183
|
+
# Ensure self.api_key is set for future use
|
184
|
+
api_key = self.api_key
|
185
|
+
|
186
|
+
def _refresh_api_key_if_needed(self) -> None:
|
187
|
+
if self._credential is None:
|
188
|
+
return
|
189
|
+
now = int(time.time())
|
190
|
+
if not getattr(self, '_token_expires_on', None) or self._token_expires_on - now < 600:
|
191
|
+
token = self._credential.get_token("https://management.azure.com/.default")
|
192
|
+
self.api_key = token.token
|
193
|
+
self.session.headers.update({'Authorization': f'Bearer {self.api_key}'})
|
194
|
+
self._token_expires_on = token.expires_on
|
195
|
+
# Store in diskcache if enabled
|
196
|
+
if ENABLE_CACHE and self._cache is not None:
|
197
|
+
base_url = getattr(self, 'base_url', 'default')
|
198
|
+
cache_key = f"azureml_api_key_{base_url}"
|
199
|
+
cache_expiry_key = f"azureml_api_key_expiry_{base_url}"
|
200
|
+
self._cache.set(cache_key, self.api_key, expire=(self._token_expires_on - now))
|
201
|
+
self._cache.set(cache_expiry_key, self._token_expires_on, expire=(self._token_expires_on - now))
|
@@ -1,18 +1,16 @@
|
|
1
1
|
# ---------------------------------------------------------
|
2
2
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
3
3
|
# ---------------------------------------------------------
|
4
|
-
from
|
5
|
-
from .base_rest_client import BaseRestClient
|
6
|
-
import time
|
4
|
+
from .base_rest_client import BaseAzureRestClient
|
7
5
|
|
8
6
|
|
9
|
-
class RegistryManagementClient(
|
7
|
+
class RegistryManagementClient(BaseAzureRestClient):
|
10
8
|
"""Python client for RegistrySyndicationManifestController (excluding S2S APIs).
|
11
9
|
|
12
10
|
Handles authentication, token refresh, and provides methods for manifest and registry management.
|
13
11
|
"""
|
14
12
|
|
15
|
-
def __init__(self, registry_name: str, primary_region: str =
|
13
|
+
def __init__(self, registry_name: str, primary_region: str = None, api_key: str = None, max_retries: int = 5, backoff_factor: int = 1) -> None:
|
16
14
|
"""
|
17
15
|
Initialize the RegistryManagementClient.
|
18
16
|
|
@@ -23,29 +21,26 @@ class RegistryManagementClient(BaseRestClient):
|
|
23
21
|
max_retries (int): Maximum number of retries for failed requests.
|
24
22
|
backoff_factor (int): Backoff factor for retry delays.
|
25
23
|
"""
|
24
|
+
if primary_region is None:
|
25
|
+
# Resolve the primary region if not provided
|
26
|
+
primary_region = self.resolve_registry_primary_region(registry_name)
|
26
27
|
base_url = f"https://{primary_region}.api.azureml.ms"
|
27
|
-
self._credential = None
|
28
|
-
self._token_expires_on = None
|
29
|
-
if api_key is None:
|
30
|
-
# Use DefaultAzureCredential for authentication if no API key is provided
|
31
|
-
self._credential = DefaultAzureCredential()
|
32
|
-
token = self._credential.get_token("https://management.azure.com/.default")
|
33
|
-
api_key = token.token
|
34
|
-
self._token_expires_on = token.expires_on
|
35
28
|
super().__init__(base_url, api_key=api_key, max_retries=max_retries, backoff_factor=backoff_factor)
|
36
29
|
self.registry_name = registry_name
|
37
30
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
31
|
+
@staticmethod
|
32
|
+
def resolve_registry_primary_region(registry_name: str) -> str:
|
33
|
+
"""
|
34
|
+
Resolve the primary region for the given registry name.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
registry_name (str): The name of the AzureML registry.
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
str: The primary region for the registry.
|
41
|
+
"""
|
42
|
+
discovery = RegistryManagementClient(registry_name, primary_region="eastus").discovery()
|
43
|
+
return discovery.get('primaryRegion', 'eastus')
|
49
44
|
|
50
45
|
def create_or_update_manifest(self, manifest_dto: dict) -> dict:
|
51
46
|
"""
|
@@ -110,5 +105,15 @@ class RegistryManagementClient(BaseRestClient):
|
|
110
105
|
"""
|
111
106
|
self._refresh_api_key_if_needed()
|
112
107
|
url = f"{self.base_url}/registrymanagement/v1.0/registries/{self.registry_name}/discovery"
|
113
|
-
|
114
|
-
|
108
|
+
try:
|
109
|
+
response = self.get(url)
|
110
|
+
return response.json()
|
111
|
+
except Exception as ex:
|
112
|
+
# Special handling for HTTP errors with status_code attribute
|
113
|
+
if hasattr(ex, 'response') and ex.response is not None:
|
114
|
+
status_code = ex.response.status_code
|
115
|
+
if status_code == 403:
|
116
|
+
raise RuntimeError(f"Received 403 Forbidden. This may indicate that the registry '{self.registry_name}' does not exist or you do not have access.")
|
117
|
+
else:
|
118
|
+
raise RuntimeError(f"Failed to get discovery information: {status_code} {ex.response.text}")
|
119
|
+
raise RuntimeError(f"Error occurred while trying to get discovery information: {ex}")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: azureml-registry-tools
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.0a3
|
4
4
|
Summary: AzureML Registry tools and CLI
|
5
5
|
Author: Microsoft Corp
|
6
6
|
License: https://aka.ms/azureml-sdk-license
|
@@ -8,6 +8,7 @@ Requires-Python: >=3.9,<3.14
|
|
8
8
|
License-File: LICENSE.txt
|
9
9
|
Requires-Dist: azure-identity<2.0
|
10
10
|
Requires-Dist: ruamel-yaml<0.19,>=0.17.21
|
11
|
+
Requires-Dist: diskcache~=5.6
|
11
12
|
Requires-Dist: azure-ai-ml<2.0
|
12
13
|
Requires-Dist: azureml-assets<2.0
|
13
14
|
Dynamic: author
|
@@ -9,6 +9,7 @@ from setuptools import setup, find_packages
|
|
9
9
|
DEPENDENCIES = [
|
10
10
|
"azure-identity<2.0",
|
11
11
|
"ruamel-yaml>=0.17.21,<0.19",
|
12
|
+
"diskcache~=5.6",
|
12
13
|
"azure-ai-ml<2.0",
|
13
14
|
"azureml-assets<2.0"
|
14
15
|
]
|
@@ -17,7 +18,7 @@ exclude_list = ["*.tests"]
|
|
17
18
|
|
18
19
|
setup(
|
19
20
|
name='azureml-registry-tools',
|
20
|
-
version="0.1.
|
21
|
+
version="0.1.0a3",
|
21
22
|
description='AzureML Registry tools and CLI',
|
22
23
|
author='Microsoft Corp',
|
23
24
|
license="https://aka.ms/azureml-sdk-license",
|
File without changes
|
File without changes
|
{azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/__init__.py
RENAMED
File without changes
|
{azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/_cli/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/mgmt/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/tools/__init__.py
RENAMED
File without changes
|
{azureml_registry_tools-0.1.0a2 → azureml_registry_tools-0.1.0a3}/azureml/registry/tools/config.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|