Skip to content

Commit 25a65b6

Browse files
Merge branch 'fix/keyvault-certificates-san-ip-uri-validator' of https://github.com/Azure/azure-sdk-for-python into fix/keyvault-certificates-san-ip-uri-validator
2 parents 20d57ee + 0b20476 commit 25a65b6

58 files changed

Lines changed: 5112 additions & 3836 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/post-apiview.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,7 @@ jobs:
3131
- name: Create APIView Comment on PR
3232
run: |
3333
. "eng/common/scripts/Helpers/ApiView-Helpers.ps1"
34-
Set-ApiViewCommentForRelatedIssues -HeadCommitish ${{ github.event.check_run.head_sha }} -AuthToken ${{ secrets.GITHUB_TOKEN }}
34+
Set-ApiViewCommentForRelatedIssues -HeadCommitish ${{ github.event.check_run.head_sha }} -AuthToken ${{ secrets.GITHUB_TOKEN }} -GitHubActionRunUrl $env:GITHUB_ACTION_RUN_URL
3535
shell: pwsh
36+
env:
37+
GITHUB_ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}

eng/tools/azure-sdk-tools/azpysdk/verifytypes.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ def install_from_main(self, setup_path: str, python_executable: Optional[str] =
176176

177177
command = get_pip_command(python_executable) + ["install", ".", "--force-reinstall"]
178178

179+
# When using uv, add --no-sources to ignore [tool.uv.sources] relative paths
180+
# that can't resolve in a sparse checkout, and --python to target the correct venv.
181+
if command[0] == "uv":
182+
command += ["--no-sources"]
183+
if python_executable:
184+
command += ["--python", python_executable]
185+
179186
subprocess.check_call(command, stdout=subprocess.DEVNULL)
180187
finally:
181188
os.chdir(cwd) # allow temp dir to be deleted

sdk/appconfiguration/azure-appconfiguration-provider/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
### Features Added
66

7+
- Added `refresh_enabled` parameter to the `load` method. Defaults to `True` if `refresh_on` is set. When set to `True` without `refresh_on` keys, all selected key-values are monitored for changes. When set to `False`, calling `refresh` will be a no-op.
8+
- Added the ability to monitor all selected key-values for refresh with the `refresh_enabled` kwarg. When this kwarg is set to `True`, and `refresh_on` is not specified, changes to any selected key-values will trigger configuration reload.
9+
710
### Breaking Changes
811

912
### Bugs Fixed

sdk/appconfiguration/azure-appconfiguration-provider/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/appconfiguration/azure-appconfiguration-provider",
5-
"Tag": "python/appconfiguration/azure-appconfiguration-provider_5ac0518c19"
5+
"Tag": "python/appconfiguration/azure-appconfiguration-provider_b3a8244024"
66
}

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# Licensed under the MIT License. See License.txt in the project root for
44
# license information.
55
# -------------------------------------------------------------------------
6-
7-
from ._azureappconfigurationprovider import load, AzureAppConfigurationProvider
6+
from ._load import load
7+
from ._azureappconfigurationprovider import AzureAppConfigurationProvider
88
from ._models import (
99
AzureAppConfigurationKeyVaultOptions,
1010
SettingSelector,

sdk/appconfiguration/azure-appconfiguration-provider/azure/appconfiguration/provider/_azureappconfigurationprovider.py

Lines changed: 22 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
Dict,
1313
Mapping,
1414
Optional,
15-
overload,
1615
List,
1716
Tuple,
1817
)
@@ -23,191 +22,22 @@
2322
)
2423
from azure.core.credentials import TokenCredential
2524
from azure.core.exceptions import AzureError, HttpResponseError
26-
from ._models import AzureAppConfigurationKeyVaultOptions, SettingSelector
2725
from ._key_vault._secret_provider import SecretProvider
2826
from ._constants import (
2927
FEATURE_MANAGEMENT_KEY,
3028
FEATURE_FLAG_KEY,
3129
DEFAULT_STARTUP_TIMEOUT,
3230
SNAPSHOT_REF_CONTENT_TYPE,
3331
)
34-
from ._azureappconfigurationproviderbase import (
35-
AzureAppConfigurationProviderBase,
36-
delay_failure,
37-
process_load_parameters,
38-
sdk_allowed_kwargs,
39-
_get_startup_backoff,
40-
)
32+
from ._azureappconfigurationproviderbase import AzureAppConfigurationProviderBase
4133
from ._client_manager import ConfigurationClientManager, _ConfigurationClientWrapper as ConfigurationClient
4234
from ._user_agent import USER_AGENT
35+
from ._utils import get_startup_backoff
4336

4437
JSON = Mapping[str, Any]
4538
logger = logging.getLogger(__name__)
4639

4740

