Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
import org.springframework.util.StringUtils;

import com.azure.core.util.Context;
import com.azure.data.appconfiguration.models.ConfigurationSetting;
import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting;
import com.azure.data.appconfiguration.models.SecretReferenceConfigurationSetting;
Expand Down Expand Up @@ -60,7 +61,7 @@ class AppConfigurationApplicationSettingPropertySource extends AppConfigurationP
* @param keyPrefixTrimValues prefixs to trim from key values
* @throws InvalidConfigurationPropertyValueException thrown if fails to parse Json content type
*/
public void initProperties(List<String> keyPrefixTrimValues, boolean isRefresh) throws InvalidConfigurationPropertyValueException {
public void initProperties(List<String> keyPrefixTrimValues, Context context) throws InvalidConfigurationPropertyValueException {

List<String> labels = Arrays.asList(labelFilters);
// Reverse labels so they have the right priority order.
Expand All @@ -70,7 +71,7 @@ public void initProperties(List<String> keyPrefixTrimValues, boolean isRefresh)
SettingSelector settingSelector = new SettingSelector().setKeyFilter(keyFilter + "*").setLabelFilter(label);

// * for wildcard match
processConfigurationSettings(replicaClient.listSettings(settingSelector, isRefresh), settingSelector.getKeyFilter(),
processConfigurationSettings(replicaClient.listSettings(settingSelector, context), settingSelector.getKeyFilter(),
keyPrefixTrimValues);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ public class AppConfigurationConstants {
* Constant for tracing if Key Vault is configured for use.
*/
public static final String KEY_VAULT_CONFIGURED_TRACING = "UsesKeyVault";

/**
* Constant for tracing if Push Refresh is enabled for the store.
*/
public static final String PUSH_REFRESH = "PushRefresh";

/**
* Http Header User Agent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
import org.springframework.core.env.EnumerablePropertySource;

import com.azure.core.util.Context;
import com.azure.data.appconfiguration.ConfigurationClient;

/**
Expand Down Expand Up @@ -51,5 +52,5 @@ protected static String getLabelName(String[] labelFilters) {
return String.join(",", labelFilters);
}

protected abstract void initProperties(List<String> trim, boolean isRefresh) throws InvalidConfigurationPropertyValueException;
protected abstract void initProperties(List<String> trim, Context context) throws InvalidConfigurationPropertyValueException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@
// Licensed under the MIT License.
package com.azure.spring.cloud.appconfiguration.config.implementation;

import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.PUSH_REFRESH;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.azure.core.exception.HttpResponseException;
import com.azure.core.util.Context;
import com.azure.data.appconfiguration.models.ConfigurationSetting;
import com.azure.spring.cloud.appconfiguration.config.implementation.autofailover.ReplicaLookUp;
import com.azure.spring.cloud.appconfiguration.config.implementation.feature.FeatureFlagState;
import com.azure.spring.cloud.appconfiguration.config.implementation.feature.FeatureFlags;
import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring;
import com.azure.spring.cloud.appconfiguration.config.implementation.properties.AppConfigurationStoreMonitoring.PushNotification;
import com.azure.spring.cloud.appconfiguration.config.implementation.properties.FeatureFlagStore;

public class AppConfigurationRefreshUtil {
Expand Down Expand Up @@ -50,14 +54,22 @@ RefreshEventData refreshStoresCheck(AppConfigurationReplicaClientFactory clientF
clientFactory.setCurrentConfigStoreClient(originEndpoint, originEndpoint);

AppConfigurationStoreMonitoring monitor = connection.getMonitoring();

boolean pushRefresh = false;
PushNotification notification = monitor.getPushNotification();
if ((notification.getPrimaryToken() != null && StringUtils.hasText(notification.getPrimaryToken().getName()))
|| (notification.getSecondaryToken() != null && StringUtils.hasText(notification.getPrimaryToken().getName()))) {
pushRefresh = true;
}
Context context = new Context("refresh", true).addData(PUSH_REFRESH, pushRefresh);

List<AppConfigurationReplicaClient> clients = clientFactory.getAvailableClients(originEndpoint);

if (monitor.isEnabled() && StateHolder.getLoadState(originEndpoint)) {
for (AppConfigurationReplicaClient client : clients) {
try {
refreshWithTime(client, StateHolder.getState(originEndpoint), monitor.getRefreshInterval(),
eventData, replicaLookUp);
eventData, replicaLookUp, context);
if (eventData.getDoRefresh()) {
clientFactory.setCurrentConfigStoreClient(originEndpoint, client.getEndpoint());
return eventData;
Expand All @@ -81,7 +93,7 @@ RefreshEventData refreshStoresCheck(AppConfigurationReplicaClientFactory clientF
for (AppConfigurationReplicaClient client : clients) {
try {
refreshWithTimeFeatureFlags(client, StateHolder.getStateFeatureFlag(originEndpoint),
monitor.getFeatureFlagRefreshInterval(), eventData, replicaLookUp);
monitor.getFeatureFlagRefreshInterval(), eventData, replicaLookUp, context);
if (eventData.getDoRefresh()) {
clientFactory.setCurrentConfigStoreClient(originEndpoint, client.getEndpoint());
return eventData;
Expand Down Expand Up @@ -115,10 +127,10 @@ RefreshEventData refreshStoresCheck(AppConfigurationReplicaClientFactory clientF
* @param originEndpoint config store origin endpoint
* @return A refresh should be triggered.
*/
static boolean refreshStoreCheck(AppConfigurationReplicaClient client, String originEndpoint) {
static boolean refreshStoreCheck(AppConfigurationReplicaClient client, String originEndpoint, Context context) {
RefreshEventData eventData = new RefreshEventData();
if (StateHolder.getLoadState(originEndpoint)) {
refreshWithoutTime(client, StateHolder.getState(originEndpoint).getWatchKeys(), eventData);
refreshWithoutTime(client, StateHolder.getState(originEndpoint).getWatchKeys(), eventData, context);
}
return eventData.getDoRefresh();
}
Expand All @@ -131,12 +143,12 @@ static boolean refreshStoreCheck(AppConfigurationReplicaClient client, String or
* @return true if a refresh should be triggered.
*/
static boolean refreshStoreFeatureFlagCheck(Boolean featureStoreEnabled,
AppConfigurationReplicaClient client) {
AppConfigurationReplicaClient client, Context context) {
RefreshEventData eventData = new RefreshEventData();
String endpoint = client.getEndpoint();

if (featureStoreEnabled && StateHolder.getStateFeatureFlag(endpoint) != null) {
refreshWithoutTimeFeatureFlags(client, StateHolder.getStateFeatureFlag(endpoint), eventData);
refreshWithoutTimeFeatureFlags(client, StateHolder.getStateFeatureFlag(endpoint), eventData, context);
} else {
LOGGER.debug("Skipping feature flag refresh check for " + endpoint);
}
Expand All @@ -151,10 +163,10 @@ static boolean refreshStoreFeatureFlagCheck(Boolean featureStoreEnabled,
* @param eventData Info for this refresh event.
*/
private static void refreshWithTime(AppConfigurationReplicaClient client, State state, Duration refreshInterval,
RefreshEventData eventData, ReplicaLookUp replicaLookUp) throws AppConfigurationStatusException {
RefreshEventData eventData, ReplicaLookUp replicaLookUp, Context context) throws AppConfigurationStatusException {
if (Instant.now().isAfter(state.getNextRefreshCheck())) {
replicaLookUp.updateAutoFailoverEndpoints();
refreshWithoutTime(client, state.getWatchKeys(), eventData);
refreshWithoutTime(client, state.getWatchKeys(), eventData, context);

StateHolder.getCurrentState().updateStateRefresh(state, refreshInterval);
}
Expand All @@ -168,9 +180,9 @@ private static void refreshWithTime(AppConfigurationReplicaClient client, State
* @param eventData Refresh event info
*/
private static void refreshWithoutTime(AppConfigurationReplicaClient client, List<ConfigurationSetting> watchKeys,
RefreshEventData eventData) throws AppConfigurationStatusException {
RefreshEventData eventData, Context context) throws AppConfigurationStatusException {
for (ConfigurationSetting watchKey : watchKeys) {
ConfigurationSetting watchedKey = client.getWatchKey(watchKey.getKey(), watchKey.getLabel(), true);
ConfigurationSetting watchedKey = client.getWatchKey(watchKey.getKey(), watchKey.getLabel(), context);

// If there is no result, etag will be considered empty.
// A refresh will trigger once the selector returns a value.
Expand All @@ -184,14 +196,14 @@ private static void refreshWithoutTime(AppConfigurationReplicaClient client, Lis
}

private static void refreshWithTimeFeatureFlags(AppConfigurationReplicaClient client, FeatureFlagState state,
Duration refreshInterval, RefreshEventData eventData, ReplicaLookUp replicaLookUp)
Duration refreshInterval, RefreshEventData eventData, ReplicaLookUp replicaLookUp, Context context)
throws AppConfigurationStatusException {
Instant date = Instant.now();
if (date.isAfter(state.getNextRefreshCheck())) {
replicaLookUp.updateAutoFailoverEndpoints();

for (FeatureFlags featureFlags : state.getWatchKeys()) {
if (client.checkWatchKeys(featureFlags.getSettingSelector(), true)) {
if (client.checkWatchKeys(featureFlags.getSettingSelector(), context)) {
String eventDataInfo = ".appconfig.featureflag/*";

// Only one refresh Event needs to be call to update all of the
Expand All @@ -209,10 +221,10 @@ private static void refreshWithTimeFeatureFlags(AppConfigurationReplicaClient cl
}

private static void refreshWithoutTimeFeatureFlags(AppConfigurationReplicaClient client, FeatureFlagState watchKeys,
RefreshEventData eventData) throws AppConfigurationStatusException {
RefreshEventData eventData, Context context) throws AppConfigurationStatusException {

for (FeatureFlags featureFlags : watchKeys.getWatchKeys()) {
if (client.checkWatchKeys(featureFlags.getSettingSelector(), true)) {
if (client.checkWatchKeys(featureFlags.getSettingSelector(), context)) {
String eventDataInfo = ".appconfig.featureflag/*";

// Only one refresh Event needs to be call to update all of the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,9 @@ String getEndpoint() {
* @param label String value of the watch key, use \0 for null.
* @return The first returned configuration.
*/
ConfigurationSetting getWatchKey(String key, String label, boolean isRefresh)
ConfigurationSetting getWatchKey(String key, String label, Context context)
throws HttpResponseException {
try {
Context context = new Context("refresh", isRefresh);
ConfigurationSetting selector = new ConfigurationSetting().setKey(key).setLabel(label);
ConfigurationSetting watchKey = NormalizeNull
.normalizeNullLabel(
Expand All @@ -111,11 +110,10 @@ ConfigurationSetting getWatchKey(String key, String label, boolean isRefresh)
* @param settingSelector Information on which setting to pull. i.e. number of results, key value...
* @return List of Configuration Settings.
*/
List<ConfigurationSetting> listSettings(SettingSelector settingSelector, boolean isRefresh)
List<ConfigurationSetting> listSettings(SettingSelector settingSelector, Context context)
throws HttpResponseException {
List<ConfigurationSetting> configurationSettings = new ArrayList<>();
try {
Context context = new Context("refresh", isRefresh);
PagedIterable<ConfigurationSetting> settings = client.listConfigurationSettings(settingSelector, context);
settings.forEach(setting -> {
configurationSettings.add(NormalizeNull.normalizeNullLabel(setting));
Expand All @@ -130,11 +128,11 @@ List<ConfigurationSetting> listSettings(SettingSelector settingSelector, boolean
}
}

FeatureFlags listFeatureFlags(SettingSelector settingSelector, boolean isRefresh) throws HttpResponseException {
FeatureFlags listFeatureFlags(SettingSelector settingSelector, Context context)
throws HttpResponseException {
List<ConfigurationSetting> configurationSettings = new ArrayList<>();
List<MatchConditions> checks = new ArrayList<>();
try {
Context context = new Context("refresh", isRefresh);
client.listConfigurationSettings(settingSelector, context).streamByPage().forEach(pagedResponse -> {
checks.add(
new MatchConditions().setIfNoneMatch(pagedResponse.getHeaders().getValue(HttpHeaderName.ETAG)));
Expand All @@ -155,12 +153,11 @@ FeatureFlags listFeatureFlags(SettingSelector settingSelector, boolean isRefresh
}
}

List<ConfigurationSetting> listSettingSnapshot(String snapshotName, boolean isRefresh) {
List<ConfigurationSetting> listSettingSnapshot(String snapshotName, Context context) {
List<ConfigurationSetting> configurationSettings = new ArrayList<>();
try {
// Because Spring always refreshes all we still have to load snapshots on refresh to build the property
// sources.
Context context = new Context("refresh", isRefresh);
ConfigurationSnapshot snapshot = client.getSnapshotWithResponse(snapshotName, null, context).getValue();
if (!SnapshotComposition.KEY.equals(snapshot.getSnapshotComposition())) {
throw new IllegalArgumentException("Snapshot " + snapshotName + " needs to be of type Key.");
Expand All @@ -177,8 +174,7 @@ List<ConfigurationSetting> listSettingSnapshot(String snapshotName, boolean isRe
}
}

boolean checkWatchKeys(SettingSelector settingSelector, boolean isRefresh) {
Context context = new Context("refresh", false);
boolean checkWatchKeys(SettingSelector settingSelector, Context context) {
List<PagedResponse<ConfigurationSetting>> results = client.listConfigurationSettings(settingSelector, context)
.streamByPage().filter(pagedResponse -> pagedResponse.getStatusCode() != 304).toList();
return results.size() > 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;

import com.azure.core.util.Context;
import com.azure.data.appconfiguration.models.ConfigurationSetting;
import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting;
import com.azure.spring.cloud.appconfiguration.config.implementation.feature.FeatureFlags;
Expand Down Expand Up @@ -45,8 +46,8 @@ final class AppConfigurationSnapshotPropertySource extends AppConfigurationAppli
* @param isRefresh true if a refresh triggered the loading of the Snapshot.
* @throws InvalidConfigurationPropertyValueException thrown if fails to parse Json content type
*/
public void initProperties(List<String> trim, boolean isRefresh) throws InvalidConfigurationPropertyValueException {
processConfigurationSettings(replicaClient.listSettingSnapshot(snapshotName, isRefresh), null, trim);
public void initProperties(List<String> trim, Context context) throws InvalidConfigurationPropertyValueException {
processConfigurationSettings(replicaClient.listSettingSnapshot(snapshotName, context), null, trim);

FeatureFlags featureFlags = new FeatureFlags(null, featureFlagsList);
featureFlagClient.proccessFeatureFlags(featureFlags, replicaClient.getEndpoint());
Expand Down
Loading