I have a spring boot web app which simply prints a property that is passed in a Kubernetes' ConfigMap.
This is my main class:
@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class DemoApplication {
private MyConfig config;
private DiscoveryClient discoveryClient;
@Autowired
public DemoApplication(MyConfig config, DiscoveryClient discoveryClient) {
this.config = config;
this.discoveryClient = discoveryClient;
}
@RequestMapping("/")
public String info() {
return config.getMessage();
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@RequestMapping("/services")
public String services() {
StringBuilder b = new StringBuilder();
discoveryClient.getServices().forEach((s) -> b.append(s).append(" , "));
return b.toString();
}
}
and the MyConfig
class is:
@Configuration
@ConfigurationProperties(prefix = "bean")
public class MyConfig {
private String message = "a message that can be changed live";
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Basically, by invoking root resource I always get:
a message that can be changed live
And invoking /services I actually get a list of Kubernetes services.
I'm creating the ConfigMap with kubectl create -f configmap-demo.yml
being the content:
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
bean.message: This is an info from k8
And the deployment with kubecetl create -f deploy-demo.yml
and the content is:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
labels:
app: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
# this service account was created according to
# https://kubernetes.io/docs/reference/access-authn-authz/rbac/#service-account-permissions
# point 5 - Grant super-user access to all service accounts cluster-wide (strongly discouraged)
serviceAccountName: i18n-spring-k8
containers:
- name: demo
image: aribeiro/sck-demo
imagePullPolicy: Never
env:
- name: JAVA_OPTS
value:
ports:
- containerPort: 8080
volumes:
- name: demo
configMap:
name: demo
The problem is that when accessing the root resource /
I always get the default hardcoded value and never what is defined in Kubernetes' ConfigMap.
Example project also with yaml files and Docker file available at https://drive.google.com/open?id=107IcwnYIbVpmwVgdgi8Dhx4nHEFAVxV8 .
Also checked the startup DEBUG logs and I don't see any error or clue why it should not work.
The Spring Cloud Kubernetes documentation is incomplete. It lacks the instruction to include this dependency to enable loading application properties from ConfigMap's:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-config</artifactId>
</dependency>
You are close:
1) Define the ConfigMap a bit differently so it contains a properties file. For example:
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
data:
demo.properties: |
bean.message: This is an info from k8
2) Mount the ConfigMap as a volume:
...
spec:
containers:
- name: demo
...
volumeMounts:
- name: config
mountPath: /demo/config
volumes:
- name: config
configMap:
name: demo
As result, a demo.properties
file as defined in the ConfigMap will "appear" in the /demo/config
directory inside the running container.
3) Add @PropertySource
annotation to the MyConfig
class:
@Configuration
@PropertySource("file:/demo/config/demo.properties")
@ConfigurationProperties(prefix = "bean")
public class MyConfig {
...
}