48-
@overload
49-
def load( # pylint: disable=docstring-keyword-should-match-keyword-only
50-
endpoint: str,
51-
credential: "TokenCredential",
52-
*,
53-
selects: Optional[List[SettingSelector]] = None,
54-
trim_prefixes: Optional[List[str]] = None,
55-
keyvault_credential: Optional["TokenCredential"] = None,
56-
keyvault_client_configs: Optional[Mapping[str, JSON]] = None,
57-
secret_resolver: Optional[Callable[[str], str]] = None,
58-
key_vault_options: Optional[AzureAppConfigurationKeyVaultOptions] = None,
59-
refresh_on: Optional[List[Tuple[str, str]]] = None,
60-
refresh_interval: int = 30,
61-
on_refresh_success: Optional[Callable] = None,
62-
on_refresh_error: Optional[Callable[[Exception], None]] = None,
63-
feature_flag_enabled: bool = False,
64-
feature_flag_selectors: Optional[List[SettingSelector]] = None,
65-
feature_flag_refresh_enabled: bool = False,
66-
startup_timeout: int = DEFAULT_STARTUP_TIMEOUT,
67-
**kwargs,
68-
) -> "AzureAppConfigurationProvider":
69-
"""
70-
Loads configuration settings from Azure App Configuration into a Python application.
71-
72-
:param str endpoint: Endpoint for App Configuration resource.
73-
:param ~azure.core.credentials.TokenCredential credential: Credential for App Configuration resource.
74-
:keyword Optional[List[~azure.appconfiguration.provider.SettingSelector]] selects: List of setting selectors to
75-
filter configuration settings
76-
:keyword Optional[List[str]] trim_prefixes: List of prefixes to trim from configuration keys
77-
:keyword ~azure.core.credentials.TokenCredential keyvault_credential: A credential for authenticating with the key
78-
vault. This is optional if keyvault_client_configs is provided.
79-
:keyword Mapping[str, Mapping] keyvault_client_configs: A Mapping of SecretClient endpoints to client
80-
configurations from azure-keyvault-secrets. This is optional if keyvault_credential is provided. If a credential
81-
isn't provided a credential will need to be in each set for each.
82-
:keyword Callable[[str], str] secret_resolver: A function that takes a URI and returns a value.
83-
:keyword List[Tuple[str, str]] refresh_on: One or more settings whose modification will trigger a full refresh
84-
after a fixed interval. This should be a list of Key-Label pairs for specific settings (filters and wildcards are
85-
not supported).
86-
:keyword int refresh_interval: The minimum time in seconds between when a call to `refresh` will actually trigger a
87-
service call to update the settings. Default value is 30 seconds.
88-
:keyword on_refresh_success: Optional callback to be invoked when a change is found and a successful refresh has
89-
happened.
90-
:paramtype on_refresh_success: Optional[Callable]
91-
:keyword on_refresh_error: Optional callback to be invoked when an error occurs while refreshing settings. If not
92-
specified, errors will be raised.
93-
:paramtype on_refresh_error: Optional[Callable[[Exception], None]]
94-
:keyword feature_flag_enabled: Optional flag to enable or disable the loading of feature flags. Default is False.
95-
:paramtype feature_flag_enabled: bool
96-
:keyword feature_flag_selectors: Optional list of selectors to filter feature flags. By default will load all
97-
feature flags without a label.
98-
:paramtype feature_flag_selectors: List[SettingSelector]
99-
:keyword feature_flag_refresh_enabled: Optional flag to enable or disable the refresh of feature flags. Default is
100-
False.
101-
:paramtype feature_flag_refresh_enabled: bool
102-
:keyword replica_discovery_enabled: Optional flag to enable or disable the discovery of replica endpoints. Default
103-
is True.
104-
:paramtype replica_discovery_enabled: bool
105-
:keyword load_balancing_enabled: Optional flag to enable or disable the load balancing of replica endpoints. Default
106-
is False.
107-
:paramtype load_balancing_enabled: bool
108-
:keyword configuration_mapper: Optional function to map configuration settings. Enables transformation of
109-
configurations before they are added to the provider.
110-
:paramtype configuration_mapper: Optional[Callable[[ConfigurationSetting], None]]
111-
:keyword startup_timeout: The amount of time in seconds allowed to load data from Azure App Configuration on
112-
startup. The default value is 100 seconds.
113-
:paramtype startup_timeout: int
114-
"""
115-
116-
117-
@overload
118-
def load( # pylint: disable=docstring-keyword-should-match-keyword-only
119-
*,
120-
connection_string: str,
121-
selects: Optional[List[SettingSelector]] = None,
122-
trim_prefixes: Optional[List[str]] = None,
123-
keyvault_credential: Optional["TokenCredential"] = None,
124-
keyvault_client_configs: Optional[Mapping[str, JSON]] = None,
125-
secret_resolver: Optional[Callable[[str], str]] = None,
126-
key_vault_options: Optional[AzureAppConfigurationKeyVaultOptions] = None,
127-
refresh_on: Optional[List[Tuple[str, str]]] = None,
128-
refresh_interval: int = 30,
129-
on_refresh_success: Optional[Callable] = None,
130-
on_refresh_error: Optional[Callable[[Exception], None]] = None,
131-
feature_flag_enabled: bool = False,
132-
feature_flag_selectors: Optional[List[SettingSelector]] = None,
133-
feature_flag_refresh_enabled: bool = False,
134-
startup_timeout: int = DEFAULT_STARTUP_TIMEOUT,
135-
**kwargs,
136-
) -> "AzureAppConfigurationProvider":
137-
"""
138-
Loads configuration settings from Azure App Configuration into a Python application.
139-
140-
:keyword str connection_string: Connection string for App Configuration resource.
141-
:keyword Optional[List[~azure.appconfiguration.provider.SettingSelector]] selects: List of setting selectors to
142-
filter configuration settings
143-
:keyword trim_prefixes: Optional[List[str]] trim_prefixes: List of prefixes to trim from configuration keys
144-
:keyword ~azure.core.credentials.TokenCredential keyvault_credential: A credential for authenticating with the key
145-
vault. This is optional if keyvault_client_configs is provided.
146-
:keyword Mapping[str, Mapping] keyvault_client_configs: A Mapping of SecretClient endpoints to client
147-
configurations from azure-keyvault-secrets. This is optional if keyvault_credential is provided. If a credential
148-
isn't provided a credential will need to be in each set for each.
149-
:keyword Callable[[str], str] secret_resolver: A function that takes a URI and returns a value.
150-
:keyword List[Tuple[str, str]] refresh_on: One or more settings whose modification will trigger a full refresh
151-
after a fixed interval. This should be a list of Key-Label pairs for specific settings (filters and wildcards are
152-
not supported).
153-
:keyword refresh_on: One or more settings whose modification will trigger a full refresh after a fixed interval.
154-
This should be a list of Key-Label pairs for specific settings (filters and wildcards are not supported).
155-
:paramtype refresh_on: List[Tuple[str, str]]
156-
:keyword int refresh_interval: The minimum time in seconds between when a call to `refresh` will actually trigger a
157-
service call to update the settings. Default value is 30 seconds.
158-
:keyword on_refresh_success: Optional callback to be invoked when a change is found and a successful refresh has
159-
happened.
160-
:paramtype on_refresh_success: Optional[Callable]
161-
:keyword on_refresh_error: Optional callback to be invoked when an error occurs while refreshing settings. If not
162-
specified, errors will be raised.
163-
:paramtype on_refresh_error: Optional[Callable[[Exception], None]]
164-
:keyword feature_flag_enabled: Optional flag to enable or disable the loading of feature flags. Default is False.
165-
:paramtype feature_flag_enabled: bool
166-
:keyword feature_flag_selectors: Optional list of selectors to filter feature flags. By default will load all
167-
feature flags without a label.
168-
:paramtype feature_flag_selectors: List[SettingSelector]
169-
:keyword feature_flag_refresh_enabled: Optional flag to enable or disable the refresh of feature flags. Default is
170-
False.
171-
:paramtype feature_flag_refresh_enabled: bool
172-
:keyword replica_discovery_enabled: Optional flag to enable or disable the discovery of replica endpoints. Default
173-
is True.
174-
:paramtype replica_discovery_enabled: bool
175-
:keyword load_balancing_enabled: Optional flag to enable or disable the load balancing of replica endpoints. Default
176-
is False.
177-
:paramtype load_balancing_enabled: bool
178-
:keyword configuration_mapper: Optional function to map configuration settings. Enables transformation of
179-
configurations before they are added to the provider.
180-
:paramtype configuration_mapper: Optional[Callable[[ConfigurationSetting], None]]
181-
:keyword startup_timeout: The amount of time in seconds allowed to load data from Azure App Configuration on
182-
startup. The default value is 100 seconds.
183-
:paramtype startup_timeout: int
184-
"""
185-
186-
187-
def load(*args, **kwargs) -> "AzureAppConfigurationProvider":
188-
start_time = datetime.datetime.now()
189-
190-
# Process common load parameters using shared logic
191-
params = process_load_parameters(*args, **kwargs)
192-
193-
provider = _buildprovider(
194-
params["connection_string"],
195-
params["endpoint"],
196-
params["credential"],
197-
uses_key_vault=params["uses_key_vault"],
198-
startup_timeout=params["startup_timeout"],
199-
**params["kwargs"],
200-
)
201-
kwargs = sdk_allowed_kwargs(params["kwargs"])
202-
203-
try:
204-
provider._load_all(**kwargs) # pylint:disable=protected-access
205-
except Exception as e:
206-
delay_failure(start_time)
207-
raise e
208-
return provider
209-
210-
21141
def _buildprovider(
21242
connection_string: Optional[str], endpoint: Optional[str], credential: Optional[TokenCredential], **kwargs
21343
) -> "AzureAppConfigurationProvider":
@@ -283,16 +113,26 @@ def _attempt_refresh(self, client: ConfigurationClient, replica_count: int, is_f
283113
feature_flag_refresh_attempted = False
284114
updated_watched_settings: Mapping[Tuple[str, str], Optional[str]] = {}
285115
existing_feature_flag_usage = self._tracing_context.feature_filter_usage.copy()
116+
page_etags: List[List[str]] = []
286117
try:
287-
if self._watched_settings and self._refresh_timer.needs_refresh():
118+
if self._refresh_enabled and not self._watched_settings and self._refresh_timer.needs_refresh():
119+
configuration_refresh_attempted = True
120+
121+
if client.check_page_etags(self._selects, self._page_etags, headers=headers, **kwargs):
122+
configuration_settings, page_etags = client.load_configuration_settings(
123+
self._selects, headers=headers, **kwargs
124+
)
125+
settings_refreshed = True
126+
127+
elif self._refresh_enabled and self._watched_settings and self._refresh_timer.needs_refresh():
288128
configuration_refresh_attempted = True
289129

290130
updated_watched_settings = client.get_updated_watched_settings(
291131
self._watched_settings, headers=headers, **kwargs
292132
)
293133

294134
if len(updated_watched_settings) > 0:
295-
configuration_settings = client.load_configuration_settings(
135+
configuration_settings, _ = client.load_configuration_settings(
296136
self._selects, headers=headers, **kwargs
297137
)
298138
settings_refreshed = True
@@ -319,6 +159,7 @@ def _attempt_refresh(self, client: ConfigurationClient, replica_count: int, is_f
319159
processed_settings = self._process_feature_flags(processed_settings, processed_feature_flags, feature_flags)
320160
self._dict = processed_settings
321161
if settings_refreshed:
162+
self._page_etags = page_etags
322163
# Update the watch keys that have changed
323164
self._watched_settings.update(updated_watched_settings)
324165
# Reset timers at the same time as they should load from the same store.
@@ -336,8 +177,8 @@ def _attempt_refresh(self, client: ConfigurationClient, replica_count: int, is_f
336177
raise e
337178

338179
def refresh(self, **kwargs) -> None:
339-
if not self._watched_settings and not self._feature_flag_refresh_enabled:
340-
logger.debug("Refresh called but no refresh enabled.")
180+
if not self._refresh_enabled and not self._feature_flag_refresh_enabled:
181+
logger.debug("Refresh called but refresh is not enabled.")
341182
return
342183
if not self._refresh_lock.acquire(blocking=False): # pylint: disable= consider-using-with
343184
logger.debug("Refresh called but refresh already in progress.")
@@ -388,7 +229,7 @@ def _load_all(self, **kwargs: Any) -> None:
388229

389230
# Calculate delay before next retry attempt
390231
elapsed_seconds = (datetime.datetime.now() - startup_start_time).total_seconds()
391-
delay, is_exponential_backoff = _get_startup_backoff(elapsed_seconds, exponential_backoff_attempts)
232+
delay, is_exponential_backoff = get_startup_backoff(elapsed_seconds, exponential_backoff_attempts)
392233

393234
if is_exponential_backoff:
394235
exponential_backoff_attempts += 1
@@ -427,7 +268,9 @@ def _try_initialize(self, startup_exceptions: List[Exception], **kwargs: Any) ->
427268
is_failover_request,
428269
)
429270
try:
430-
configuration_settings = client.load_configuration_settings(self._selects, headers=headers, **kwargs)
271+
configuration_settings, page_etags = client.load_configuration_settings(
272+
self._selects, headers=headers, **kwargs
273+
)
431274
watched_settings = self._update_watched_settings(configuration_settings)
432275
processed_settings = self._process_configurations(configuration_settings, client)
433276

@@ -460,6 +303,7 @@ def _try_initialize(self, startup_exceptions: List[Exception], **kwargs: Any) ->
460303
with self._update_lock:
461304
self._watched_settings = watched_settings
462305
self._dict = processed_settings
306+
self._page_etags = page_etags
463307
return True
464308
except AzureError as e:
465309
logger.warning("Failed to load configurations from endpoint %s.\n %s", client.endpoint, e.message)

0 commit comments

Comments
 (0)