Skip to content

Commit 4b2f8c3

Browse files
authored
Turns Spring Profiles into App Configuration labels. (Azure#18577)
1 parent 72a3b53 commit 4b2f8c3

File tree

8 files changed

+82
-115
lines changed

8 files changed

+82
-115
lines changed

sdk/appconfiguration/azure-spring-cloud-appconfiguration-config/src/main/java/com/microsoft/azure/spring/cloud/config/AppConfigurationBootstrapConfiguration.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,9 @@
22
// Licensed under the MIT License.
33
package com.microsoft.azure.spring.cloud.config;
44

5-
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationProperties;
6-
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationProviderProperties;
7-
import com.microsoft.azure.spring.cloud.config.properties.ConfigStore;
8-
import com.microsoft.azure.spring.cloud.config.resource.AppConfigManagedIdentityProperties;
9-
import com.microsoft.azure.spring.cloud.config.resource.Connection;
10-
import com.microsoft.azure.spring.cloud.config.resource.ConnectionPool;
11-
import com.microsoft.azure.spring.cloud.config.stores.ClientStore;
125
import java.util.List;
136
import java.util.Optional;
14-
import org.apache.http.impl.client.CloseableHttpClient;
15-
import org.apache.http.impl.client.HttpClients;
7+
168
import org.slf4j.Logger;
179
import org.slf4j.LoggerFactory;
1810
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -24,6 +16,14 @@
2416
import org.springframework.util.Assert;
2517
import org.springframework.util.StringUtils;
2618

19+
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationProperties;
20+
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationProviderProperties;
21+
import com.microsoft.azure.spring.cloud.config.properties.ConfigStore;
22+
import com.microsoft.azure.spring.cloud.config.resource.AppConfigManagedIdentityProperties;
23+
import com.microsoft.azure.spring.cloud.config.resource.Connection;
24+
import com.microsoft.azure.spring.cloud.config.resource.ConnectionPool;
25+
import com.microsoft.azure.spring.cloud.config.stores.ClientStore;
26+
2727
@Configuration
2828
@EnableConfigurationProperties({AppConfigurationProperties.class, AppConfigurationProviderProperties.class})
2929
@ConditionalOnClass(AppConfigurationPropertySourceLocator.class)
@@ -56,11 +56,6 @@ public ConnectionPool initConnectionString(AppConfigurationProperties properties
5656
return pool;
5757
}
5858

59-
@Bean
60-
public CloseableHttpClient closeableHttpClient() {
61-
return HttpClients.createSystem();
62-
}
63-
6459
@Bean
6560
public AppConfigurationPropertySourceLocator sourceLocator(AppConfigurationProperties properties,
6661
AppConfigurationProviderProperties appProperties, ClientStore clients, ApplicationContext context,

sdk/appconfiguration/azure-spring-cloud-appconfiguration-config/src/main/java/com/microsoft/azure/spring/cloud/config/AppConfigurationPropertySourceLocator.java

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,6 @@
22
// Licensed under the MIT License.
33
package com.microsoft.azure.spring.cloud.config;
44

5-
import com.azure.data.appconfiguration.models.ConfigurationSetting;
6-
import com.azure.data.appconfiguration.models.SettingSelector;
7-
import com.microsoft.azure.spring.cloud.config.feature.management.entity.FeatureSet;
8-
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationProperties;
9-
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationProviderProperties;
10-
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationStoreTrigger;
11-
import com.microsoft.azure.spring.cloud.config.properties.ConfigStore;
12-
import com.microsoft.azure.spring.cloud.config.stores.ClientStore;
135
import java.util.ArrayList;
146
import java.util.Arrays;
157
import java.util.Collections;
@@ -19,6 +11,7 @@
1911
import java.util.Map;
2012
import java.util.concurrent.ConcurrentHashMap;
2113
import java.util.concurrent.atomic.AtomicBoolean;
14+
2215
import org.apache.commons.lang3.time.DateUtils;
2316
import org.slf4j.Logger;
2417
import org.slf4j.LoggerFactory;
@@ -31,31 +24,46 @@
3124
import org.springframework.util.ReflectionUtils;
3225
import org.springframework.util.StringUtils;
3326

27+
import com.azure.data.appconfiguration.models.ConfigurationSetting;
28+
import com.azure.data.appconfiguration.models.SettingSelector;
29+
import com.microsoft.azure.spring.cloud.config.feature.management.entity.FeatureSet;
30+
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationProperties;
31+
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationProviderProperties;
32+
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationStoreTrigger;
33+
import com.microsoft.azure.spring.cloud.config.properties.ConfigStore;
34+
import com.microsoft.azure.spring.cloud.config.stores.ClientStore;
35+
3436
public class AppConfigurationPropertySourceLocator implements PropertySourceLocator {
3537

3638
private static final Logger LOGGER = LoggerFactory.getLogger(AppConfigurationPropertySourceLocator.class);
3739

3840
private static final String SPRING_APP_NAME_PROP = "spring.application.name";
3941

4042
private static final String PROPERTY_SOURCE_NAME = "azure-config-store";
41-
43+
4244
private static final String PATH_SPLITTER = "/";
45+
4346
private final AppConfigurationProperties properties;
44-
private final String profileSeparator;
47+
4548
private final List<ConfigStore> configStores;
49+
4650
private final Map<String, List<String>> storeContextsMap = new ConcurrentHashMap<>();
51+
4752
private final AppConfigurationProviderProperties appProperties;
53+
4854
private final ClientStore clients;
55+
4956
private final KeyVaultCredentialProvider keyVaultCredentialProvider;
57+
5058
private final SecretClientBuilderSetup keyVaultClientProvider;
59+
5160
private static AtomicBoolean startup = new AtomicBoolean(true);
5261

5362
public AppConfigurationPropertySourceLocator(AppConfigurationProperties properties,
5463
AppConfigurationProviderProperties appProperties, ClientStore clients,
5564
KeyVaultCredentialProvider keyVaultCredentialProvider, SecretClientBuilderSetup keyVaultClientProvider) {
5665
this.properties = properties;
5766
this.appProperties = appProperties;
58-
this.profileSeparator = properties.getProfileSeparator();
5967
this.configStores = properties.getStores();
6068
this.clients = clients;
6169
this.keyVaultCredentialProvider = keyVaultCredentialProvider;
@@ -102,21 +110,20 @@ public Map<String, List<String>> getStoreContextsMap() {
102110
/**
103111
* Adds a new Property Source
104112
*
105-
* @param composite PropertySource being added
106-
* @param store Config Store the PropertySource is being generated from
107-
* @param applicationName Name of the application
108-
* @param profiles Active profiles in the Store
113+
* @param composite PropertySource being added
114+
* @param store Config Store the PropertySource is being generated from
115+
* @param applicationName Name of the application
116+
* @param profiles Active profiles in the Store
109117
* @param storeContextsMap the Map storing the storeName -> List of contexts map
110-
* @param initFeatures determines if Feature Management is set in the PropertySource. When generating more than
111-
* one it needs to be in the last one.
118+
* @param initFeatures determines if Feature Management is set in the PropertySource. When generating more than one
119+
* it needs to be in the last one.
112120
*/
113121
private void addPropertySource(CompositePropertySource composite, ConfigStore store, String applicationName,
114122
List<String> profiles, Map<String, List<String>> storeContextsMap, boolean initFeatures) {
115123
/*
116-
* Generate which contexts(key prefixes) will be used for key-value items search
117-
* If key prefix is empty, default context is: application, current application
118-
* name is: foo, active profile is: dev, profileSeparator is: _ Will generate
119-
* these contexts: /application/, /application_dev/, /foo/, /foo_dev/
124+
* Generate which contexts(key prefixes) will be used for key-value items search If key prefix is empty, default
125+
* context is: application, current application name is: foo, active profile is: dev, profileSeparator is: _
126+
* Will generate these contexts: /application/, /application_dev/, /foo/, /foo_dev/
120127
*/
121128
List<String> contexts = new ArrayList<>();
122129
contexts.addAll(generateContexts(this.properties.getDefaultContext(), profiles, store));
@@ -132,7 +139,7 @@ private void addPropertySource(CompositePropertySource composite, ConfigStore st
132139
Collections.reverse(contexts);
133140
for (String sourceContext : contexts) {
134141
try {
135-
sourceList.addAll(create(sourceContext, store, storeContextsMap, initFeatures, featureSet));
142+
sourceList.addAll(create(sourceContext, store, storeContextsMap, profiles, initFeatures, featureSet));
136143

137144
LOGGER.debug("PropertySource context [{}] is added.", sourceContext);
138145
} catch (Exception e) {
@@ -166,7 +173,6 @@ private List<String> generateContexts(String applicationName, List<String> profi
166173

167174
String prefixedContext = propWithAppName(prefix, applicationName);
168175
result.add(prefixedContext + PATH_SPLITTER);
169-
profiles.forEach(profile -> result.add(propWithProfile(prefixedContext, profile)));
170176

171177
return result;
172178
}
@@ -180,26 +186,25 @@ private String propWithAppName(String prefix, String applicationName) {
180186
return PATH_SPLITTER + applicationName;
181187
}
182188

183-
private String propWithProfile(String context, String profile) {
184-
return context + this.profileSeparator + profile + PATH_SPLITTER;
185-
}
186-
187189
/**
188190
* Creates a new set of AppConfigurationProertySources, 1 per Label.
189191
*
190-
* @param context Context of the application, part of uniquely define a PropertySource
191-
* @param store Config Store the PropertySource is being generated from
192+
* @param context Context of the application, part of uniquely define a PropertySource
193+
* @param store Config Store the PropertySource is being generated from
192194
* @param storeContextsMap the Map storing the storeName -> List of contexts map
193-
* @param initFeatures determines if Feature Management is set in the PropertySource. When generating more than
194-
* one it needs to be in the last one.
195+
* @param initFeatures determines if Feature Management is set in the PropertySource. When generating more than one
196+
* it needs to be in the last one.
195197
* @return a list of AppConfigurationPropertySources
196198
*/
197199
private List<AppConfigurationPropertySource> create(String context, ConfigStore store,
198-
Map<String, List<String>> storeContextsMap, boolean initFeatures, FeatureSet featureSet) throws Exception {
200+
Map<String, List<String>> storeContextsMap, List<String> profiles, boolean initFeatures, FeatureSet featureSet)
201+
throws Exception {
199202
List<AppConfigurationPropertySource> sourceList = new ArrayList<>();
200203

201204
try {
202-
for (String label : store.getLabels()) {
205+
String[] labels = store.getLabels(profiles);
206+
207+
for (String label : labels) {
203208
putStoreContext(store.getEndpoint(), context, storeContextsMap);
204209
AppConfigurationPropertySource propertySource = new AppConfigurationPropertySource(context, store,
205210
label, properties, clients, appProperties, keyVaultCredentialProvider, keyVaultClientProvider);
@@ -234,8 +239,8 @@ private List<AppConfigurationPropertySource> create(String context, ConfigStore
234239
/**
235240
* Put certain context to the store contexts map
236241
*
237-
* @param storeName the name of the configuration store
238-
* @param context the context text for the PropertySource, e.g., "/application"
242+
* @param storeName the name of the configuration store
243+
* @param context the context text for the PropertySource, e.g., "/application"
239244
* @param storeContextsMap the Map storing the storeName -> List of contexts map
240245
*/
241246
private void putStoreContext(String storeName, String context,

sdk/appconfiguration/azure-spring-cloud-appconfiguration-config/src/main/java/com/microsoft/azure/spring/cloud/config/properties/AppConfigurationProperties.java

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
// Licensed under the MIT License.
33
package com.microsoft.azure.spring.cloud.config.properties;
44

5-
import com.microsoft.azure.spring.cloud.config.resource.AppConfigManagedIdentityProperties;
65
import java.util.ArrayList;
76
import java.util.List;
87
import java.util.stream.Collectors;
8+
99
import javax.annotation.PostConstruct;
1010
import javax.validation.constraints.NotEmpty;
11-
import javax.validation.constraints.Pattern;
11+
1212
import org.springframework.boot.context.properties.ConfigurationProperties;
1313
import org.springframework.boot.context.properties.NestedConfigurationProperty;
1414
import org.springframework.context.annotation.Import;
@@ -17,9 +17,11 @@
1717
import org.springframework.util.StringUtils;
1818
import org.springframework.validation.annotation.Validated;
1919

20+
import com.microsoft.azure.spring.cloud.config.resource.AppConfigManagedIdentityProperties;
21+
2022
@Validated
2123
@ConfigurationProperties(prefix = AppConfigurationProperties.CONFIG_PREFIX)
22-
@Import({AppConfigurationProviderProperties.class})
24+
@Import({ AppConfigurationProviderProperties.class })
2325
public class AppConfigurationProperties {
2426

2527
public static final String CONFIG_PREFIX = "spring.cloud.azure.appconfiguration";
@@ -41,11 +43,6 @@ public class AppConfigurationProperties {
4143
@NestedConfigurationProperty
4244
private AppConfigManagedIdentityProperties managedIdentity;
4345

44-
// Profile separator for the key name, e.g., /foo-app_dev/db.connection.key
45-
@NotEmpty
46-
@Pattern(regexp = "^[a-zA-Z0-9_@]+$")
47-
private String profileSeparator = "_";
48-
4946
private boolean pushRefresh = true;
5047

5148
public boolean isEnabled() {
@@ -89,14 +86,6 @@ public void setManagedIdentity(AppConfigManagedIdentityProperties managedIdentit
8986
this.managedIdentity = managedIdentity;
9087
}
9188

92-
public String getProfileSeparator() {
93-
return profileSeparator;
94-
}
95-
96-
public void setProfileSeparator(String profileSeparator) {
97-
this.profileSeparator = profileSeparator;
98-
}
99-
10089
/**
10190
* @return the pushRefresh
10291
*/
@@ -118,8 +107,7 @@ public void validateAndInit() {
118107
this.stores.forEach(store -> {
119108
Assert.isTrue(
120109
StringUtils.hasText(store.getEndpoint()) || StringUtils.hasText(store.getConnectionString()),
121-
"Either configuration store name or connection string should be configured."
122-
);
110+
"Either configuration store name or connection string should be configured.");
123111
store.validateAndInit();
124112
});
125113

sdk/appconfiguration/azure-spring-cloud-appconfiguration-config/src/main/java/com/microsoft/azure/spring/cloud/config/properties/ConfigStore.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,13 @@ public void validateAndInit() {
131131
}
132132

133133
/**
134+
* @param profiles List of current Spring profiles to default to using is null label is set.
134135
* @return List of reversed label values, which are split by the separator, the latter label has higher priority
135136
*/
136-
public String[] getLabels() {
137-
if (!StringUtils.hasText(this.getLabel())) {
137+
public String[] getLabels(List<String> profiles) {
138+
if (this.getLabel() == null && profiles.size() > 0) {
139+
return profiles.toArray(new String[profiles.size()]);
140+
} else if (!StringUtils.hasText(this.getLabel())) {
138141
return EMPTY_LABEL_ARRAY;
139142
}
140143

sdk/appconfiguration/azure-spring-cloud-appconfiguration-config/src/test/java/com/microsoft/azure/spring/cloud/config/AppConfigurationPropertiesTest.java

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import static com.microsoft.azure.spring.cloud.config.TestConstants.FAIL_FAST_PROP;
1010
import static com.microsoft.azure.spring.cloud.config.TestConstants.LABEL_PROP;
1111
import static com.microsoft.azure.spring.cloud.config.TestConstants.PREFIX_PROP;
12-
import static com.microsoft.azure.spring.cloud.config.TestConstants.SEPARATOR_PROP;
1312
import static com.microsoft.azure.spring.cloud.config.TestConstants.STORE_ENDPOINT_PROP;
1413
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_CONN_STRING;
1514
import static com.microsoft.azure.spring.cloud.config.TestUtils.propPair;
@@ -18,10 +17,9 @@
1817
import static org.junit.Assert.fail;
1918
import static org.mockito.Mockito.when;
2019

21-
import com.fasterxml.jackson.databind.ObjectMapper;
22-
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationProperties;
2320
import java.io.InputStream;
2421
import java.util.Arrays;
22+
2523
import org.apache.http.HttpEntity;
2624
import org.apache.http.ProtocolVersion;
2725
import org.apache.http.RequestLine;
@@ -40,13 +38,15 @@
4038
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
4139
import org.springframework.context.ApplicationContext;
4240

41+
import com.fasterxml.jackson.databind.ObjectMapper;
42+
import com.microsoft.azure.spring.cloud.config.properties.AppConfigurationProperties;
43+
4344
public class AppConfigurationPropertiesTest {
4445

4546
private static final String NO_ENDPOINT_CONN_STRING = "Id=fake-conn-id;Secret=ZmFrZS1jb25uLXNlY3JldA==";
4647
private static final String NO_ID_CONN_STRING = "Endpoint=https://fake.test.config.io;Secret=ZmFrZS1jb25uLXNlY3JldA==";
4748
private static final String NO_SECRET_CONN_STRING = "Endpoint=https://fake.test.config.io;Id=fake-conn-id;";
4849
private static final String[] ILLEGAL_PREFIXES = {"/ config", "config"};
49-
private static final String[] ILLEGAL_PROFILE_SEPARATOR = {"/", "\\", "."};
5050
private static final String ILLEGAL_LABELS = "*,my-label";
5151
@InjectMocks
5252
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
@@ -139,18 +139,6 @@ public void prefixShouldFollowPattern() {
139139
});
140140
}
141141

142-
@Test
143-
public void profileSeparatorShouldFollowPattern() {
144-
Arrays.asList(ILLEGAL_PROFILE_SEPARATOR).stream().forEach(separator -> {
145-
this.contextRunner
146-
.withPropertyValues(
147-
propPair(CONN_STRING_PROP, TEST_CONN_STRING),
148-
propPair(SEPARATOR_PROP, separator)
149-
)
150-
.run(context -> assertInvalidField(context, "profileSeparator"));
151-
});
152-
}
153-
154142
@Test
155143
public void asteriskShouldNotBeIncludedInTheLabels() {
156144
this.contextRunner

0 commit comments

Comments
 (0)