zenml-nightly 0.73.0.dev20250131__py3-none-any.whl → 0.73.0.dev20250202__py3-none-any.whl

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.
zenml/VERSION CHANGED
@@ -1 +1 @@
1
- 0.73.0.dev20250131
1
+ 0.73.0.dev20250202
@@ -64,7 +64,10 @@ class AzureArtifactStore(BaseArtifactStore, AuthenticationMixin):
64
64
  """
65
65
  connector = self.get_connector()
66
66
  if connector:
67
- from azure.identity import ClientSecretCredential
67
+ from azure.identity import (
68
+ ClientSecretCredential,
69
+ DefaultAzureCredential,
70
+ )
68
71
  from azure.storage.blob import BlobServiceClient
69
72
 
70
73
  client = connector.connect()
@@ -77,18 +80,24 @@ class AzureArtifactStore(BaseArtifactStore, AuthenticationMixin):
77
80
  )
78
81
  # Get the credentials from the client
79
82
  credentials = client.credential
80
- if not isinstance(credentials, ClientSecretCredential):
83
+
84
+ if isinstance(credentials, ClientSecretCredential):
85
+ return AzureSecretSchema(
86
+ client_id=credentials._client_id,
87
+ client_secret=credentials._client_credential,
88
+ tenant_id=credentials._tenant_id,
89
+ account_name=client.account_name,
90
+ )
91
+
92
+ elif isinstance(credentials, DefaultAzureCredential):
93
+ return AzureSecretSchema(account_name=client.account_name)
94
+
95
+ else:
81
96
  raise RuntimeError(
82
97
  "The Azure Artifact Store connector can only be used "
83
98
  "with a service connector that is configured with "
84
- "Azure service principal credentials."
99
+ "Azure service principal credentials or implicit authentication"
85
100
  )
86
- return AzureSecretSchema(
87
- client_id=credentials._client_id,
88
- client_secret=credentials._client_credential,
89
- tenant_id=credentials._tenant_id,
90
- account_name=client.account_name,
91
- )
92
101
 
93
102
  secret = self.get_typed_authentication_secret(
94
103
  expected_schema_type=AzureSecretSchema
@@ -14,12 +14,14 @@
14
14
  """Azure Service Connector."""
15
15
 
16
16
  import datetime
17
+ import json
17
18
  import logging
18
19
  import re
19
20
  import subprocess
20
21
  from typing import Any, Dict, List, Optional, Tuple
21
22
  from uuid import UUID
22
23
 
24
+ import requests
23
25
  import yaml
24
26
  from azure.core.credentials import AccessToken, TokenCredential
25
27
  from azure.core.exceptions import AzureError
@@ -69,6 +71,7 @@ logger = get_logger(__name__)
69
71
  AZURE_MANAGEMENT_TOKEN_SCOPE = "https://management.azure.com/.default"
70
72
  AZURE_SESSION_TOKEN_DEFAULT_EXPIRATION_TIME = 60 * 60 # 1 hour
71
73
  AZURE_SESSION_EXPIRATION_BUFFER = 15 # 15 minutes
74
+ AZURE_ACR_OAUTH_SCOPE = "repository:*:*"
72
75
 
73
76
 
74
77
  class AzureBaseConfig(AuthenticationConfig):
@@ -362,8 +365,8 @@ neither a storage account nor a resource group is configured in the connector,
362
365
  all blob storage containers in all accessible storage accounts will be
363
366
  accessible.
364
367
 
365
- The only Azure authentication method that works with Azure blob storage
366
- resources is the service principal authentication method.
368
+ The only Azure authentication methods that work with Azure blob storage resources are the implicit
369
+ authentication and the service principal authentication method.
367
370
  """,
368
371
  auth_methods=AzureAuthenticationMethods.values(),
369
372
  # Request a blob container to be configured in the
@@ -435,12 +438,10 @@ following formats:
435
438
  If a resource group is configured in the connector, only ACR registries in that
436
439
  resource group will be accessible.
437
440
 
