Skip to content

Commit 4ee85c0

Browse files
authored
Revert "App Config - Feature Flag load/refresh (#33411)" (#33725)
This reverts commit 97f43d2.
1 parent 24cdb8d commit 4ee85c0

File tree

13 files changed

+45
-286
lines changed

13 files changed

+45
-286
lines changed

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_29963ddf05"
5+
"Tag": "python/appconfiguration/azure-appconfiguration-provider_51fb3fb738"
66
}

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

Lines changed: 11 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,6 @@ def load(
100100
:paramtype on_refresh_error: Optional[Callable[[Exception], None]]
101101
:keyword on_refresh_error: Optional callback to be invoked when an error occurs while refreshing settings. If not
102102
specified, errors will be raised.
103-
:paramtype feature_flag_enabled: bool
104-
:keyword feature_flag_enabled: Optional flag to enable or disable the loading of feature flags. Default is False.
105-
:paramtype feature_flag_selectors: List[SettingSelector]
106-
:keyword feature_flag_selectors: Optional list of selectors to filter feature flags. By default will load all
107-
feature flags without a label.
108-
:paramtype feature_flag_refresh_enabled: bool
109-
:keyword feature_flag_refresh_enabled: Optional flag to enable or disable the refresh of feature flags. Default is
110-
False.
111-
:paramtype feature_flag_trim_prefixes: List[str]
112-
:keyword feature_flag_trim_prefixes: After the FEATURE_FLAG_PREFIX is trimmed, the first match in
113-
feature_flag_trim_prefixes will be trimmed, if there is one.
114103
"""
115104

116105

@@ -155,17 +144,6 @@ def load(
155144
:paramtype on_refresh_error: Optional[Callable[[Exception], None]]
156145
:keyword on_refresh_error: Optional callback to be invoked when an error occurs while refreshing settings. If not
157146
specified, errors will be raised.
158-
:paramtype feature_flag_enabled: bool
159-
:keyword feature_flag_enabled: Optional flag to enable or disable the loading of feature flags. Default is False.
160-
:paramtype feature_flag_selectors: List[SettingSelector]
161-
:keyword feature_flag_selectors: Optional list of selectors to filter feature flags. By default will load all
162-
feature flags without a label.
163-
:paramtype feature_flag_refresh_enabled: bool
164-
:keyword feature_flag_refresh_enabled: Optional flag to enable or disable the refresh of feature flags. Default is
165-
False.
166-
:paramtype feature_flag_trim_prefixes: List[str]
167-
:keyword feature_flag_trim_prefixes: After the FEATURE_FLAG_PREFIX is trimmed, the first match in
168-
feature_flag_trim_prefixes will be trimmed, if there is one.
169147
"""
170148

171149

@@ -460,19 +438,12 @@ def __init__(self, **kwargs) -> None:
460438
or self._keyvault_client_configs is not None
461439
or self._secret_resolver is not None
462440
)
463-
self._feature_flag_enabled = kwargs.pop("feature_flag_enabled", False)
464-
self._feature_flag_selectors = kwargs.pop("feature_flag_selectors", [SettingSelector(key_filter="*")])
465-
self._refresh_on_feature_flags = None
466-
self._feature_flag_refresh_timer: _RefreshTimer = _RefreshTimer(**kwargs)
467-
self._feature_flag_refresh_enabled = kwargs.pop("feature_flag_refresh_enabled", False)
468-
feature_flag_trim_prefixes = kwargs.pop("feature_flag_trim_prefixes", [])
469-
self._feature_flag_trim_prefixes: List[str] = sorted(feature_flag_trim_prefixes, key=len, reverse=True)
470441
self._update_lock = Lock()
471442
self._refresh_lock = Lock()
472443

473444
def refresh(self, **kwargs) -> None:
474-
if not self._refresh_on and not self._feature_flag_refresh_enabled:
475-
logging.debug("Refresh called but no refresh enabled.")
445+
if not self._refresh_on:
446+
logging.debug("Refresh called but no refresh options set.")
476447
return
477448
if not self._refresh_timer.needs_refresh():
478449
logging.debug("Refresh called but refresh interval not elapsed.")
@@ -532,76 +503,7 @@ def refresh(self, **kwargs) -> None:
532503
elif need_refresh and self._on_refresh_success:
533504
self._on_refresh_success()
534505

