Skip to content
This repository has been archived by the owner on Aug 29, 2024. It is now read-only.

Commit

Permalink
App configuration 1.1.4 (#676)
Browse files Browse the repository at this point in the history
* Fixing Pagination, Fixing Partial Load

* Adding Client Modifier

* Fixing Empty Label Issue

* Updated Readme

* Makes \0 a diffrent constant then {\0}

* Adding Missing logs
  • Loading branch information
mrm9084 authored Apr 27, 2020
1 parent 12d073b commit c0657a4
Show file tree
Hide file tree
Showing 19 changed files with 279 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
package com.microsoft.azure.spring.cloud.config;

import java.util.List;
import java.util.Optional;

import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand Down Expand Up @@ -67,31 +66,50 @@ public CloseableHttpClient closeableHttpClient() {

@Bean
public AppConfigurationPropertySourceLocator sourceLocator(AppConfigurationProperties properties,
AppConfigurationProviderProperties appProperties, ClientStore clients, ApplicationContext context) {
AppConfigurationProviderProperties appProperties, ClientStore clients, ApplicationContext context,
Optional<KeyVaultCredentialProvider> keyVaultCredentialProviderOptional,
Optional<SecretClientBuilderSetup> keyVaultClientProviderOptional) {

KeyVaultCredentialProvider keyVaultCredentialProvider = null;
try {
keyVaultCredentialProvider = context.getBean(KeyVaultCredentialProvider.class);
} catch (NoUniqueBeanDefinitionException e) {
throw new RuntimeException("Failed to find unique KeyVaultCredentialProvider Bean for authentication.", e);
} catch (NoSuchBeanDefinitionException e) {
SecretClientBuilderSetup keyVaultClientProvider = null;

if (!keyVaultCredentialProviderOptional.isPresent()) {
LOGGER.debug("No KeyVaultCredentialProvider found.");
} else {
keyVaultCredentialProvider = keyVaultCredentialProviderOptional.get();
}

if (!keyVaultClientProviderOptional.isPresent()) {
LOGGER.debug("No KeyVaultCredentialProvider found.");
} else {
keyVaultClientProvider = keyVaultClientProviderOptional.get();
}

return new AppConfigurationPropertySourceLocator(properties, appProperties, clients,
keyVaultCredentialProvider);
keyVaultCredentialProvider, keyVaultClientProvider);
}

@Bean
public ClientStore buildClientStores(AppConfigurationProperties properties,
AppConfigurationProviderProperties appProperties, ConnectionPool pool, ApplicationContext context) {
AppConfigurationProviderProperties appProperties, ConnectionPool pool, ApplicationContext context,
Optional<AppConfigurationCredentialProvider> tokenCredentialProviderOptional,
Optional<ConfigurationClientBuilderSetup> clientProviderOptional) {

AppConfigurationCredentialProvider tokenCredentialProvider = null;
try {
tokenCredentialProvider = context.getBean(AppConfigurationCredentialProvider.class);
} catch (NoUniqueBeanDefinitionException e) {
throw new RuntimeException(
"Failed to find unique AppConfigurationCredentialProvider Bean for authentication.", e);
} catch (NoSuchBeanDefinitionException e) {
ConfigurationClientBuilderSetup clientProvider = null;

if (!tokenCredentialProviderOptional.isPresent()) {
LOGGER.debug("No AppConfigurationCredentialProvider found.");
} else {
tokenCredentialProvider = tokenCredentialProviderOptional.get();
}

if (!clientProviderOptional.isPresent()) {
LOGGER.debug("No AppConfigurationClientProvider found.");
} else {
clientProvider = clientProviderOptional.get();
}
return new ClientStore(appProperties, pool, tokenCredentialProvider);

return new ClientStore(appProperties, pool, tokenCredentialProvider, clientProvider);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,16 @@ public class AppConfigurationPropertySource extends EnumerablePropertySource<Con

private KeyVaultCredentialProvider keyVaultCredentialProvider;

private SecretClientBuilderSetup keyVaultClientProvider;

private AppConfigurationProviderProperties appProperties;

private ConfigStore configStore;

AppConfigurationPropertySource(String context, ConfigStore configStore, String label,
AppConfigurationProperties appConfigurationProperties, ClientStore clients,
AppConfigurationProviderProperties appProperties, KeyVaultCredentialProvider keyVaultCredentialProvider) {
AppConfigurationProviderProperties appProperties, KeyVaultCredentialProvider keyVaultCredentialProvider,
SecretClientBuilderSetup keyVaultClientProvider) {
// The context alone does not uniquely define a PropertySource, append storeName
// and label to uniquely define a PropertySource
super(context + configStore.getEndpoint() + "/" + label);
Expand All @@ -76,6 +79,7 @@ public class AppConfigurationPropertySource extends EnumerablePropertySource<Con
this.keyVaultClients = new HashMap<String, KeyVaultClient>();
this.clients = clients;
this.keyVaultCredentialProvider = keyVaultCredentialProvider;
this.keyVaultClientProvider = keyVaultClientProvider;
}

@Override
Expand Down Expand Up @@ -108,10 +112,7 @@ public Object getProperty(String name) {
FeatureSet initProperties(FeatureSet featureSet) throws IOException {
String storeName = configStore.getEndpoint();
Date date = new Date();
SettingSelector settingSelector = new SettingSelector();
if (!label.equals("%00")) {
settingSelector.setLabelFilter(label);
}
SettingSelector settingSelector = new SettingSelector().setLabelFilter(label);

// * for wildcard match
settingSelector.setKeyFilter(context + "*");
Expand Down Expand Up @@ -172,7 +173,8 @@ private String getKeyVaultEntry(String value) {
// Check if we already have a client for this key vault, if not we will make
// one
if (!keyVaultClients.containsKey(uri.getHost())) {
KeyVaultClient client = new KeyVaultClient(appConfigurationProperties, uri, keyVaultCredentialProvider);
KeyVaultClient client = new KeyVaultClient(appConfigurationProperties, uri, keyVaultCredentialProvider,
keyVaultClientProvider);
keyVaultClients.put(uri.getHost(), client);
}
KeyVaultSecret secret = keyVaultClients.get(uri.getHost()).getSecret(uri, appProperties.getMaxRetryTime());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,21 @@ public class AppConfigurationPropertySourceLocator implements PropertySourceLoca
private ClientStore clients;

private KeyVaultCredentialProvider keyVaultCredentialProvider;

private SecretClientBuilderSetup keyVaultClientProvider;

private static Boolean startup = true;

public AppConfigurationPropertySourceLocator(AppConfigurationProperties properties,
AppConfigurationProviderProperties appProperties, ClientStore clients,
KeyVaultCredentialProvider keyVaultCredentialProvider) {
KeyVaultCredentialProvider keyVaultCredentialProvider, SecretClientBuilderSetup keyVaultClientProvider) {
this.properties = properties;
this.appProperties = appProperties;
this.profileSeparator = properties.getProfileSeparator();
this.configStores = properties.getStores();
this.clients = clients;
this.keyVaultCredentialProvider = keyVaultCredentialProvider;
this.keyVaultClientProvider = keyVaultClientProvider;
}

@Override
Expand Down Expand Up @@ -137,28 +140,35 @@ private void addPropertySource(CompositePropertySource composite, ConfigStore st
// There is only one Feature Set for all AppConfigurationPropertySources
FeatureSet featureSet = new FeatureSet();

List<AppConfigurationPropertySource> sourceList = new ArrayList<AppConfigurationPropertySource>();

// Reverse in order to add Profile specific properties earlier, and last profile
// comes first
Collections.reverse(contexts);
for (String sourceContext : contexts) {
try {
List<AppConfigurationPropertySource> sourceList = create(sourceContext, store, storeContextsMap,
initFeatures, featureSet);
sourceList.forEach(composite::addPropertySource);
sourceList.addAll(create(sourceContext, store, storeContextsMap, initFeatures, featureSet));

LOGGER.debug("PropertySource context [{}] is added.", sourceContext);
} catch (Exception e) {
if (store.isFailFast() || !startup) {
LOGGER.error(
"Fail fast is set and there was an error reading configuration from Azure App "
+ "Configuration Service for " + sourceContext);
+ "Configuration store " + store.getEndpoint()
+ ". The configuration starting with " + sourceContext + " failed to load.");
ReflectionUtils.rethrowRuntimeException(e);
} else {
LOGGER.warn("Unable to load configuration from Azure AppConfiguration Service for " + sourceContext,
LOGGER.warn(
"Unable to load configuration from Azure AppConfiguration store " + store.getEndpoint()
+ ". The configurations starting with " + sourceContext + "failed to load.",
e);
StateHolder.setLoadState(store.getEndpoint(), false);
}
// If anything breaks we skip out on loading the rest of the store.
return;
}
}
sourceList.forEach(composite::addPropertySource);
}

private List<String> generateContexts(String applicationName, List<String> profiles, ConfigStore configStore) {
Expand Down Expand Up @@ -207,7 +217,7 @@ private List<AppConfigurationPropertySource> create(String context, ConfigStore
for (String label : store.getLabels()) {
putStoreContext(store.getEndpoint(), context, storeContextsMap);
AppConfigurationPropertySource propertySource = new AppConfigurationPropertySource(context, store,
label, properties, clients, appProperties, keyVaultCredentialProvider);
label, properties, clients, appProperties, keyVaultCredentialProvider, keyVaultClientProvider);

propertySource.initProperties(featureSet);
if (initFeatures) {
Expand All @@ -220,23 +230,22 @@ private List<AppConfigurationPropertySource> create(String context, ConfigStore
String watchedKeyNames = clients.watchedKeyNames(store, storeContextsMap);
SettingSelector settingSelector = new SettingSelector().setKeyFilter(watchedKeyNames).setLabelFilter("*");

List<ConfigurationSetting> configurationRevisions = clients.listSettingRevisons(settingSelector,
ConfigurationSetting configurationRevision = clients.getRevison(settingSelector,
store.getEndpoint());

settingSelector = new SettingSelector().setKeyFilter(FEATURE_STORE_WATCH_KEY).setLabelFilter("*");

List<ConfigurationSetting> featureRevisions = clients.listSettingRevisons(settingSelector,
ConfigurationSetting featureRevision = clients.getRevison(settingSelector,
store.getEndpoint());

if (configurationRevisions != null && !configurationRevisions.isEmpty()) {
StateHolder.setEtagState(store.getEndpoint() + CONFIGURATION_SUFFIX,
configurationRevisions.get(0));
if (configurationRevision != null) {
StateHolder.setEtagState(store.getEndpoint() + CONFIGURATION_SUFFIX, configurationRevision);
} else {
StateHolder.setEtagState(store.getEndpoint() + CONFIGURATION_SUFFIX, new ConfigurationSetting());
}

if (featureRevisions != null && !featureRevisions.isEmpty()) {
StateHolder.setEtagState(store.getEndpoint() + FEATURE_SUFFIX, featureRevisions.get(0));
if (featureRevision != null) {
StateHolder.setEtagState(store.getEndpoint() + FEATURE_SUFFIX, featureRevision);
} else {
StateHolder.setEtagState(store.getEndpoint() + FEATURE_SUFFIX, new ConfigurationSetting());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class AppConfigurationProviderProperties {
private String version;

@NotNull
@Value("${maxRetries:12}")
@Value("${maxRetries:2}")
private int maxRetries;

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ private boolean refreshStores() {
// Refresh Feature Flags
willRefresh = refresh(configStore, FEATURE_SUFFIX, FEATURE_STORE_WATCH_KEY) ? true
: willRefresh;
} else {
LOGGER.debug("Skipping refresh check for " + configStore.getEndpoint()
+ ". The store failed to load on startup.");
}
}
// Resetting last Checked date to now.
Expand All @@ -110,6 +113,11 @@ private boolean refreshStores() {
if (willRefresh) {
// Only one refresh Event needs to be call to update all of the
// stores, not one for each.
if (eventDataInfo.equals("*")) {
LOGGER.info("Configuration Refresh event triggered by store modification.");
} else {
LOGGER.info("Configuration Refresh Event triggered by " + eventDataInfo);
}
RefreshEventData eventData = new RefreshEventData(eventDataInfo);
publisher.publishEvent(new RefreshEvent(this, eventData, eventData.getMessage()));
}
Expand All @@ -134,18 +142,19 @@ private boolean refresh(ConfigStore store, String storeSuffix, String watchedKey
SettingSelector settingSelector = new SettingSelector().setKeyFilter(watchedKeyNames)
.setLabelFilter("*");

List<ConfigurationSetting> items = clientStore.listSettingRevisons(settingSelector, store.getEndpoint());
ConfigurationSetting revision = clientStore.getRevison(settingSelector, store.getEndpoint());

String etag = null;
// If there is no result, etag will be considered empty.
// A refresh will trigger once the selector returns a value.
if (items != null && !items.isEmpty()) {
etag = items.get(0).getETag();
if (revision != null) {
etag = revision.getETag();
}

if (StateHolder.getEtagState(storeNameWithSuffix) == null) {
// On startup there was no Configurations, but now there is.
if (etag != null) {
LOGGER.info("The store " + store.getEndpoint() + " had no keys on startup, but now has keys to load.");
return true;
}
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for
* license information.
*/
package com.microsoft.azure.spring.cloud.config;


import com.azure.data.appconfiguration.ConfigurationClientBuilder;

public interface ConfigurationClientBuilderSetup {

public void setup(ConfigurationClientBuilder builder, String endpoint);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for
* license information.
*/
package com.microsoft.azure.spring.cloud.config;

import com.azure.security.keyvault.secrets.SecretClientBuilder;

public interface SecretClientBuilderSetup {

public void setup(SecretClientBuilder builder, String uri);

}
Loading

0 comments on commit c0657a4

Please sign in to comment.