Skip to content

Commit 6372626

Browse files
authored
Merge pull request #654 from mrm9084/1.1.xAppConfigurationUpdate
1.1.x app configuration update
2 parents 66be0d4 + c3347ff commit 6372626

File tree

13 files changed

+311
-117
lines changed

13 files changed

+311
-117
lines changed

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

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@
55
*/
66
package com.microsoft.azure.spring.cloud.config;
77

8-
import static com.microsoft.azure.spring.cloud.config.Constants.CONFIGURATION_SUFFIX;
98
import static com.microsoft.azure.spring.cloud.config.Constants.FEATURE_FLAG_CONTENT_TYPE;
109
import static com.microsoft.azure.spring.cloud.config.Constants.FEATURE_FLAG_PREFIX;
1110
import static com.microsoft.azure.spring.cloud.config.Constants.FEATURE_MANAGEMENT_KEY;
12-
import static com.microsoft.azure.spring.cloud.config.Constants.FEATURE_STORE_WATCH_KEY;
13-
import static com.microsoft.azure.spring.cloud.config.Constants.FEATURE_SUFFIX;
1411
import static com.microsoft.azure.spring.cloud.config.Constants.KEY_VAULT_CONTENT_TYPE;
1512

1613
import java.io.IOException;
@@ -27,7 +24,6 @@
2724
import org.slf4j.LoggerFactory;
2825
import org.springframework.core.env.EnumerablePropertySource;
2926
import org.springframework.util.ReflectionUtils;
30-
import org.springframework.util.StringUtils;
3127

3228
import com.azure.data.appconfiguration.ConfigurationClient;
3329
import com.azure.data.appconfiguration.models.ConfigurationSetting;
@@ -66,12 +62,9 @@ public class AppConfigurationPropertySource extends EnumerablePropertySource<Con
6662

6763
private ConfigStore configStore;
6864

69-
private Map<String, List<String>> storeContextsMap;
70-
7165
AppConfigurationPropertySource(String context, ConfigStore configStore, String label,
7266
AppConfigurationProperties appConfigurationProperties, ClientStore clients,
73-
AppConfigurationProviderProperties appProperties, KeyVaultCredentialProvider keyVaultCredentialProvider,
74-
Map<String, List<String>> storeContextsMap) {
67+
AppConfigurationProviderProperties appProperties, KeyVaultCredentialProvider keyVaultCredentialProvider) {
7568
// The context alone does not uniquely define a PropertySource, append storeName
7669
// and label to uniquely define a PropertySource
7770
super(context + configStore.getEndpoint() + "/" + label);
@@ -83,7 +76,6 @@ public class AppConfigurationPropertySource extends EnumerablePropertySource<Con
8376
this.keyVaultClients = new HashMap<String, KeyVaultClient>();
8477
this.clients = clients;
8578
this.keyVaultCredentialProvider = keyVaultCredentialProvider;
86-
this.storeContextsMap = storeContextsMap;
8779
}
8880

8981
@Override
@@ -146,34 +138,7 @@ FeatureSet initProperties(FeatureSet featureSet) throws IOException {
146138
}
147139
}
148140

149-
featureSet = addToFeatureSet(featureSet, features, date);
150-
151-
// Setting new ETag values for Watch
152-
String watchedKeyNames = clients.watchedKeyNames(configStore, storeContextsMap);
153-
settingSelector = new SettingSelector().setKeyFilter(watchedKeyNames)
154-
.setLabelFilter(StringUtils.arrayToCommaDelimitedString(configStore.getLabels()));
155-
156-
List<ConfigurationSetting> configurationRevisions = clients.listSettingRevisons(settingSelector, storeName);
157-
158-
settingSelector = new SettingSelector().setKeyFilter(FEATURE_STORE_WATCH_KEY)
159-
.setLabelFilter(StringUtils.arrayToCommaDelimitedString(configStore.getLabels()));
160-
161-
List<ConfigurationSetting> featureRevisions = clients.listSettingRevisons(settingSelector, storeName);
162-
163-
if (configurationRevisions != null && !configurationRevisions.isEmpty()) {
164-
StateHolder.setEtagState(configStore.getEndpoint() + CONFIGURATION_SUFFIX, configurationRevisions.get(0));
165-
} else {
166-
StateHolder.setEtagState(configStore.getEndpoint() + CONFIGURATION_SUFFIX, new ConfigurationSetting());
167-
}
168-
169-
if (featureRevisions != null && !featureRevisions.isEmpty()) {
170-
StateHolder.setEtagState(configStore.getEndpoint() + FEATURE_SUFFIX, featureRevisions.get(0));
171-
} else {
172-
StateHolder.setEtagState(configStore.getEndpoint() + FEATURE_SUFFIX, new ConfigurationSetting());
173-
}
174-
StateHolder.setLoadState(configStore.getEndpoint(), true);
175-
176-
return featureSet;
141+
return addToFeatureSet(featureSet, features, date);
177142
}
178143