535-
def refresh_configuration_settings(self, configuration_settings, sentinel_keys, timer, **kwargs) -> None:
536-
success = False
537-
need_refresh = False
538-
updated_feature_flags = False
539-
headers = _get_headers("Watch", uses_key_vault=self._uses_key_vault, **kwargs)
540-
try:
541-
for (key, label), etag in sentinel_keys.items():
542-
etag, update, is_feature_flag, configuration_settings = self._check_configuration_settings(
543-
key, label, etag, headers, configuration_settings, **kwargs
544-
)
545-
if update and not is_feature_flag:
546-
need_refresh = True
547-
elif update and is_feature_flag:
548-
updated_feature_flags = True
549-
550-
sentinel_keys[(key, label)] = etag
551-
# Need to only update once, no matter how many sentinels are updated
552-
if need_refresh:
553-
configuration_settings, sentinel_keys = self._load_configuration_settings(
554-
headers=headers, sentinel_keys=sentinel_keys, **kwargs
555-
)
556-
success = True
557-
# Even if we don't need to refresh, we should reset the timer
558-
timer.reset()
559-
except (ServiceRequestError, ServiceResponseError, HttpResponseError) as e:
560-
# If a call back is provided, we should call it, otherwise raise the error
561-
if not self._on_refresh_error:
562-
raise
563-
self._on_refresh_error(e)
564-
finally:
565-
# If we get an error we should retry sooner than the next refresh interval
566-
if not success:
567-
timer.backoff()
568-
return configuration_settings, sentinel_keys, need_refresh or updated_feature_flags
569-
570-
def _check_configuration_settings(self, key, label, etag, headers, configuration_settings, **kwargs) -> None:
571-
is_feature_flag = False
572-
try:
573-
updated_config = self._client.get_configuration_setting(
574-
key=key, label=label, etag=etag, match_condition=MatchConditions.IfModified, headers=headers, **kwargs
575-
)
576-
is_feature_flag = not isinstance(updated_config, FeatureFlagConfigurationSetting)
577-
if updated_config is not None:
578-
if not is_feature_flag:
579-
logging.debug("Refresh all triggered by key: %s label %s.", key, label)
580-
elif is_feature_flag:
581-
configuration_settings[self._process_key_name(updated_config)] = updated_config.value
582-
return updated_config.etag, True, is_feature_flag, configuration_settings
583-
except HttpResponseError as e:
584-
if e.status_code == 404:
585-
if etag is not None and not is_feature_flag:
586-
# If the sentinel is not found, it means the key/label was deleted, so we should refresh
587-
logging.debug("Refresh all triggered by key: %s label %s.", key, label)
588-
elif etag is not None and is_feature_flag:
589-
configuration_settings[key] = None
590-
return None, True, is_feature_flag, configuration_settings
591-
raise e
592-
return etag, False, is_feature_flag, configuration_settings
593-
594506
def _load_all(self, **kwargs):
595-
configuration_settings, sentinel_keys = self._load_configuration_settings(**kwargs)
596-
feature_flags, feature_flag_sentinel_keys = self._load_feature_flags(**kwargs)
597-
if self._feature_flag_enabled:
598-
configuration_settings[FEATURE_MANAGEMENT_KEY] = feature_flags
599-
self._refresh_on_feature_flags = feature_flag_sentinel_keys
600-
with self._update_lock:
601-
self._refresh_on = sentinel_keys
602-
self._dict = configuration_settings
603-
604-
def _load_configuration_settings(self, **kwargs):
605507
configuration_settings = {}
606508
sentinel_keys = kwargs.pop("sentinel_keys", self._refresh_on)
607509
for select in self._selects:
@@ -612,51 +514,30 @@ def _load_configuration_settings(self, **kwargs):
612514
key = self._process_key_name(config)
613515
value = self._process_key_value(config)
614516
if isinstance(config, FeatureFlagConfigurationSetting):
615-
# Feature flags are ignored when loaded by Selects, as they are selected from
616-
# `feature_flag_selectors`
617-
pass
517+
feature_management = configuration_settings.get(FEATURE_MANAGEMENT_KEY, {})
518+
feature_management[key] = value
519+
if FEATURE_MANAGEMENT_KEY not in configuration_settings:
520+
configuration_settings[FEATURE_MANAGEMENT_KEY] = feature_management
618521
else:
619522
configuration_settings[key] = value
620523
# Every time we run load_all, we should update the etag of our refresh sentinels
621524
# so they stay up-to-date.
622525
# Sentinel keys will have unprocessed key names, so we need to use the original key.
623526
if (config.key, config.label) in self._refresh_on:
624527
sentinel_keys[(config.key, config.label)] = config.etag
625-
return configuration_settings, sentinel_keys
626-
627-
def _load_feature_flags(self, **kwargs):
628-
feature_flag_sentinel_keys = {}
629-
loaded_feature_flags = {}
630-
if self._feature_flag_enabled:
631-
for select in self._feature_flag_selectors:
632-
feature_flags = self._client.list_configuration_settings(
633-
key_filter=FEATURE_FLAG_PREFIX + select.key_filter, label_filter=select.label_filter, **kwargs
634-
)
635-
for feature_flag in feature_flags:
636-
key = self._process_key_name(feature_flag)
637-
loaded_feature_flags[key] = feature_flag.value
638-
if self._feature_flag_refresh_enabled:
639-
feature_flag_sentinel_keys[(feature_flag.key, feature_flag.label)] = feature_flag.etag
640-
return loaded_feature_flags, feature_flag_sentinel_keys
528+
self._refresh_on = sentinel_keys
529+
with self._update_lock:
530+
self._dict = configuration_settings
641531

