1212 Dict ,
1313 Mapping ,
1414 Optional ,
15- overload ,
1615 List ,
1716 Tuple ,
1817)
2322)
2423from azure .core .credentials import TokenCredential
2524from azure .core .exceptions import AzureError , HttpResponseError
26- from ._models import AzureAppConfigurationKeyVaultOptions , SettingSelector
2725from ._key_vault ._secret_provider import SecretProvider
2826from ._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
4133from ._client_manager import ConfigurationClientManager , _ConfigurationClientWrapper as ConfigurationClient
4234from ._user_agent import USER_AGENT
35+ from ._utils import get_startup_backoff
4336
4437JSON = Mapping [str , Any ]
4538logger = 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-
21141def _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