According to documentation:
To convert a property name in the canonical-form to an environment variable name you can follow these rules:
Replace dots (.) with underscores (_).
Remove any dashes (-).
Convert to uppercase.
However I can see in Actuator this:
URL: .../actuator/env/portal.s3.secret-key
|
-- | --
property |
source | "systemEnvironment"
value | "******"
activeProfiles |
0 | "awsqa"
propertySources |
0 |
name | "server.ports"
1 |
name | "serviceEndpoints"
2 |
name | "servletConfigInitParams"
3 |
name | "servletContextInitParams"
4 |
name | "systemProperties"
5 |
name | "systemEnvironment"
property |
value | "******"
origin | "System Environment Property \"PORTAL_S3_SECRET_KEY\""
6 |
name | "random"
7 |
name | "applicationConfig: [classpath:/application-awsqa.yml]"
8 |
name | "applicationConfig: [classpath:/application.yml]"
property |
value | "******"
origin | "class path resource [application.yml]:69:16"
9 |
name | "defaultProperties"
10 |
name | "Management Server"
How could that be, that portal.s3.secret-key
matches PORTAL_S3_SECRET_KEY and doesn't match (bug) PORTAL_S3_SECRETKEY?
Spring Boot 2.3.3
Comment From: wilkinsona
PORTAL_S3_SECRETKEY
is the preferred form but as a result of https://github.com/spring-projects/spring-boot/commit/e9f31f9c34b8ee3ea2ee90361011d3a8e44ce936 (issue https://github.com/spring-projects/spring-boot/issues/10873), PORTAL_S3_SECRET_KEY
should also work.
We can confirm this behaviour with the following test, which passes when added to SystemEnvironmentPropertyMapperTests
:
@Test
void gh23703() {
assertThat(mapConfigurationPropertyName("portal.s3.secret-key")).containsExactly("PORTAL_S3_SECRETKEY", "PORTAL_S3_SECRET_KEY");
}
Unfortunately, I can't tell why this isn't working from the information provided thus far. If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.
Comment From: tomas-laubr-smart-software
Thank you for quick reply.
So my test is as follows:
- from windows command line:
set PORTAL_S3_SECRETKEY=mysecret
- run the app:
mvnw -P dev spring-boot:run -Dspring-boot.run.fork=false
- go to
http://localhost:8080/actuator/env/portal.s3.secret-key
Now I see this:
{"property":{"source":"applicationConfig: [classpath:/application-dev.yml]","value":"******"},"activeProfiles":["dev"],"propertySources":[{"name":"server.ports"},{"name":"serviceEndpoints"},{"name":"servletConfigInitParams"},{"name":"servletContextInitParams"},{"name":"systemProperties"},{"name":"systemEnvironment"},{"name":"random"},{"name":"applicationConfig: [classpath:/application-dev.yml]","property":{"value":"******","origin":"class path resource [application-dev.yml]:89:17"}},{"name":"applicationConfig: [classpath:/application.yml]","property":{"value":"******","origin":"class path resource [application.yml]:69:16"}},{"name":"defaultProperties"},{"name":"Management Server"}]}
Then I continue:
4. from windows command line: set PORTAL_S3_SECRET_KEY=mysecret2
5. run the app: mvnw -P dev spring-boot:run -Dspring-boot.run.fork=false
6. go to http://localhost:8080/actuator/env/portal.s3.secret-key
again
This time I can see this:
{"property":{"source":"systemEnvironment","value":"******"},"activeProfiles":["dev"],"propertySources":[{"name":"server.ports"},{"name":"serviceEndpoints"},{"name":"servletConfigInitParams"},{"name":"servletContextInitParams"},{"name":"systemProperties"},{"name":"systemEnvironment","property":{"value":"******","origin":"System Environment Property \"PORTAL_S3_SECRET_KEY\""}},{"name":"random"},{"name":"applicationConfig: [classpath:/application-dev.yml]","property":{"value":"******","origin":"class path resource [application-dev.yml]:89:17"}},{"name":"applicationConfig: [classpath:/application.yml]","property":{"value":"******","origin":"class path resource [application.yml]:69:16"}},{"name":"defaultProperties"},{"name":"Management Server"}]}
As you can see only the second env. variable applies.
I don't complain the PORTAL_S3_SECRET_KEY
matches portal.s3.secret-key
. I complain the PORTAL_S3_SECRETKEY
doesn't match. Neither locally on my machine, nor on Amazon Linux, it seems.
Any idea what other information I can provide?
Ah, ok, you want me to provide a working sample. OK, I will try later.
Comment From: mmadoo
I have same issue this morning.
Env variable are processed (since fix of #5315) as described at https://github.com/spring-projects/spring-boot/wiki/Relaxed-Binding-2.0 but actuator does not do the same processing.
You could verify it with:
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import lombok.Data;
@Configuration
@ConfigurationProperties("foo")
@Data
public class FooConfiguration {
private List<String> bar = new ArrayList<>();
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
System.out.println("Got bar: " + bar);
}
}
and setting the env variable FOO_BAR_0_=env, then at startup, there is the log: Got bar: [env]
. but the command curl -X GET "http://localhost:8080/actuator/env/foo.bar%5B0%5D" -H "accept: application/json"
returns a 404 Not Found
Comment From: wilkinsona
Thanks, @mmadoo, but I think that's a subtly different issue. In your case, it's configuration property binding that applies certain rules to the names of the properties in the environment. For example, it's configuration property binding that knows that FOO_BAR_0_
is configuring the zeroth element of the foo.bar
property. The env
endpoint is providing a view of the environment and by design doesn't apply any of the configuration property binding rules to that view.
You can query the env
endpoint for the FOO_BAR_0_
property and see that it's there:
http localhost:8080/actuator/env/FOO_BAR_0_
HTTP/1.1 200
Connection: keep-alive
Content-Type: application/vnd.spring-boot.actuator.v3+json
Date: Thu, 15 Oct 2020 13:08:16 GMT
Keep-Alive: timeout=60
Transfer-Encoding: chunked
{
"activeProfiles": [],
"property": {
"source": "systemEnvironment",
"value": "env"
},
"propertySources": [
{
"name": "server.ports"
},
{
"name": "servletConfigInitParams"
},
{
"name": "servletContextInitParams"
},
{
"name": "systemProperties"
},
{
"name": "systemEnvironment",
"property": {
"origin": "System Environment Property \"FOO_BAR_0_\"",
"value": "env"
}
},
{
"name": "random"
},
{
"name": "applicationConfig: [classpath:/application.properties]"
},
{
"name": "Management Server"
}
]
}
If you want to see how properties have been bound, you should use the configprops
endpoint.
Let's keep this issue focused on the situation that @tomas-laubr-smart-software has described above.
Comment From: wilkinsona
@tomas-laubr-smart-software No need for a sample any more. I have reproduced the problem.
Comment From: wilkinsona
As with the situation described by @mmadoo, there's no configuration property binding involved here. The behaviour is entirely dependent on how the Environment
works.
In this case, the request to http://localhost:8080/actuator/env/portal.s3.secret-key
results in Framework's SystemEnvironmentPropertySource
being queried for a property named portal.s3.secret-key
. It has some rules, implemented in checkPropertyName(String)
, for converting the given name into environment variable names. It tries the property as-is, with .
replaced with _
, with -
replaced with _
, and with .
and -
both replaced with _
. It also tries all four options having upper-cased the original input. These rules result in the following names being checked:
portal.s3.secret-key
portal_s3_secret-key
portal.s3.secret_key
portal_s3_secret_key
PORTAL.S3.SECRET-KEY
PORTAL_S3_SECRET-KEY
PORTAL.S3.SECRET_KEY
PORTAL_S3_SECRET_KEY
It's the final one that the results in the PORTAL_S3_SECRET_KEY
environment variable being found. As Framework doesn't try a variant where -
is removed, the PORTAL_S3_SECRETKEY
environment variable is not matched.
The conversion to canonical form that's described above is taken from Binding from Environment Variables which is specific to @ConfigurationProperties
so, strictly speaking, they don't apply here. However, it's certainly confusing that some conversion relaxed-binding-like conversion is performed by Framework's SystemEnvironment
and that it's subtly different to what Boot does during configuration property binding.
Comment From: tomas-laubr-smart-software
What I need is a way how to check, if my Spring Boot app is properly configured from environment variables. So I went with /actuator/evn
Is it the right way?
Are you saying that even if /actuator/env
doesn't "see" my PORTAL_S3_SECRETKEY
, my Spring Boot app does and is properly configured?
Comment From: wilkinsona
Are you saying that even if /actuator/env doesn't "see" my PORTAL_S3_SECRETKEY, my Spring Boot app does and is properly configured?
Yes, if you have used @ConfigurationProperties
which is what we recommend. That may look something like this:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import com.example.demo.Gh23703Application.PortalS3Properties;
@SpringBootApplication
@EnableConfigurationProperties(PortalS3Properties.class)
public class Gh23703Application {
public static void main(String[] args) {
SpringApplication.run(Gh23703Application.class, args);
}
@ConfigurationProperties(prefix="portal.s3")
static class PortalS3Properties {
private String secretKey;
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
}
}
You can then verify how it has been configured using the Actuator's configprops endpoint. With the above and an environment variable names PORTAL_S3_SECRETKEY
, it will contain an entry like this:
"portal.s3-com.example.demo.Gh23703Application$PortalS3Properties": {
"inputs": {
"secretKey": {
"origin": "System Environment Property \"PORTAL_S3_SECRETKEY\"",
"value": "******"
}
},
"prefix": "portal.s3",
"properties": {
"secretKey": "******"
}
},
It will also work with an environment variable named PORTAL_S3_SECRET_KEY
which will result in the following entry in the response from the configprops endpoint:
"portal.s3-com.example.demo.Gh23703Application$PortalS3Properties": {
"inputs": {
"secretKey": {
"origin": "System Environment Property \"PORTAL_S3_SECRET_KEY\"",
"value": "******"
}
},
"prefix": "portal.s3",
"properties": {
"secretKey": "******"
}
},
Comment From: philwebb
See #10178 for some background on why we use ConfigurationPropertySources.isAttachedConfigurationPropertySource
.
Comment From: philwebb
We're cleaning out the issue tracker and closing issues that we've not seen much demand to fix. Feel free to comment with additional justifications if you feel that this one should not have been closed.