Skip to content

'spring.config.import' placeholders can resolve from profile-specific documents when they should fail #29386

@tauparticle

Description

@tauparticle

Greetings Spring Boot. I ran into an unexpected results with the following setup with config loading. Let's consider we have the following application.yml file.

test:
  environment: test

---
spring:
  config:
    activate:
      on-profile: profileA

test:
  environment: prod


---
spring:
  config:
    activate:
      on-profile: profileB
    import:
      - application/${test.environment}.yml

otherProp: ${test.environment}  

And we have application/prod.yml with the following contents:

myTestProp: testProp/prod

And application/test.yml with the following contents:

myTestProp: testProp/test

And finally we have the following test that will activate profileB and assert on expected results.

@ActiveProfiles(profiles = "profileB")
@SpringBootTest
public class ProfileBPropsTest {

    @Autowired
    private Environment env;

    @Test
    public void Test() {
        assertEquals("test", env.getProperty("test.environment", String.class));
        assertEquals("test", env.getProperty("otherProp", String.class));
        assertEquals("testProp/test", env.getProperty("myTestProp", String.class));
    }

    @Configuration
    class TestConfig {}

And we have the following test to activate both profiles profileA and profileB and assert on expected results.

@ActiveProfiles(profiles = {"profileA", "profileB"})
@SpringBootTest
public class ProfileABPropsTest {

    @Autowired
    private Environment env;

    @Test
    public void Test() {
        assertEquals("prod", env.getProperty("test.environment", String.class));
        assertEquals("prod", env.getProperty("otherProp", String.class));
        assertEquals("testProp/prod", env.getProperty("myTestProp", String.class));
    }

    @Configuration
    class TestConfig {}

}

What I've observed is ProfileABPropsTest passes expectedly. The file-order profile activation picks up the override for test.environment in profileA which is then used in profileB to import prod.yml as a property source.

However what fails is ProfileBPropsTest. Only profileB is active, so we assume the default value of test.environment=test will cause test.yml to be imported as a property source. Instead what we find is the value of myTestProp=testProp/prod which would only happen if prod.yml was loaded. But as we can see by the test, test.environment=test, so how is it that we get this property in this scenario unless the expression ${test.environment} is pulling in an incorrect value that should technically only happen if profileA is activated.

I cannot explain this from reading the Spring documentation. For context, I am seeing this in spring boot 2.5.x.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions