Spring-Boot: How to load a keystore resource file either from the classpath or from the file system? - spring-boot

My spring-boot application requires access to private keys via a Java keystore. For testing, I would like to use a test keystore that is available on the classpath (so I can easily execute the application as part of an integration test), while in production I would like to use a keystore from some external volume (e.g., mounted inside a container as k8 secret).
I know that the spring-boot #Value annotation allows me to inject a resource. However, I don't see how it can be made dynamic to respect the spring environment (e.g., test or production). Does spring-boot provide an out-of-the-box way to solve the above problem, or do I need to write my own bean to load the correct keystore depending on the deployment environment?

You can use a property for the resource path and set that property values in your application props differently based on an active profile.
#Value("${my.resource.path:#{null}}")
private Resource myResource;
application.yml:
spring:
config:
activate:
on-profile: test
my.resource.path: 'classpath:test/path/keystore.jks'
---
spring:
config:
activate:
on-profile: prod
my.resource.path: 'file:prod/path/keystore.jks'

Related

Best practice for application.properties for tests in spring-boot

I have seen projects where a standalone application.properties is defined under src/test/resources containing all the properties from the default application.properties defined under src/main/resources while overriding properties for testing purposes. The disadvantage of this approach is that every change (adding/removing/updating of properties) must be done in both application.properties, otherwise tests may fail.
On the other hand I have seen projects where only a context based property file is defined under src/test/resources, say application-test.properties containing only the overriden properties for testing purposes. This approach merges the default application.properties under src/main/resources and the application-test.properties under src/test/resources. The respective context must be activated in test classes via #ActiveProfiles, say #ActiveProfiles("test")
What is the approach you follow and why? Is there any best practice or recommendation for this?
Thanks for your replies in advance.
Profiles are generally used to "map" different Spring beans accordingly. I've seen that people usually stick to three of them for most of the workflows: development, test, and production — mainly in tutorials and blogs.
I use profiles aligned with the actual environments the application is going to be deployed at since it's more realistic. So I always run the tests with the local profile. If I need any specific settings for running the tests, I just redefine an application.properties file under src/test/resources and overwrite what's needed.
In this way, you would always effectively have what's going to be used to run the application in each environment within src/main/resources, and if anything needs to be overwritten only for testing, the src/test/resources/application.properties could take care of that.
For instance, let's say your src/main/resources/application.yaml file looks like:
application:
key: value-common
another-key: value-common-another
spring:
profiles:
active: default
---
spring:
config:
activate:
on-profile: local
main:
banner-mode: console
---
spring:
config:
activate:
on-profile: test
This defines effectively two profiles: local and test — because default would have what's common to both. Now, if you place an application.properties file with the following content in src/test/resources:
application.key: value-common-test
...everything will stay the same, but application.key will be bound to value-common-test instead when you run the tests (for any profile). If you happen to just run the application, it will be bound to value-common instead.

Spring Cloud Embedded Config Server with native repository

I have a working spring boot application and I am trying to remove some properties from application.yaml file and read them from an embedded config server in the same app. At the moment, I am trying to read properties from file system through "native" profile type, and I am planning to later replace this with S3. Also, I am trying to read the configuration directly from backend repository as explained here https://docs.spring.io/spring-cloud-config/docs/current/reference/html/#_embedding_the_config_server , rather than connecting through an endpoint.
If you want to read the configuration for an application directly from
the backend repository (instead of from the config server), you
basically want an embedded config server with no endpoints. You can
switch off the endpoints entirely by not using the #EnableConfigServer
annotation (set spring.cloud.config.server.bootstrap=true).
I have introduced following changes to my existing application to achieve this.
Added following dependencies to pom.xml
spring-cloud-starter-config
spring-cloud-config-server
spring-cloud-config-client
application-dev.yaml
spring:
could:
bootstrap:
enabled: true
bootstrap.yaml
spring:
profiles:
active: composite
cloud:
config:
server:
composite:
- type: native
search-locations: file:C:\\Users\\chamila\\config-test\\config
# bootstrap: true
The required properties are saved in a different application-dev.yaml file at the above file path. I have not used #EnableConfigServer annotation from my app class, as I want connect directly without the endpoint.
However, still my program is failing to read the properties from the config-server. I tried setting spring.cloud.config.server.bootstrap=true and spring.cloud.bootstrap.enabled=true from both application-dev.yaml and bootstrap.yaml, but it didn't work?
Any idea what I am missing? I note that I never specified how to connect to the config-server in my application-dev.yaml file also. Is that an issue?
Any help is highly appreciated.
I created a sample project of an embedded Configuration Server which uses a custom repository:
https://github.com/taxone/embedded-config-server
The initial problem was that the custom EnvironmentRepository was not available via bootstrap, as described in this issue.
I followed https://cloud.spring.io/spring-cloud-config/reference/html/#_embedding_the_config_server and achieved the expected result.
Add spring-cloud-starter-bootstrap to your dependencies as suggested in https://github.com/taxone/embedded-config-server.
Make sure that composite profile is active when you start the app.
Verify that your native configuration is added by finding a log message from NativeEnvironmentRepository during startup.
(Optional) Remove spring.cloud.bootstrap.enabled from application-dev.yaml.
(Optional) Remove spring-cloud-config-client dependency as it is included in both spring-cloud-starter-config and
spring-cloud-config-server.
(Further steps) Use #RefreshScope on a spring bean to achieve dynamic properties. Requires POST actuator/refresh.

