Merge branch 'main' of github.com:spring-cloud/spring-cloud-config

This commit is contained in:
Ryan Baxter
2024-10-02 10:17:20 -04:00
12 changed files with 208 additions and 38 deletions

View File

@@ -16,6 +16,11 @@
package org.springframework.cloud.config.server.environment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.cloud.config.environment.Environment;
@@ -23,6 +28,7 @@ import org.springframework.cloud.config.server.support.AbstractScmAccessor;
import org.springframework.cloud.config.server.support.AbstractScmAccessorProperties;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.StringUtils;
/**
* @author Dave Syer
@@ -57,12 +63,29 @@ public abstract class AbstractScmEnvironmentRepository extends AbstractScmAccess
@Override
public synchronized Environment findOne(String application, String profile, String label, boolean includeOrigin) {
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),
new NativeEnvironmentProperties(), this.observationRegistry);
Locations locations = getLocations(application, profile, label);
delegate.setSearchLocations(locations.getLocations());
Environment result = delegate.findOne(application, profile, "", includeOrigin);
result.setVersion(locations.getVersion());
Environment result;
if (StringUtils.hasText(label) && label.contains(",")) {
List<String> labels = Arrays.asList(StringUtils.commaDelimitedListToStringArray(label));
Collections.reverse(labels);
List<EnvironmentRepository> environmentRepositories = new ArrayList<>();
Environment env = new Environment(application, new String[] { profile }, label, null, null);
for (String l : labels) {
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),
new NativeEnvironmentProperties(), this.observationRegistry);
Locations locations = getLocations(application, profile, l);
delegate.setSearchLocations(locations.getLocations());
env.addAll(delegate.findOne(application, profile, "", includeOrigin).getPropertySources());
}
result = env;
}
else {
NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),
new NativeEnvironmentProperties(), this.observationRegistry);
Locations locations = getLocations(application, profile, label);
delegate.setSearchLocations(locations.getLocations());
result = delegate.findOne(application, profile, "", includeOrigin);
result.setVersion(locations.getVersion());
}
result.setLabel(label);
return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(), getUri());
}

View File

@@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
@@ -93,15 +94,28 @@ public class AwsS3EnvironmentRepository implements EnvironmentRepository, Ordere
final Environment environment = new Environment(application, profileArray);
environment.setLabel(label);
for (String profile : profileArray) {
for (String app : apps) {
addPropertySource(environment, app, profile, label);
List<String> labels;
if (StringUtils.hasText(label) && label.contains(",")) {
labels = Arrays.asList(StringUtils.commaDelimitedListToStringArray(label));
Collections.reverse(labels);
}
else {
labels = Collections.singletonList(label);
}
for (String l : labels) {
for (String profile : profileArray) {
for (String app : apps) {
addPropertySource(environment, app, profile, l);
}
}
}
// Add propertysources without profiles as well
for (String app : apps) {
addPropertySource(environment, app, null, label);
for (String l : labels) {
for (String app : apps) {
addPropertySource(environment, app, null, l);
}
}
if (LOG.isDebugEnabled()) {

View File

@@ -101,29 +101,37 @@ public class AwsSecretsManagerEnvironmentRepository implements EnvironmentReposi
environment.add(new PropertySource("overrides", overrides));
}
List<String> labels;
if (StringUtils.hasText(label) && label.contains(",")) {
labels = Arrays.asList(StringUtils.commaDelimitedListToStringArray(label));
Collections.reverse(labels);
}
else {
labels = Collections.singletonList(label);
}
List<String> reversedProfiles = new ArrayList<>(Arrays.asList(profiles));
Collections.reverse(reversedProfiles);
for (String profile : reversedProfiles) {
addPropertySource(environment, application, profile, label);
if (!defaultApplication.equals(application)) {
addPropertySource(environment, defaultApplication, profile, label);
for (String l : labels) {
for (String profile : reversedProfiles) {
addPropertySource(environment, application, profile, l);
if (!defaultApplication.equals(application)) {
addPropertySource(environment, defaultApplication, profile, l);
}
}
if (!Arrays.asList(profiles).contains(defaultProfile)) {
addPropertySource(environment, application, defaultProfile, l);
}
if (!Arrays.asList(profiles).contains(defaultProfile) && !defaultApplication.equals(application)) {
addPropertySource(environment, defaultApplication, defaultProfile, l);
}
}
if (!Arrays.asList(profiles).contains(defaultProfile)) {
addPropertySource(environment, application, defaultProfile, label);
if (!defaultApplication.equals(application)) {
addPropertySource(environment, application, null, l);
}
addPropertySource(environment, defaultApplication, null, l);
}
if (!Arrays.asList(profiles).contains(defaultProfile) && !defaultApplication.equals(application)) {
addPropertySource(environment, defaultApplication, defaultProfile, label);
}
if (!defaultApplication.equals(application)) {
addPropertySource(environment, application, null, label);
}
addPropertySource(environment, defaultApplication, null, label);
return environment;
}

View File

@@ -77,11 +77,14 @@ public class CredhubEnvironmentRepository implements EnvironmentRepository, Orde
List<String> applications = normalize(application, DEFAULT_APPLICATION);
List<String> profiles = normalize(profile, DEFAULT_PROFILE);
List<String> labels = normalize(label, this.defaultLabel);
Environment environment = new Environment(application, split(profile), label, null, null);
for (String prof : profiles) {
for (String app : applications) {
addPropertySource(environment, app, prof, label);
for (String l : labels) {
for (String prof : profiles) {
for (String app : applications) {
addPropertySource(environment, app, prof, l);
}
}
}

View File

@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -210,9 +211,19 @@ public class NativeEnvironmentRepository implements EnvironmentRepository, Searc
if (this.addLabelLocations) {
for (String location : locations) {
if (StringUtils.hasText(label)) {
String labelled = location + label.trim() + "/";
if (isDirectory(labelled)) {
output.add(labelled);
List<String> labels;
if (label.contains(",")) {
labels = Arrays.asList(StringUtils.commaDelimitedListToStringArray(label));
Collections.reverse(labels);
}
else {
labels = Collections.singletonList(label);
}
for (String l : labels) {
String labelled = location + l + "/";
if (isDirectory(labelled)) {
output.add(labelled);
}
}
}
}

View File

@@ -103,6 +103,15 @@ public class AwsS3IntegrationTests {
objectResponse = s3Client.putObject((request) -> request.bucket("test-bucket").key("data-dev.properties"),
RequestBody.fromString("bar=1"));
LOG.info("object response " + objectResponse);
objectResponse = s3Client.putObject((request) -> request.bucket("test-bucket").key("main/foo.properties"),
RequestBody.fromString("foo=1"));
LOG.info("object response " + objectResponse);
objectResponse = s3Client.putObject((request) -> request.bucket("test-bucket").key("dev/foo.properties"),
RequestBody.fromString("devfoo=1"));
LOG.info("object response " + objectResponse);
objectResponse = s3Client.putObject((request) -> request.bucket("test-bucket").key("test/foo.properties"),
RequestBody.fromString("testfoo=1"));
LOG.info("object response " + objectResponse);
}
@AfterAll
@@ -124,6 +133,16 @@ public class AwsS3IntegrationTests {
.isEqualTo("this is a test");
}
@Test
public void testMultipleLabels() throws IOException {
RestTemplate rest = new RestTemplateBuilder().build();
String configServerUrl = "http://localhost:" + configServerPort;
Environment env = rest.getForObject(configServerUrl + "/foo/default/main,dev,test", Environment.class);
assertThat(env.getPropertySources().get(0).getSource().get("testfoo")).isEqualTo("1");
assertThat(env.getPropertySources().get(1).getSource().get("devfoo")).isEqualTo("1");
assertThat(env.getPropertySources().get(2).getSource().get("foo")).isEqualTo("1");
}
@Test
public void defaultApplicationAndProfileIncluded() throws IOException {
RestTemplate rest = new RestTemplateBuilder().build();

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.config.server;
import org.eclipse.jgit.junit.MockSystemReader;
import org.eclipse.jgit.util.SystemReader;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@@ -41,6 +42,7 @@ import static org.springframework.cloud.config.server.test.ConfigServerTestUtils
*/
public class CompositeIntegrationTests {
@Nested
@SpringBootTest(classes = TestConfigServerApplication.class,
properties = { "spring.config.name:compositeconfigserver",
"spring.cloud.config.server.svn.uri:file:///./target/repos/svn-config-repo",
@@ -49,7 +51,7 @@ public class CompositeIntegrationTests {
"spring.cloud.config.server.git.order:1" },
webEnvironment = RANDOM_PORT)
@ActiveProfiles({ "test", "git", "subversion" })
public static class StaticTests {
class StaticTests {
@LocalServerPort
private int port;
@@ -92,6 +94,7 @@ public class CompositeIntegrationTests {
}
@Nested
@SpringBootTest(classes = TestConfigServerApplication.class,
properties = { "spring.config.name:compositeconfigserver",
"spring.cloud.config.server.svn.uri:file:///./target/repos/svn-config-repo",
@@ -100,7 +103,7 @@ public class CompositeIntegrationTests {
"spring.cloud.config.server.git.order:1", "spring.cloud.config.server.reverseLocationOrder:true" },
webEnvironment = RANDOM_PORT)
@ActiveProfiles({ "test", "git", "subversion" })
public static class ReverseLocationOrderTest {
class ReverseLocationOrderTest {
@LocalServerPort
private int port;
@@ -130,6 +133,7 @@ public class CompositeIntegrationTests {
}
@Nested
@SpringBootTest(classes = TestConfigServerApplication.class,
properties = { "spring.config.name:compositeconfigserver",
"spring.cloud.config.server.composite[0].uri:file:./target/repos/config-repo",
@@ -138,7 +142,7 @@ public class CompositeIntegrationTests {
"spring.cloud.config.server.composite[1].type:svn" },
webEnvironment = RANDOM_PORT)
@ActiveProfiles({ "test", "composite" })
public static class ListTests {
class ListTests {
@LocalServerPort
private int port;

View File

@@ -18,6 +18,7 @@ package org.springframework.cloud.config.server.environment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -1595,6 +1596,41 @@ public class AwsSecretsManagerEnvironmentRepositoryTests {
assertThat(resultEnv).usingRecursiveComparison().withStrictTypeChecking().isEqualTo(expectedEnv);
}
@Test
public void testFindOneWithExistingApplicationAndDefaultProfileAndExistingLabelWhenMultipleLabelIsSet() {
String application = "foo";
String profile = configServerProperties.getDefaultProfile();
String label = "release,test";
String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
String fooPropertiesName = "aws:secrets:/secret/foo/";
PropertySource fooProperties = new PropertySource(fooPropertiesName, getFooReleaseProperties());
String fooDefaultPropertiesName = "aws:secrets:/secret/foo-default/";
PropertySource fooDefaultProperties = new PropertySource(fooDefaultPropertiesName,
getFooDefaultReleaseProperties());
String applicationDefaultPropertiesName = "aws:secrets:/secret/application-default/";
PropertySource applicationDefaultProperties = new PropertySource(applicationDefaultPropertiesName,
getApplicationDefaultReleaseProperties());
String applicationPropertiesName = "aws:secrets:/secret/application/";
PropertySource applicationProperties = new PropertySource(applicationPropertiesName,
getApplicationReleaseProperties());
Environment expectedEnv = new Environment(application, profiles, label, null, null);
expectedEnv.addAll(Arrays.asList(applicationDefaultProperties, fooProperties));
putSecrets("release", Collections.singletonList(fooProperties));
putSecrets("dev", Collections.singletonList(fooDefaultProperties));
putSecrets("test", Collections.singletonList(applicationDefaultProperties));
putSecrets("", Collections.singletonList(applicationProperties));
Environment resultEnv = labeledRepository.findOne(application, profile, label);
assertThat(resultEnv).usingRecursiveComparison().withStrictTypeChecking().isEqualTo(expectedEnv);
}
@Test
public void testFindOneWithExistingApplicationAndNonExistingProfileAndExistingLabelWhenDefaultLabelIsSet() {
String application = "foo";
@@ -2640,7 +2676,11 @@ public class AwsSecretsManagerEnvironmentRepositoryTests {
private void putSecrets(Environment environment) {
String label = environment.getLabel() != null ? environment.getLabel()
: environmentProperties.getDefaultLabel();
for (PropertySource ps : environment.getPropertySources()) {
putSecrets(label, environment.getPropertySources());
}
private void putSecrets(String label, List<PropertySource> propertySources) {
for (PropertySource ps : propertySources) {
String path = StringUtils.delete(ps.getName(), environmentProperties.getOrigin());
String secrets = getSecrets(ps);
CreateSecretResponse response = smClient

View File

@@ -121,6 +121,28 @@ public class CredhubEnvironmentRepositoryTests {
assertThat(environment.getPropertySources().get(1).getSource()).isEqualTo(Map.of("k1", "v1"));
}
@Test
public void shouldRetrieveGivenLabelsProfiles() {
stubCredentials("/myApp/prod/myLabel", credential("c1", "k1", "v1"));
stubCredentials("/myApp/cloud/mySecondLabel", credential("c2", "k2", "v2"));
stubCredentials("/myApp/prod/myThirdLabel", credential("c3", "k3", "v3"));
Environment environment = this.credhubEnvironmentRepository.findOne("myApp", "prod,cloud",
"myLabel,mySecondLabel");
assertThat(environment.getName()).isEqualTo("myApp");
assertThat(environment.getProfiles()).containsExactly("prod", "cloud");
assertThat(environment.getLabel()).isEqualTo("myLabel,mySecondLabel");
assertThat(environment.getPropertySources()).hasSize(2);
assertThat(environment.getPropertySources().get(0).getName()).isEqualTo("credhub-myApp-cloud-mySecondLabel");
assertThat(environment.getPropertySources().get(0).getSource()).isEqualTo(Map.of("k2", "v2"));
assertThat(environment.getPropertySources().get(1).getName()).isEqualTo("credhub-myApp-prod-myLabel");
assertThat(environment.getPropertySources().get(1).getSource()).isEqualTo(Map.of("k1", "v1"));
}
@Test
public void shouldRetrieveGivenMultipleApplicationNames() {
stubCredentials("/app1/default/myLabel", credential("c1", "k1", "v1"));

View File

@@ -210,6 +210,13 @@ public class JGitEnvironmentRepositoryTests {
assertVersion(environment);
}
@Test
public void multipleLabels() {
this.repository.setBasedir(this.basedir);
Environment environment = this.repository.findOne("bar", "staging", "master,foo,raw");
assertThat(environment.getPropertySources()).hasSize(6);
}
@Test
public void basedirExists() throws Exception {
assertThat(this.basedir.mkdirs()).isTrue();

View File

@@ -244,6 +244,16 @@ public class NativeEnvironmentRepositoryTests {
assertThat(environment.getPropertySources().get(0).getSource().get("foo")).isNotEqualTo("dev_bar");
}
@Test
public void locationAddMultipleLabelLocations() {
this.repository.setSearchLocations("classpath:/test/dev/");
Environment environment = this.repository.findOne("foo", "development", "ignore,applicationxyz");
assertThat(environment.getPropertySources()).hasSize(3);
assertThat(environment.getPropertySources().get(0).getSource().get("foo")).isEqualTo("app");
assertThat(environment.getPropertySources().get(1).getSource().get("foo")).isEqualTo("default-app");
assertThat(environment.getPropertySources().get(2).getSource().get("foo")).isEqualTo("dev_bar");
}
@Test
public void tryToStartReactive() {
this.repository.setSearchLocations("classpath:/test/reactive/");

View File

@@ -115,6 +115,15 @@ public class SVNKitEnvironmentRepositoryTests {
assertThat(environment.getPropertySources().get(1).getName()).contains("application.yml");
}
@Test
public void testMultipleLabels() {
Environment environment = this.repository.findOne("bar", "staging", "branches/demobranch,trunk");
assertThat(environment.getPropertySources()).hasSize(3);
assertThat(environment.getPropertySources().get(0).getName()).contains("bar.properties");
assertThat(environment.getPropertySources().get(1).getName()).contains("application.yml");
assertThat(environment.getPropertySources().get(2).getName()).contains("branches/demobranch/bar.properties");
}
@Test
public void invalidLabel() {
Assertions.assertThatThrownBy(() -> {