Fernando Lozano
Fernando Lozano

Reputation: 392

Using environment variables to fill a Map in a @ConfigurationProperties

I have this @ConfigurationProperties:

@ConfigurationProperties(prefix = "myapp")
public class CustomProperties {

    private Map<String, String> namespace = new HashMap<>();

    public Map<String, String> getNamespace() {
        return namespace;
    }
}

Before I start the application, I put these arguments as a program arguments:

--myapp.namespace.namespace1.connection="xxxx"
--myapp.namespace.namespace1.url="yyy"
--myapp.namespace.namespace2.topic="zzz"
--myapp.namespace.namespace2.id="ccc"

And it works successfully, the namespace map will contain four entries:

{namespace2.id=ccc, namespace1.connection=xxxx, namespace2.topic=zzz, namespace1.url=yyy}

Now I am "dockerizing" the application. How can I make it work with environment variables? I tried:

MYAPP_NAMESPACE_NAMESPACE1.CONNECTION="xxxx"
MYAPP_NAMESPACE_NAMESPACE1.URL="yyy"
MYAPP_NAMESPACE_NAMESPACE2.TOPIC="zzz"
MYAPP_NAMESPACE_NAMESPACE2.ID="ccc"

and

MYAPP_NAMESPACE_NAMESPACE1_CONNECTION="xxxx"
MYAPP_NAMESPACE_NAMESPACE1_URL="yyy"
MYAPP_NAMESPACE_NAMESPACE2_TOPIC="zzz"
MYAPP_NAMESPACE_NAMESPACE2_ID="ccc"

And it doesn't work. Can you help me?

Thanks Fernando.

Upvotes: 5

Views: 7988

Answers (3)

devatherock
devatherock

Reputation: 4991

Just in case it helps someone else using Kubernetes, there is a simpler way, than environment variables, to inject complex config into spring boot applications. We can have a config map entry named application.yml with the required config, inject the entry into the container through a volume and set the environment variable SPRING_CONFIG_ADDITIONAL_LOCATION to point to that location

Steps:

  • Create a config map named demo-config which contains a multi-line data with the key application.yml
  • Create a volume named config-volume from config map demo-config mapping only the data key application.yml
  • Mount the config volume into the container at /etc/config
  • Set the environment variable SPRING_CONFIG_ADDITIONAL_LOCATION to /etc/config/ - the trailing slash is required
  • The app starts up, reads the config from /etc/config/application.yml and logs the line com.example.demo.AppProps - Namespace: {namespace2.id=ccc, namespace1.connection=xxxx, namespace2.topic=zzz, namespace1.url=yyy}

Test Configuration class:

package com.example.demo;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "myapp")
public class AppProps {

    private Map<String, String> namespace = new HashMap<>();
    
    @PostConstruct
    public void init() {
        log.info("Namespace: {}", namespace);
    }
}

Config map:

apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-config
data:
  application.yml: |
    myapp:
      namespace:
        namespace1.connection: xxxx
        namespace1.url: yyy
        namespace2.topic: zzz
        namespace2.id: ccc

Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-deployment
  labels:
    app: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      volumes:
        - name: config-volume
          configMap:
            name: demo-config
            items:
              - key: application.yml
                path: application.yml
      containers:
        - name: app
          image: devatherock/demo:0.1.0-local.1
          imagePullPolicy: Never
          ports:
            - containerPort: 8080
          volumeMounts:
            - name: config-volume
              mountPath: /etc/config  
          env:
           - name: SPRING_CONFIG_ADDITIONAL_LOCATION
             value: "/etc/config/"

Upvotes: 4

Mohit Singh
Mohit Singh

Reputation: 524

You can map these properties to the environment variables in spring profile i.e. application or bootstrap properties (like in application.properties/yml or bootstrap.properties/yml file) by using the placeholders. You can map to the environment variable placeholders as below. These will resolve to the corresponding environment variable.

myapp.namespace.namespace1.connection=${MYAPP_NAMESPACE_NAMESPACE1_CONNECTION}
myapp.namespace.namespace1.url=${MYAPP_NAMESPACE_NAMESPACE1_CONNECTION}
myapp.namespace.namespace2.topic=${MYAPP_NAMESPACE_NAMESPACE1_CONNECTION}
myapp.namespace.namespace2.id=${MYAPP_NAMESPACE_NAMESPACE2_ID}

If you know know the properties name in advance and know only the prefix:

You only need to map with one placeholder and provide environmental variables by appending that placeholder and with underscores like:

myapp.namespace=${MYAPP_NAMESPACE}

If you provide env variable,

MYAPP_NAMESPACE_NAMESPACE1_CONNECTION=xxx

Springboot is smart enough to resolve this to

myapp.namespace.namespace1.connection==xxx

You just have to follow the convention to use underscores (_) only which will be resolved to dots (.).

We are using it in our application. I also tested this particular scenario and when it prints the namespaces as below:

{namespace1.connection=xxxx}

I hope I understood correctly what you want to achieve here.

Upvotes: 1

Spider
Spider

Reputation: 455

I think you can initialize it just by doing @Value("${MYAPP_NAMESPACE_NAMESPACE1_CONNECTION}") or whatever the key in your env var is

Then give it the variable you want it in like so

@Value("${variable}")
private String variable;

Upvotes: -1

Related Questions