Dnyaneshwar Jadhav
Dnyaneshwar Jadhav

Reputation: 353

Groovy string replacing token with dollar symbol from variable not working

props = readProperties  file: "src/main/resources/application-DEV.properties";
def DB_HOST         = (props['com_db_url'].split(':')[2]).replace('//', '');
def DB_USER         = props['com_db_username'];
def DB_PASSWORD     = props['com_db_password'];
def DB_PORT         = (props['com_db_url'].split(':')[3]).split('/')[0];
def jsonData = "{'$DB_HOST':'${DB_HOST}','$DB_USER':'${DB_USER}','$DB_PASSWORD':'${DB_PASSWORD}','$DB_PORT':'${DB_PORT}'}";
String filenew = readFile('dev/values-dev.yaml')
def jsonObject = readJSON text: jsonData;

jsonObject.each { key, value ->
    def _key = "${key}";
    _key = '\\\\'+_key
    echo "$key : $value, custom key : ${_key}"
    filenew = filenew.replaceAll("${_key}", '$value')
}

My values-dev.yaml file as follows

resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi
config:
  autoDiscoverDatabases: true
  datasource:
    host: $DB_HOST
    user: $DB_USER
    password: $DB_PASSWORD
    port: $DB_PORT
    sslmode: "require"
    database: "ibmclouddb"

I have tried multiple ways in last 4 days to replace token using for loop, but hard luck. With following way it is working for me

//filenew = filenew.replaceAll('\\$DB_HOST', jsonObject.DB_HOST)
//filenew = filenew.replaceAll('\\$DB_USER', jsonObject.DB_USER)
//filenew = filenew.replaceAll('\\$DB_PASSWORD', jsonObject.DB_PASSWORD)
//filenew = filenew.replaceAll('\\$DB_PORT', jsonObject.DB_PORT)

Purposefully I have prepended $ symbol variables in JSON data, i.e. in above jsonData variable declared initially.

But I wanted to achieve this via a generic way.

Upvotes: 1

Views: 1272

Answers (2)

Catalin
Catalin

Reputation: 484

Here a simpler solution which works in a generic way as you wish.

import groovy.json.JsonSlurper

String props = '''
    user: $DB_USER
    pass: $DB_PASS
'''

def DB_USER = 'com_db_username'
def DB_PASS = 'com_db_password'
def jsonData = $/
    {
        "$$DB_USER":"${DB_USER}",
        "$$DB_PASS":"${DB_PASS}",
    }/$

//println jsonData

def jsonObject =  new JsonSlurper().parseText(jsonData)
jsonObject.each { key, value ->
    props = props.replaceAll(/\$key/, value)
}
println props

with the output:

    user: com_db_username
    pass: com_db_password

The trickiest part is the regex expression and the escaping and what kind of string to use for it for a convenient escaping. In the regex expression /\$key/ the exapnded value of $key will be $DB_USER which will output the $ sign that must be also escaped with the \ before.

I had to adapt your code, to work without a Jenkins server. One error I had to fix is that your JSON do not use double-quotes for key-values. To fix it without mess up the interpolation, I used a groovy dollar-slashy string which is more readable and flexible. In the $-slashy strings you need to escape only the $ signs, everything else can be left as it is without escaping.
For simplicity, instead of yaml file I use just string, for the properties I defined some variables and I reduced everything to 2 variables.

Upvotes: 1

cfrick
cfrick

Reputation: 37008

All the replacements can be done in one step and there is no need to use an interim JSON map.

Just build a map with all the replacements you know. Then use replaceAll with a regexp, that catches your replacement keys and use a closure to handle the replacement. The closure simply looks up the key in your replacements and has some means to generate some error, if a replacement is unknown (or you could just write the key back).

def replacements = [
  DB_USER: "the user",
  // ...
]

def yaml = '''
config:
  autoDiscoverDatabases: true
  datasource:
    host: $DB_FAILS
    user: $DB_USER
'''

println yaml.replaceAll(/\$([A-Z_]+)/, { _, k -> replacements.getOrDefault(k, "FAILED TO REPLACE $k") })
// →
// config:
//   autoDiscoverDatabases: true
//   datasource:
//     host: FAILED TO REPLACE DB_FAILS
//     user: the user

Upvotes: 2

Related Questions