642532
def _process_key_name(self, config):
643533
trimmed_key = config.key
644534
# Trim the key if it starts with one of the prefixes provided
645-
646-
# Feature Flags have there own prefix, so we need to trim that first
647-
if isinstance(config, FeatureFlagConfigurationSetting):
648-
if trimmed_key.startswith(FEATURE_FLAG_PREFIX):
649-
trimmed_key = trimmed_key[len(FEATURE_FLAG_PREFIX) :]
650-
for trim in self._feature_flag_trim_prefixes:
651-
if trimmed_key.startswith(trim):
652-
trimmed_key = trimmed_key[len(trim) :]
653-
break
654-
return trimmed_key
655-
656535
for trim in self._trim_prefixes:
657536
if config.key.startswith(trim):
658537
trimmed_key = config.key[len(trim) :]
659538
break
539+
if isinstance(config, FeatureFlagConfigurationSetting) and trimmed_key.startswith(FEATURE_FLAG_PREFIX):
540+
return trimmed_key[len(FEATURE_FLAG_PREFIX) :]
660541
return trimmed_key
661542

662543
def _process_key_value(self, config):

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# license information.
55
# -------------------------------------------------------------------------
66

7-
FEATURE_MANAGEMENT_KEY = "FeatureManagement"
7+
FEATURE_MANAGEMENT_KEY = "FeatureManagementFeatureFlags"
88
FEATURE_FLAG_PREFIX = ".appconfig.featureflag/"
99

1010
EMPTY_LABEL = "\0"

sdk/appconfiguration/azure-appconfiguration-provider/samples/feature_flag_sample.py

Lines changed: 0 additions & 23 deletions
This file was deleted.

sdk/appconfiguration/azure-appconfiguration-provider/samples/refresh_sample_feature_flags.py

Lines changed: 0 additions & 73 deletions
This file was deleted.

sdk/appconfiguration/azure-appconfiguration-provider/tests/asynctestcase.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ async def create_aad_client(
2727
secret_resolver=None,
2828
key_vault_options=None,
2929
on_refresh_success=None,
30-
feature_flag_enabled=False,
3130
):
3231
cred = self.get_credential(AzureAppConfigurationClient, is_async=True)
3332

@@ -51,7 +50,6 @@ async def create_aad_client(
5150
user_agent="SDK/Integration",
5251
keyvault_credential=keyvault_cred,
5352
on_refresh_success=on_refresh_success,
54-
feature_flag_enabled=feature_flag_enabled,
5553
)
5654
if key_vault_options:
5755
if not key_vault_options.secret_resolver:
@@ -66,7 +64,6 @@ async def create_aad_client(
6664
user_agent="SDK/Integration",
6765
key_vault_options=key_vault_options,
6866
on_refresh_success=on_refresh_success,
69-
feature_flag_enabled=feature_flag_enabled,
7067
)
7168
return await load(
7269
credential=cred,
@@ -78,7 +75,6 @@ async def create_aad_client(
7875
user_agent="SDK/Integration",
7976
secret_resolver=secret_resolver,
8077
on_refresh_success=on_refresh_success,
81-
feature_flag_enabled=feature_flag_enabled,
8278
)
8379

8480
async def create_client(
@@ -92,7 +88,6 @@ async def create_client(
9288
secret_resolver=None,
9389
key_vault_options=None,
9490
on_refresh_success=None,
95-
feature_flag_enabled=False,
9691
):
9792
client = AzureAppConfigurationClient.from_connection_string(appconfiguration_connection_string)
9893
await setup_configs(client, keyvault_secret_url)
@@ -107,7 +102,6 @@ async def create_client(
107102
user_agent="SDK/Integration",
108103
keyvault_credential=self.get_credential(AzureAppConfigurationClient, is_async=True),
109104
on_refresh_success=on_refresh_success,
110-
feature_flag_enabled=feature_flag_enabled,
111105
)
112106
if key_vault_options:
113107
if not key_vault_options.secret_resolver:
@@ -123,7 +117,6 @@ async def create_client(
123117
user_agent="SDK/Integration",
124118
key_vault_options=key_vault_options,
125119
on_refresh_success=on_refresh_success,
126-
feature_flag_enabled=feature_flag_enabled,
127120
)
128121
return await load(
129122
connection_string=appconfiguration_connection_string,
@@ -134,7 +127,6 @@ async def create_client(
134127
user_agent="SDK/Integration",
135128
secret_resolver=secret_resolver,
136129
on_refresh_success=on_refresh_success,
137-
feature_flag_enabled=feature_flag_enabled,
138130
)
139131

140132

0 commit comments

Comments
 (0)