How to override a property defined in profile-specific application-{profile}.yml for a single application?

I have a spring-cloud-config server set up to serve YML files from a git repository. The server is using Spring Cloud 2020.0.3. There are several spring-cloud-config clients consuming config files from the server. The client applications consuming the config are using Spring Boot 2.4.6 and the new config parsing implementation.
The config repository contains an application-db.yml file and myapp.yml file. The application-db.yml file has several common properties set that all applications will use for the db profile. myapp.yml has properties for a specific application. Prior to using Spring Boot 2.4.6, I was able to override properties from application-db.yml in myapp.yml. In effect, the application-db.yml properties were the default values that individual applications could override.
For example application-db.yml might have (property key is not relevant):
spring:
datasource:
hikari:
maximum-pool-size: 5
to set the connection pool size to 5 for most applications and myapp.yml could set
spring:
datasource:
hikari:
maximum-pool-size: 10
for a one-off connection pool size of 10.
In Spring Cloud 2020.0.3 (Spring Cloud Config 3.0.4) and Spring Boot 2.4.6, the behavior is that the property in application-db.yml cannot be overridden in myapp.yml and the connection pool size property always has value 5.
I have tried setting the following properties in the application.yml and still have the same results.
spring:
cloud:
config:
allowOverride: true
overrideNone: true
overrideSystemProperties: false
According to Config file processing in Spring Boot 2.4 overriding property values is allowed in the config processing, but it is dependent on document order.
Is the overriding behavior still supported by Spring Boot using Spring Cloud Config? If so, does it depend on the order the cloud config server serves the documents? Is there a way to control the order?
edit note: I originally posted about overriding properties in the application.yml file. After more investigation, that does allowing overriding. It is in the profile specific files like application-db.yml that I cannot override values. I'm starting to think this might be a bug.
The overriding behavior you are looking for is supported in the latest release documented here and should be the default behavior without needing to add additional properties to enable override. You will need to include the application name with the spring.application.name property for the config server to recognize the overriding file.The overriding file also needs to have the same name you give in the client application (in this case,for example, my config repo has files application.properties and myapp.properties.
Client application.properties
spring.application.name=myapp
spring.config.import=optional:configserver:http://localhost:8888
...
Server application.properties
spring.application.name=configserver
spring.cloud.config.server.git.uri=https://github.com/...
server.port=8888
...
Within config repo
application.properties
spring:
datasource:
hikari:
maximum-pool-size: 5
myapp.properties
spring:
datasource:
hikari:
maximum-pool-size: 10
If the client application cannot fetch the overriding file (in this case the myapp file) from the config server application, it will fall back to its local application.properties file. However, the application will probably fail if all of the properties your application needs are not present there as well, so it's a good practice to copy all properties from your overriding file to the client application.properties file in case fetch fails.
If this didn't fix your problem I would check out this section on property overrides.

Integration tests in spring boot application with spring cloud

I have an spring-boot application that uses config server.
In project I have a bootstrap.yml:
spring:
cloud:
config:
uri: ${CLOUD_CONFIG_URI:http://localhost:8888}
failFast: true
enabled: ??
and I can pass actual config for server location through parameter. That's ok.
With this configuration I don't know how to disable this in integration tests. My tests load this configuration and want to communicate with config server. I know that I can pass spring.cloud.config.enabled=false but it's not a solution (I want to right click in IDE and run test without additional configuration per each test method).
Any idea?
This is a late answer but for the people that join from any search engines here is my solution:
The #Profile("test") annotation above your integration test class is correct. To get this really working is adding two extra configuration files to your normal resources folder, not the test resources folder.
Add "application-test.yml"
Add "bootstrap-test.yml"
In your bootstrap-test.yml add the following:
spring:
cloud:
config:
enabled: false
With this configuration you can put all of your needed configuration options in the application-test.yml and the bootstrap-test.yml will disabled spring cloud config.

Spring Cloud Client Config with placeholders

I have a Spring Boot application.yml with this config for Cloud Config:
spring:
cloud:
config:
uri: http://localhost:8080/config
name: ${cluster.name}
profile: ${cluster.idx}
I read cluster.name and cluster.idx from a custom PropertySouce that loads a JSON file. But Spring Boot is not capable of resolve the placeholder yet.
13:04:37,370 ERROR [main] org.springframework.boot.SpringApplication(SpringApplication.java:839) : Application startup failed
java.lang.IllegalArgumentException: Could not resolve placeholder 'cluster.idx' in string value "${cluster.idx}"
How do I tell Spring Boot to load my PropertySource before trying to resolve the placeholders? Thanks in advance.
I'm using Sp Boot 1.4.3 and Sp Cloud Config 1.2.2.
You won't be able to use the property sources as they are not yet available, and will not be at the time that this file is read. You have a couple of options, though. First, you can place these properties into the bootstrap.properties (or bootstrap.yml if you prefer). I suspect that you are trying to do something that's profile driven though and should use the appropriate properties for the profile. In this case, you can create bootstrap-{profile}.yml. So, if you are running with -Dspring.profiles.active=dev, you would have a bootstrap-dev.yml file that would contain the values for this profile.
The second and simpler approach would be to pass these to the VM as arguments. -Dcluster.name=foo -Dcluster.idx=bar

Resources