179144
/**

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

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
*/
66
package com.microsoft.azure.spring.cloud.config;
77

8+
import static com.microsoft.azure.spring.cloud.config.Constants.CONFIGURATION_SUFFIX;
9+
import static com.microsoft.azure.spring.cloud.config.Constants.FEATURE_STORE_WATCH_KEY;
10+
import static com.microsoft.azure.spring.cloud.config.Constants.FEATURE_SUFFIX;
11+
812
import java.util.ArrayList;
913
import java.util.Arrays;
1014
import java.util.Collections;
@@ -26,6 +30,8 @@
2630
import org.springframework.util.ReflectionUtils;
2731
import org.springframework.util.StringUtils;
2832

33+
import com.azure.data.appconfiguration.models.ConfigurationSetting;
34+
import com.azure.data.appconfiguration.models.SettingSelector;
2935
import com.microsoft.azure.spring.cloud.config.feature.management.entity.FeatureSet;
3036
import com.microsoft.azure.spring.cloud.config.stores.ClientStore;
3137
import com.microsoft.azure.spring.cloud.config.stores.ConfigStore;
@@ -96,7 +102,7 @@ public PropertySource<?> locate(Environment environment) {
96102
}
97103
}
98104

99-
startup = false;
105+
startup = false;
100106

101107
return composite;
102108
}
@@ -201,14 +207,40 @@ private List<AppConfigurationPropertySource> create(String context, ConfigStore
201207
for (String label : store.getLabels()) {
202208
putStoreContext(store.getEndpoint(), context, storeContextsMap);
203209
AppConfigurationPropertySource propertySource = new AppConfigurationPropertySource(context, store,
204-
label, properties, clients, appProperties, keyVaultCredentialProvider, storeContextsMap);
210+
label, properties, clients, appProperties, keyVaultCredentialProvider);
205211

206212
propertySource.initProperties(featureSet);
207213
if (initFeatures) {
208214
propertySource.initFeatures(featureSet);
209215
}
210216
sourceList.add(propertySource);
211217
}
218+
219+
// Setting new ETag values for Watch
220+
String watchedKeyNames = clients.watchedKeyNames(store, storeContextsMap);
221+
SettingSelector settingSelector = new SettingSelector().setKeyFilter(watchedKeyNames).setLabelFilter("*");
222+
223+
List<ConfigurationSetting> configurationRevisions = clients.listSettingRevisons(settingSelector,
224+
store.getEndpoint());
225+
226+
settingSelector = new SettingSelector().setKeyFilter(FEATURE_STORE_WATCH_KEY).setLabelFilter("*");
227+
228+
List<ConfigurationSetting> featureRevisions = clients.listSettingRevisons(settingSelector,
229+
store.getEndpoint());
230+
231+
if (configurationRevisions != null && !configurationRevisions.isEmpty()) {
232+
StateHolder.setEtagState(store.getEndpoint() + CONFIGURATION_SUFFIX,
233+
configurationRevisions.get(0));
234+
} else {
235+
StateHolder.setEtagState(store.getEndpoint() + CONFIGURATION_SUFFIX, new ConfigurationSetting());
236+
}
237+
238+
if (featureRevisions != null && !featureRevisions.isEmpty()) {
239+
StateHolder.setEtagState(store.getEndpoint() + FEATURE_SUFFIX, featureRevisions.get(0));
240+
} else {
241+
StateHolder.setEtagState(store.getEndpoint() + FEATURE_SUFFIX, new ConfigurationSetting());
242+
}
243+
StateHolder.setLoadState(store.getEndpoint(), true);
212244
} catch (Exception e) {
213245
delayException();
214246
throw e;

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

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.springframework.cloud.endpoint.event.RefreshEvent;
2424
import org.springframework.context.ApplicationEventPublisher;
2525
import org.springframework.context.ApplicationEventPublisherAware;
26-
import org.springframework.util.StringUtils;
2726

2827
import com.azure.data.appconfiguration.models.ConfigurationSetting;
2928
import com.azure.data.appconfiguration.models.SettingSelector;
@@ -133,27 +132,26 @@ private boolean refreshStores() {
133132
private boolean refresh(ConfigStore store, String storeSuffix, String watchedKeyNames) {
134133
String storeNameWithSuffix = store.getEndpoint() + storeSuffix;
135134
SettingSelector settingSelector = new SettingSelector().setKeyFilter(watchedKeyNames)
136-
.setLabelFilter(StringUtils.arrayToCommaDelimitedString(store.getLabels()));
135+
.setLabelFilter("*");
137136

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

140-
String etag = "";
139+
String etag = null;
141140
// If there is no result, etag will be considered empty.
142141
// A refresh will trigger once the selector returns a value.
143142
if (items != null && !items.isEmpty()) {
144143
etag = items.get(0).getETag();
145144
}
146145

147146
if (StateHolder.getEtagState(storeNameWithSuffix) == null) {
148-
// Should never be the case as Property Source should set the state, but if
149-
// etag != null return true.
147+
// On startup there was no Configurations, but now there is.
150148
if (etag != null) {
151149
return true;
152150
}
153151
return false;
154152
}
155153

156-
if (!etag.equals(StateHolder.getEtagState(storeNameWithSuffix).getETag())) {
154+
if (etag != null && !etag.equals(StateHolder.getEtagState(storeNameWithSuffix).getETag())) {
157155
LOGGER.trace("Some keys in store [{}] matching [{}] is updated, will send refresh event.",
158156
store.getEndpoint(), watchedKeyNames);
159157
if (this.eventDataInfo.isEmpty()) {

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

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@
3535

3636
public class AppConfigurationBootstrapConfigurationTest {
3737
private static final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
38-
.withPropertyValues(propPair(CONN_STRING_PROP, TEST_CONN_STRING),
39-
propPair(STORE_ENDPOINT_PROP, TEST_STORE_NAME))
38+
.withPropertyValues(propPair(STORE_ENDPOINT_PROP, TEST_STORE_NAME))
4039
.withConfiguration(AutoConfigurations.of(AppConfigurationBootstrapConfiguration.class));
41-
40+
4241
@Mock
4342
private CloseableHttpResponse mockClosableHttpResponse;
4443

@@ -50,7 +49,7 @@ public class AppConfigurationBootstrapConfigurationTest {
5049

5150
@Mock
5251
ObjectMapper mockObjectMapper;
53-
52+
5453
@Mock
5554
ClientStore clientStoreMock;
5655

@@ -69,16 +68,34 @@ public void setup() {
6968
}
7069

7170
@Test
72-
public void propertySourceLocatorBeanCreated() throws Exception {
71+
public void iniConnectionStringSystemAssigned() throws Exception {
7372
whenNew(ClientStore.class).withAnyArguments().thenReturn(clientStoreMock);
7473
contextRunner.withPropertyValues(propPair(FAIL_FAST_PROP, "false"))
7574
.run(context -> assertThat(context).hasSingleBean(AppConfigurationPropertySourceLocator.class));
7675
}
7776

77+
@Test
78+
public void iniConnectionStringUserAssigned() throws Exception {
79+
whenNew(ClientStore.class).withAnyArguments().thenReturn(clientStoreMock);
80+
contextRunner
81+
.withPropertyValues(propPair(FAIL_FAST_PROP, "false"),
82+
propPair("spring.cloud.azure.appconfiguration.managed-identity.client-id", "client-id"))
83+
.run(context -> assertThat(context).hasSingleBean(AppConfigurationPropertySourceLocator.class));
84+
}
85+
86+
@Test
87+
public void propertySourceLocatorBeanCreated() throws Exception {
88+
whenNew(ClientStore.class).withAnyArguments().thenReturn(clientStoreMock);
89+
contextRunner
90+
.withPropertyValues(propPair(CONN_STRING_PROP, TEST_CONN_STRING), propPair(FAIL_FAST_PROP, "false"))
91+
.run(context -> assertThat(context).hasSingleBean(AppConfigurationPropertySourceLocator.class));
92+
}
93+
7894
@Test
7995
public void clientsBeanCreated() throws Exception {
8096
whenNew(ClientStore.class).withAnyArguments().thenReturn(clientStoreMock);
81-
contextRunner.withPropertyValues(propPair(FAIL_FAST_PROP, "false"))
97+
contextRunner
98+
.withPropertyValues(propPair(CONN_STRING_PROP, TEST_CONN_STRING), propPair(FAIL_FAST_PROP, "false"))
8299
.run(context -> {
83100
assertThat(context).hasSingleBean(ClientStore.class);
84101
});

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@
3030
import java.io.IOException;
3131
import java.net.URI;
3232
import java.util.ArrayList;
33-
import java.util.HashMap;
3433
import java.util.Iterator;
3534
import java.util.List;
36-
import java.util.Map;
3735

3836
import org.junit.Before;
3937
import org.junit.BeforeClass;
@@ -132,12 +130,10 @@ public void setup() {
132130
appProperties.setMaxRetryTime(0);
133131
ConfigStore testStore = new ConfigStore();
134132
testStore.setEndpoint(TEST_STORE_NAME);
135-
Map<String, List<String>> storeContextsMap = new HashMap<String, List<String>>();
136133
ArrayList<String> contexts = new ArrayList<String>();
137134
contexts.add("/application/*");
138-
storeContextsMap.put(TEST_STORE_NAME, contexts);
139135
propertySource = new AppConfigurationPropertySource(TEST_CONTEXT, testStore, "\0",
140-
appConfigurationProperties, clientStoreMock, appProperties, tokenCredentialProvider, storeContextsMap);
136+
appConfigurationProperties, clientStoreMock, appProperties, tokenCredentialProvider);
141137

142138
testItems = new ArrayList<ConfigurationSetting>();
143139
testItems.add(item1);

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,19 @@
1010
import static com.microsoft.azure.spring.cloud.config.TestConstants.FEATURE_VALUE;
1111
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_CONN_STRING;
1212
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_CONN_STRING_2;
13+
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_CONTEXT;
14+
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_KEY_1;
15+
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_KEY_2;
16+
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_KEY_3;
17+
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_LABEL_1;
18+
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_LABEL_2;
19+
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_LABEL_3;
1320
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_STORE_NAME;
1421
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_STORE_NAME_1;
1522
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_STORE_NAME_2;
23+
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_VALUE_1;
24+
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_VALUE_2;
25+
import static com.microsoft.azure.spring.cloud.config.TestConstants.TEST_VALUE_3;
1626
import static com.microsoft.azure.spring.cloud.config.TestUtils.createItem;
1727
import static org.assertj.core.api.Assertions.assertThat;
1828
import static org.junit.Assert.assertTrue;
@@ -115,6 +125,19 @@ public class AppConfigurationPropertySourceLocatorTest {
115125
private AppConfigurationProviderProperties appProperties;
116126

117127
private KeyVaultCredentialProvider tokenCredentialProvider = null;
128+
129+
private static final String EMPTY_CONTENT_TYPE = "";
130+
131+
public List<ConfigurationSetting> testItems = new ArrayList<>();
132+
133+
private static final ConfigurationSetting item1 = createItem(TEST_CONTEXT, TEST_KEY_1, TEST_VALUE_1, TEST_LABEL_1,
134+
EMPTY_CONTENT_TYPE);
135+
136+
private static final ConfigurationSetting item2 = createItem(TEST_CONTEXT, TEST_KEY_2, TEST_VALUE_2, TEST_LABEL_2,
137+
EMPTY_CONTENT_TYPE);
138+
139+
private static final ConfigurationSetting item3 = createItem(TEST_CONTEXT, TEST_KEY_3, TEST_VALUE_3, TEST_LABEL_3,
140+
EMPTY_CONTENT_TYPE);
118141

119142
@BeforeClass
120143
public static void init() {
@@ -149,6 +172,11 @@ public void setup() {
149172
appProperties.setVersion("1.0");
150173
appProperties.setMaxRetries(12);
151174
appProperties.setMaxRetryTime(0);
175+
176+
testItems = new ArrayList<ConfigurationSetting>();
177+
testItems.add(item1);
178+
testItems.add(item2);
179+
testItems.add(item3);
152180
}
153181

154182
@After
@@ -182,6 +210,31 @@ public void compositeSourceIsCreated() {
182210
assertThat(sources.size()).isEqualTo(6);
183211
assertThat(sources.stream().map(s -> s.getName()).toArray()).containsExactly(expectedSourceNames);
184212
}
213+
214+
@Test
215+
public void revisionsCheck() {
216+
String[] labels = new String[1];
217+
labels[0] = "\0";
218+
when(configStoreMock.getLabels()).thenReturn(labels);
219+
when(properties.getDefaultContext()).thenReturn("application");
220+
when(clientStoreMock.listSettingRevisons(Mockito.any(), Mockito.anyString())).thenReturn(testItems)
221+
.thenReturn(FEATURE_ITEMS);
222+
223+
locator = new AppConfigurationPropertySourceLocator(properties, appProperties, clientStoreMock,
224+
tokenCredentialProvider);
225+
PropertySource<?> source = locator.locate(environment);
226+
assertThat(source).isInstanceOf(CompositePropertySource.class);
227+
228+
Collection<PropertySource<?>> sources = ((CompositePropertySource) source).getPropertySources();
229+
// Application name: foo and active profile: dev,prod, should construct below
230+
// composite Property Source:
231+
// [/foo_prod/, /foo_dev/, /foo/, /application_prod/, /application_dev/,
232+
// /application/]
233+
String[] expectedSourceNames = new String[] { "/foo_prod/store1/\0", "/foo_dev/store1/\0", "/foo/store1/\0",
234+
"/application_prod/store1/\0", "/application_dev/store1/\0", "/application/store1/\0" };
235+
assertThat(sources.size()).isEqualTo(6);
236+
assertThat(sources.stream().map(s -> s.getName()).toArray()).containsExactly(expectedSourceNames);
237+
}
185238

186239
@Test
187240
public void compositeSourceIsCreatedForPrefixedConfig() {

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,10 @@ public void setup() {
157157
appProperties = new AppConfigurationProviderProperties();
158158
ConfigStore configStore = new ConfigStore();
159159
configStore.setEndpoint(TEST_STORE_NAME);
160-
Map<String, List<String>> storeContextsMap = new HashMap<String, List<String>>();
161160
ArrayList<String> contexts = new ArrayList<String>();
162161
contexts.add("/application/*");
163-
storeContextsMap.put(TEST_STORE_NAME, contexts);
164162
propertySource = new AppConfigurationPropertySource(TEST_CONTEXT, configStore, "\0",
165-
appConfigurationProperties, clientStoreMock, appProperties, tokenCredentialProvider, storeContextsMap);
163+
appConfigurationProperties, clientStoreMock, appProperties, tokenCredentialProvider);
166164

167165
testItems = new ArrayList<ConfigurationSetting>();
168166
testItems.add(item1);
@@ -181,8 +179,7 @@ public void setup() {
181179
public void testPropCanBeInitAndQueried() throws IOException {
182180
when(clientStoreMock.listSettings(Mockito.any(), Mockito.anyString())).thenReturn(testItems)
183181
.thenReturn(FEATURE_ITEMS);
184-
when(clientStoreMock.listSettingRevisons(Mockito.any(), Mockito.anyString())).thenReturn(testItems)
185-
.thenReturn(FEATURE_ITEMS);
182+
186183
FeatureSet featureSet = new FeatureSet();
187184
try {
188185
propertySource.initProperties(featureSet);

0 commit comments

Comments
 (0)