438
- If an authentication method other than the Azure service principal is used for
439
- authentication, the admin account must be enabled for the registry, otherwise
440
- clients will not be able to authenticate to the registry. See the official Azure
441
- [documentation on the admin account](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-authentication#admin-account
442
- )
443
- for more information.
441
+ If an authentication method other than the Azure service principal is used, Entra ID authentication is used.
442
+ This requires the configured identity to have the `AcrPush` role to be configured.
443
+ If this fails, admin account authentication is tried. For this the admin account must be enabled for the registry.
444
+ See the official Azure[documentation on the admin account](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-authentication#admin-account) for more information.
444
445
  """,
445
446
  auth_methods=AzureAuthenticationMethods.values(),
446
447
  supports_instances=True,
@@ -1718,10 +1719,14 @@ class AzureServiceConnector(ServiceConnector):
1718
1719
  resource_type=resource_type,
1719
1720
  resource_id=resource_id,
1720
1721
  )
1721
- resource_group: Optional[str] = None
1722
+
1723
+ resource_group: Optional[str]
1724
+ registry_name: str
1725
+ cluster_name: str
1722
1726
 
1723
1727
  if resource_type == DOCKER_REGISTRY_RESOURCE_TYPE:
1724
1728
  registry_name = self._parse_acr_resource_id(resource_id)
1729
+ registry_domain = f"{registry_name}.azurecr.io"
1725
1730
 
1726
1731
  # If a service principal is used for authentication, the client ID
1727
1732
  # and client secret can be used to authenticate to the registry, if
@@ -1733,13 +1738,12 @@ class AzureServiceConnector(ServiceConnector):
1733
1738
  ):
1734
1739
  assert isinstance(self.config, AzureServicePrincipalConfig)
1735
1740
  username = str(self.config.client_id)
1736
- password = self.config.client_secret
1741
+ password = str(self.config.client_secret)
1737
1742
 
1738
- # Without a service principal, this only works if the admin account
1739
- # is enabled for the registry, but this is not recommended and
1740
- # disabled by default.
1743
+ # Without a service principal, we try to use the AzureDefaultCredentials to authenticate against the ACR.
1744
+ # If this fails, we try to use the admin account.
1745
+ # This has to be enabled for the registry, but this is not recommended and disabled by default.
1741
1746
  # https://docs.microsoft.com/en-us/azure/container-registry/container-registry-authentication#admin-account
1742
-
1743
1747
  else:
1744
1748
  registries = self._list_acr_registries(
1745
1749
  credential,
@@ -1748,22 +1752,35 @@ class AzureServiceConnector(ServiceConnector):
1748
1752
  registry_name, resource_group = registries.popitem()
1749
1753
 
1750
1754
  try:
1751
- client = ContainerRegistryManagementClient(
1752
- credential, subscription_id
1755
+ username = "00000000-0000-0000-0000-000000000000"
1756
+ password = _ACRTokenExchangeClient(
1757
+ credential
1758
+ ).get_acr_access_token(
1759
+ registry_domain, AZURE_ACR_OAUTH_SCOPE
1753
1760
  )
1754
-
1755
- registry_credentials = client.registries.list_credentials(
1756
- resource_group, registry_name
1761
+ except AuthorizationException:
1762
+ logger.warning(
1763
+ "Falling back to admin credentials for ACR authentication. Be sure to assign AcrPush role to the configured identity."
1757
1764
  )
1758
- username = registry_credentials.username
1759
- password = registry_credentials.passwords[0].value
1760
- except AzureError as e:
1761
- raise AuthorizationException(
1762
- f"failed to list admin credentials for Azure Container "
1763
- f"Registry '{registry_name}' in resource group "
1764
- f"'{resource_group}'. Make sure the registry is "
1765
- f"configured with an admin account: {e}"
1766
- ) from e
1765
+ try:
1766
+ client = ContainerRegistryManagementClient(
1767
+ credential, subscription_id
1768
+ )
1769
+
1770
+ registry_credentials = (
1771
+ client.registries.list_credentials(
1772
+ resource_group, registry_name
1773
+ )
1774
+ )
1775
+ username = registry_credentials.username
1776
+ password = registry_credentials.passwords[0].value
1777
+ except AzureError as e:
1778
+ raise AuthorizationException(
1779
+ f"failed to list admin credentials for Azure Container "
1780
+ f"Registry '{registry_name}' in resource group "
1781
+ f"'{resource_group}'. Make sure the registry is "
1782
+ f"configured with an admin account: {e}"
1783
+ ) from e
1767
1784
 
1768
1785
  # Create a client-side Docker connector instance with the temporary
1769
1786
  # Docker credentials
@@ -1775,7 +1792,7 @@ class AzureServiceConnector(ServiceConnector):
1775
1792
  config=DockerConfiguration(
1776
1793
  username=username,
1777
1794
  password=password,
1778
- registry=f"{registry_name}.azurecr.io",
1795
+ registry=registry_domain,
1779
1796
  ),
1780
1797
  expires_at=expires_at,
1781
1798
  )
@@ -1847,3 +1864,103 @@ class AzureServiceConnector(ServiceConnector):
1847
1864
  )
1848
1865
 
1849
1866
  raise ValueError(f"Unsupported resource type: {resource_type}")
1867
+
1868
+
1869
+ class _ACRTokenExchangeClient:
1870
+ def __init__(self, credential: TokenCredential):
1871
+ self._credential = credential
1872
+
1873
+ def _get_aad_access_token(self) -> str:
1874
+ aad_access_token: str = self._credential.get_token(
1875
+ AZURE_MANAGEMENT_TOKEN_SCOPE
1876
+ ).token
1877
+ return aad_access_token
1878
+
1879
+ # https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#authenticating-docker-with-an-acr-refresh-token
1880
+ def get_acr_refresh_token(self, acr_url: str) -> str:
1881
+ try:
1882
+ aad_access_token = self._get_aad_access_token()
1883
+
1884
+ headers = {
1885
+ "Content-Type": "application/x-www-form-urlencoded",
1886
+ }
1887
+
1888
+ data = {
1889
+ "grant_type": "access_token",
1890
+ "service": acr_url,
1891
+ "access_token": aad_access_token,
1892
+ }
1893
+
1894
+ response = requests.post(
1895
+ f"https://{acr_url}/oauth2/exchange",
1896
+ headers=headers,
1897
+ data=data,
1898
+ timeout=5,
1899
+ )
1900
+
1901
+ if response.status_code != 200:
1902
+ raise AuthorizationException(
1903
+ f"failed to get refresh token for Azure Container "
1904
+ f"Registry '{acr_url}' in resource group. "
1905
+ f"Be sure to assign AcrPush to the configured principal. "
1906
+ f"The token exchange returned status {response.status_code} "
1907
+ f"with body '{response.content.decode()}'"
1908
+ )
1909
+
1910
+ acr_refresh_token_response = json.loads(response.content)
1911
+ acr_refresh_token: str = acr_refresh_token_response[
1912
+ "refresh_token"
1913
+ ]
1914
+ return acr_refresh_token
1915
+
1916
+ except (AzureError, requests.exceptions.RequestException) as e:
1917
+ raise AuthorizationException(
1918
+ f"failed to get refresh token for Azure Container "
1919
+ f"Registry '{acr_url}' in resource group. "
1920
+ f"Make sure the implicit authentication identity "
1921
+ f"has access to the configured registry: {e}"
1922
+ ) from e
1923
+
1924
+ # https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#calling-post-oauth2token-to-get-an-acr-access-token
1925
+ def get_acr_access_token(self, acr_url: str, scope: str) -> str:
1926
+ acr_refresh_token = self.get_acr_refresh_token(acr_url)
1927
+
1928
+ headers = {
1929
+ "Content-Type": "application/x-www-form-urlencoded",
1930
+ }
1931
+
1932
+ data = {
1933
+ "grant_type": "refresh_token",
1934
+ "service": acr_url,
1935
+ "scope": scope,
1936
+ "refresh_token": acr_refresh_token,
1937
+ }
1938
+
1939
+ try:
1940
+ response = requests.post(
1941
+ f"https://{acr_url}/oauth2/token",
1942
+ headers=headers,
1943
+ data=data,
1944
+ timeout=5,
1945
+ )
1946
+
1947
+ if response.status_code != 200:
1948
+ raise AuthorizationException(
1949
+ f"failed to get access token for Azure Container "
1950
+ f"Registry '{acr_url}' in resource group. "
1951
+ f"Be sure to assign AcrPush to the configured principal. "
1952
+ f"The token exchange returned status {response.status_code} "
1953
+ f"with body '{response.content.decode()}'"
1954
+ )
1955
+
1956
+ acr_access_token_response = json.loads(response.content)
1957
+ acr_access_token: str = acr_access_token_response["access_token"]
1958
+ return acr_access_token
1959
+
1960
+ except (AzureError, requests.exceptions.RequestException) as e:
1961
+ raise AuthorizationException(
1962
+ f"failed to get access token for Azure Container "
1963
+ f"Registry '{acr_url}' in resource group. "
1964
+ f"Make sure the implicit authentication identity "
1965
+ f"has access to the configured registry: {e}"
1966
+ ) from e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: zenml-nightly
3
- Version: 0.73.0.dev20250131
3
+ Version: 0.73.0.dev20250202
4
4
  Summary: ZenML: Write production-ready ML code.
5
5
  License: Apache-2.0
6
6
  Keywords: machine learning,production,pipeline,mlops,devops
@@ -104,6 +104,7 @@ Requires-Dist: python-dateutil (>=2.8.1,<3.0.0)
104
104
  Requires-Dist: python-multipart (>=0.0.9,<0.1.0) ; extra == "server"
105
105
  Requires-Dist: pyyaml (>=6.0.1)
106
106
  Requires-Dist: pyyaml-include (<2.0) ; extra == "templates"
107
+ Requires-Dist: requests (>=2.27.11,<3.0.0) ; extra == "connectors-azure"
107
108
  Requires-Dist: rich[jupyter] (>=12.0.0)
108
109
  Requires-Dist: ruff (>=0.1.7) ; extra == "templates" or extra == "dev"
109
110
  Requires-Dist: s3fs (>=2022.11.0) ; extra == "s3fs"
@@ -1,5 +1,5 @@
1
1
  zenml/README.md,sha256=827dekbOWAs1BpW7VF1a4d7EbwPbjwccX-2zdXBENZo,1777
2
- zenml/VERSION,sha256=yZ41gV2RoySfmEaBFCUmGXHDN81pnq8bzattD9wYAn4,19
2
+ zenml/VERSION,sha256=ef4hsdDc48pq_C2Kwy8-XusZWsxndp0BVpbW9Goz1sA,19
3
3
  zenml/__init__.py,sha256=SkMObQA41ajqdZqGErN00S1Vf3KAxpLvbZ-OBy5uYoo,2130
4
4
  zenml/actions/__init__.py,sha256=mrt6wPo73iKRxK754_NqsGyJ3buW7RnVeIGXr1xEw8Y,681
5
5
  zenml/actions/base_action.py,sha256=UcaHev6BTuLDwuswnyaPjdA8AgUqB5xPZ-lRtuvf2FU,25553
@@ -155,7 +155,7 @@ zenml/integrations/aws/step_operators/sagemaker_step_operator.py,sha256=-y1zqfID
155
155
  zenml/integrations/aws/step_operators/sagemaker_step_operator_entrypoint_config.py,sha256=2cXroe6-bXyHVkO5yPnZagNpqx6MrMDcvuvNr8J2j-A,1581
156
156
  zenml/integrations/azure/__init__.py,sha256=99cefcfBlEXl5lYCVUzWN0uvYn_TFGpBsEATvn1d3c4,2938
157
157
  zenml/integrations/azure/artifact_stores/__init__.py,sha256=dlIwbpgjE0Hy4rhMbelNJHVKm4t8tj_hRu9mQ_cEIAg,820
158
- zenml/integrations/azure/artifact_stores/azure_artifact_store.py,sha256=uVMpsWDIyeytn8aHxBAma5pdPwiWuK5fwew2Tvxsk3w,11955
158
+ zenml/integrations/azure/artifact_stores/azure_artifact_store.py,sha256=wgXoZFdK1tT4XneOJTOr3uYd5e6dS5ruyiNo8xldCDk,12236
159
159
  zenml/integrations/azure/azureml_utils.py,sha256=kl_ld7jgWQgVBmagO46ADvfIKvBK3qaGboZom2C5UIA,7561
160
160
  zenml/integrations/azure/flavors/__init__.py,sha256=CRdRbAnd28j2SFETYz-6PlWwhyTzr3dMwW9-LQL2G7w,1411
161
161
  zenml/integrations/azure/flavors/azure_artifact_store_flavor.py,sha256=b3gwcqsY1sYI8MevIyz5pu5THo5aZHgmz8MCqiOfQ7c,3589
@@ -166,7 +166,7 @@ zenml/integrations/azure/orchestrators/__init__.py,sha256=q4rBPIJHcuUr6dLUBdrTkQ
166
166
  zenml/integrations/azure/orchestrators/azureml_orchestrator.py,sha256=mkbjg9j-s1e2G191Q0dy4LKOeVeXV4pYr1qTuxIbMFQ,19912
167
167
  zenml/integrations/azure/orchestrators/azureml_orchestrator_entrypoint_config.py,sha256=lacOyorsHa-HuD_kxN9M6BUiZDvs5jL9AnJWwrFCtp4,3104
168
168
  zenml/integrations/azure/service_connectors/__init__.py,sha256=yMz6bTCtIZqZwfEM6h7-PSWsd_DB8l0OD9z_bdzomZw,793
169
- zenml/integrations/azure/service_connectors/azure_service_connector.py,sha256=oJItaOh0GQX0UAQG7jVMHBb9T_LXMCjG8bzfGkB_JRc,76370
169
+ zenml/integrations/azure/service_connectors/azure_service_connector.py,sha256=8kAnH6NK9J6SfJPPxlmxIc2oH73YtDI3qElgKOZSRuI,81330
170
170
  zenml/integrations/azure/step_operators/__init__.py,sha256=fV4_nAO0cH53x6_-F8-CbDEZwb_Vv64oq1r0-vtigEU,819
171
171
  zenml/integrations/azure/step_operators/azureml_step_operator.py,sha256=cmDLcxVd4lw_C2hz-_z50i9tcGgGbNtrBeL5FBQ3HTE,7394
172
172
  zenml/integrations/bentoml/__init__.py,sha256=q7Vo8DL4j5N3p2Gu0_vio5VUUlW8wQ6N-ZcunK_ibiY,1876
@@ -1293,8 +1293,8 @@ zenml/zen_stores/secrets_stores/sql_secrets_store.py,sha256=nEO0bAPlULBLxLVk-UTR
1293
1293
  zenml/zen_stores/sql_zen_store.py,sha256=GEBQDPhm52-YyxLBJcebNviwtr-VK_dnaHrg21fzJOw,417086
1294
1294
  zenml/zen_stores/template_utils.py,sha256=EKYBgmDLTS_PSMWaIO5yvHPLiQvMqHcsAe6NUCrv-i4,9068
1295
1295
  zenml/zen_stores/zen_store_interface.py,sha256=vf2gKBWfUUPtcGZC35oQB6pPNVzWVyQC8nWxVLjfrxM,92692
1296
- zenml_nightly-0.73.0.dev20250131.dist-info/LICENSE,sha256=wbnfEnXnafPbqwANHkV6LUsPKOtdpsd-SNw37rogLtc,11359
1297
- zenml_nightly-0.73.0.dev20250131.dist-info/METADATA,sha256=n0WxU1eiS-WyTdH9oHEG_eQRzqr7ZMGMLWPJnA6rU4o,21355
1298
- zenml_nightly-0.73.0.dev20250131.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
1299
- zenml_nightly-0.73.0.dev20250131.dist-info/entry_points.txt,sha256=QK3ETQE0YswAM2mWypNMOv8TLtr7EjnqAFq1br_jEFE,43
1300
- zenml_nightly-0.73.0.dev20250131.dist-info/RECORD,,
1296
+ zenml_nightly-0.73.0.dev20250202.dist-info/LICENSE,sha256=wbnfEnXnafPbqwANHkV6LUsPKOtdpsd-SNw37rogLtc,11359
1297
+ zenml_nightly-0.73.0.dev20250202.dist-info/METADATA,sha256=nmYGVPtbXZzHEmT2psgCsYABsyY6Mx4_ztCXMta2Nkk,21428
1298
+ zenml_nightly-0.73.0.dev20250202.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
1299
+ zenml_nightly-0.73.0.dev20250202.dist-info/entry_points.txt,sha256=QK3ETQE0YswAM2mWypNMOv8TLtr7EjnqAFq1br_jEFE,43
1300
+ zenml_nightly-0.73.0.dev20250202.dist-info/RECORD,,