Skip to content

Commit

Permalink
Snapshots Spring 3 (#36723)
Browse files Browse the repository at this point in the history
* Snapshots

* Updating witch change log and undoing a few changes in main library for build

* Update pom.xml

* reverting change

* more reverts

* other reverts

* Update ConfigurationClientTest.java
  • Loading branch information
mrm9084 authored Sep 15, 2023
1 parent 4854c32 commit 1a70f74
Show file tree
Hide file tree
Showing 25 changed files with 719 additions and 227 deletions.
1 change: 1 addition & 0 deletions eng/versioning/version_client.txt
Original file line number Diff line number Diff line change
Expand Up @@ -445,5 +445,6 @@ unreleased_com.azure:azure-core-test;1.20.0-beta.1
beta_com.azure:azure-communication-common;1.3.0-beta.1
beta_com.azure:azure-core-http-netty;1.14.0-beta.1
beta_com.azure:azure-core;1.42.0-beta.1
beta_com.azure:azure-data-appconfiguration;1.5.0-beta.1


Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
# Release History

## 5.6.0-beta.1 (Unreleased)
## 5.6.0-beta.1 (2023-09-11)

### Features Added

### Breaking Changes
### Features Added

### Bugs Fixed

### Other Changes
* Snapshot support using, `spring.cloud.azure.appconfiguration.stores[0].selects[0].snapshot-name`.
* Support for trimming prefixes from keys, default value is the key-filter when key-filter is used. `spring.cloud.azure.appconfiguration.stores[0].trim-key-prefix`

## 5.5.0 (2023-08-28)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
# Release History

## 5.6.0-beta.1 (Unreleased)
## 5.6.0-beta.1 (2023-09-11)

### Features Added

### Breaking Changes
### Features Added

### Bugs Fixed

### Other Changes
* Snapshot support using, `spring.cloud.azure.appconfiguration.stores[0].selects[0].snapshot-name`.
* Support for trimming prefixes from keys, default value is the key-filter when key-filter is used. `spring.cloud.azure.appconfiguration.stores[0].trim-key-prefix`

## 5.5.0 (2023-08-28)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-data-appconfiguration</artifactId>
<version>1.4.8</version> <!-- {x-version-update;com.azure:azure-data-appconfiguration;dependency} -->
<version>1.5.0-beta.1</version> <!-- {x-version-update;beta_com.azure:azure-data-appconfiguration;dependency} -->
</dependency>
<dependency>
<groupId>com.azure</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
// Licensed under the MIT License.
package com.azure.spring.cloud.appconfiguration.config.implementation;

import static com.azure.spring.cloud.appconfiguration.config.implementation.AppConfigurationConstants.FEATURE_FLAG_CONTENT_TYPE;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
Expand All @@ -17,6 +20,7 @@
import org.springframework.util.StringUtils;

import com.azure.data.appconfiguration.models.ConfigurationSetting;
import com.azure.data.appconfiguration.models.FeatureFlagConfigurationSetting;
import com.azure.data.appconfiguration.models.SecretReferenceConfigurationSetting;
import com.azure.data.appconfiguration.models.SettingSelector;
import com.azure.security.keyvault.secrets.models.KeyVaultSecret;
Expand All @@ -26,102 +30,131 @@
* Azure App Configuration PropertySource unique per Store Label(Profile) combo.
*
* <p>
* i.e. If connecting to 2 stores and have 2 labels set 4
* AppConfigurationPropertySources need to be created.
* i.e. If connecting to 2 stores and have 2 labels set 4 AppConfigurationPropertySources need to be created.
* </p>
*/
final class AppConfigurationApplicationSettingPropertySource extends AppConfigurationPropertySource {
class AppConfigurationApplicationSettingPropertySource extends AppConfigurationPropertySource {

private static final Logger LOGGER = LoggerFactory
.getLogger(AppConfigurationApplicationSettingPropertySource.class);
.getLogger(AppConfigurationApplicationSettingPropertySource.class);

private final AppConfigurationKeyVaultClientFactory keyVaultClientFactory;

private final int maxRetryTime;
private final String keyFilter;

private final String[] labelFilters;

AppConfigurationApplicationSettingPropertySource(String originEndpoint, AppConfigurationReplicaClient replicaClient,
AppConfigurationKeyVaultClientFactory keyVaultClientFactory, String keyFilter, String[] labelFilter,
int maxRetryTime) {
AppConfigurationApplicationSettingPropertySource(String name, AppConfigurationReplicaClient replicaClient,
AppConfigurationKeyVaultClientFactory keyVaultClientFactory, String keyFilter, String[] labelFilters) {
// The context alone does not uniquely define a PropertySource, append storeName
// and label to uniquely define a PropertySource
super(originEndpoint, replicaClient, keyFilter, labelFilter);
super(name + getLabelName(labelFilters), replicaClient);
this.keyVaultClientFactory = keyVaultClientFactory;
this.maxRetryTime = maxRetryTime;
this.keyFilter = keyFilter;
this.labelFilters = labelFilters;
}

/**
* <p>
* Gets settings from Azure/Cache to set as configurations. Updates the cache.
* </p>
*
* @param keyPrefixTrimValues prefixs to trim from key values
* @throws JsonProcessingException thrown if fails to parse Json content type
*/
public void initProperties() throws JsonProcessingException {
List<String> labels = Arrays.asList(labelFilter);
public void initProperties(List<String> keyPrefixTrimValues) throws JsonProcessingException {

List<String> labels = Arrays.asList(labelFilters);
// Reverse labels so they have the right priority order.
Collections.reverse(labels);

for (String label : labels) {
SettingSelector settingSelector = new SettingSelector().setKeyFilter(keyFilter + "*")
.setLabelFilter(label);
SettingSelector settingSelector = new SettingSelector().setKeyFilter(keyFilter + "*").setLabelFilter(label);

// * for wildcard match
List<ConfigurationSetting> settings = replicaClient.listSettings(settingSelector);

for (ConfigurationSetting setting : settings) {
String key = setting.getKey().trim().substring(keyFilter.length())
.replace('/', '.');
if (setting instanceof SecretReferenceConfigurationSetting) {
String entry = getKeyVaultEntry((SecretReferenceConfigurationSetting) setting);

// Null in the case of failFast is false, will just skip entry.
if (entry != null) {
properties.put(key, entry);
}
} else if (StringUtils.hasText(setting.getContentType())
&& JsonConfigurationParser.isJsonContentType(setting.getContentType())) {
Map<String, Object> jsonSettings = JsonConfigurationParser.parseJsonSetting(setting);
for (Entry<String, Object> jsonSetting : jsonSettings.entrySet()) {
key = jsonSetting.getKey().trim().substring(keyFilter.length());
properties.put(key, jsonSetting.getValue());
}
} else {
properties.put(key, setting.getValue());
}
}
processConfigurationSettings(replicaClient.listSettings(settingSelector), settingSelector.getKeyFilter(),
keyPrefixTrimValues);
}
}

protected void processConfigurationSettings(List<ConfigurationSetting> settings, String keyFilter,
List<String> keyPrefixTrimValues)
throws JsonProcessingException {
for (ConfigurationSetting setting : settings) {
if (keyPrefixTrimValues == null && StringUtils.hasText(keyFilter)) {
keyPrefixTrimValues = new ArrayList<>();
keyPrefixTrimValues.add(keyFilter.substring(0, keyFilter.length() - 1));
}
String key = trimKey(setting.getKey(), keyPrefixTrimValues);

if (setting instanceof SecretReferenceConfigurationSetting) {
handleKeyVaultReference(key, (SecretReferenceConfigurationSetting) setting);
} else if (setting instanceof FeatureFlagConfigurationSetting
&& FEATURE_FLAG_CONTENT_TYPE.equals(setting.getContentType())) {
handleFeatureFlag(key, (FeatureFlagConfigurationSetting) setting, keyPrefixTrimValues);
} else if (StringUtils.hasText(setting.getContentType())
&& JsonConfigurationParser.isJsonContentType(setting.getContentType())) {
handleJson(setting, keyPrefixTrimValues);
} else {
properties.put(key, setting.getValue());
}
}
}

/**
* Given a Setting's Key Vault Reference stored in the Settings value, it will
* get its entry in Key Vault.
* Given a Setting's Key Vault Reference stored in the Settings value, it will get its entry in Key Vault.
*
* @param secretReference {"uri":
* "&lt;your-vault-url&gt;/secret/&lt;secret&gt;/&lt;version&gt;"}
* @param key Application Setting name
* @param secretReference {"uri": "&lt;your-vault-url&gt;/secret/&lt;secret&gt;/&lt;version&gt;"}
* @return Key Vault Secret Value
*/
private String getKeyVaultEntry(SecretReferenceConfigurationSetting secretReference) {
String secretValue = null;
protected void handleKeyVaultReference(String key, SecretReferenceConfigurationSetting secretReference) {
try {
URI uri = null;
KeyVaultSecret secret = null;

// Parsing Key Vault Reference for URI
try {
uri = new URI(secretReference.getSecretId());
secret = keyVaultClientFactory.getClient("https://" + uri.getHost()).getSecret(uri, maxRetryTime);
URI uri = new URI(secretReference.getSecretId());
secret = keyVaultClientFactory.getClient("https://" + uri.getHost()).getSecret(uri);
} catch (URISyntaxException e) {
LOGGER.error("Error Processing Key Vault Entry URI.");
ReflectionUtils.rethrowRuntimeException(e);
}

if (secret == null) {
throw new IOException("No Key Vault Secret found for Reference.");
}
secretValue = secret.getValue();
properties.put(key, secret.getValue());
} catch (RuntimeException | IOException e) {
LOGGER.error("Error Retrieving Key Vault Entry");
ReflectionUtils.rethrowRuntimeException(e);
}
return secretValue;
}

void handleFeatureFlag(String key, FeatureFlagConfigurationSetting setting, List<String> trimStrings)
throws JsonProcessingException {
handleJson(setting, trimStrings);
}

void handleJson(ConfigurationSetting setting, List<String> keyPrefixTrimValues)
throws JsonProcessingException {
Map<String, Object> jsonSettings = JsonConfigurationParser.parseJsonSetting(setting);
for (Entry<String, Object> jsonSetting : jsonSettings.entrySet()) {
String key = trimKey(jsonSetting.getKey(), keyPrefixTrimValues);
properties.put(key, jsonSetting.getValue());
}
}


protected String trimKey(String key, List<String> trimStrings) {
key = key.trim();
if (trimStrings != null) {
for (String trim : trimStrings) {
if (key.startsWith(trim)) {
return key.replaceFirst("^" + trim, "").replace('/', '.');
}
}
}
return key.replace("/", ".");
}
}
Loading

0 comments on commit 1a70f74

Please sign in to comment.