Jenkins. Inserting environment variable in the file - shell

I have a .env file that contains the following data
API_URL=${API_URL}
API_KEY=${API_KEY}
API_SECRET=${API_SECRET}
Setting environment variables in Jenkins and passing them to the pipeline is clear. But it is not clear how do I replace ${API_URL}, ${API_KEY} & ${API_SECRET} in the .env file with their values in the Jenkins environment variable? Plus, how do I loop through all the Jenkins variables?

This basically requires two steps:
Get all environment variables
Replace values of environment variables in the template (.env) file
Let's start with #2, because it dictates which kind of data #1 must produce.
2. Replace variables in a template
We can use Groovy's SimpleTemplateEngine for this task.
def result = new SimpleTemplateEngine().createTemplate( templateStr ).make( dataMap )
Here templateStr is the template string (content of your .env file) and dataMap must be a Map consisting of string keys and values (the actual values of the environment variables). Getting the template string is trivial (use Jenkins readFile step), reading the environment variables into a Map is slightly more involved.
1. Read environment variables into a Map
I wrote "slightly more involved" because Groovy goodness makes this task quite easy aswell.
#Chris has already shown how to read environment variables into a string. What we need to do is split this string, first into separate lines and then each line into key and value. Fortunately, Groovy provides the member function splitEachLine of the String class, which can do both steps with a single call!
There is a little caveat, because splitEachLine is one of the functions that doesn't behave well in Jenkins pipeline context - it would only return the first line. Moving the critical code into a separate function, annotated with #NonCPS works around this problem.
#NonCPS
Map<String,String> envStrToMap( String envStr ) {
def envMap = [:]
envStr.splitEachLine('=') {
envMap[it[0]] = it[1]
}
return envMap
}
Finally
Now we have all ingredients for letting Jenkins cook us a tasty template soup!
Here is a complete pipeline demo. It uses scripted style, but it should be easy to use in declarative style as well. Just replace node with a script block.
import groovy.text.SimpleTemplateEngine
node {
// TODO: Replace the hardcoded string with:
// def tmp = readFile file: 'yourfile.env'
def tmp = '''\
API_URL=${API_URL}
API_KEY=${API_KEY}
API_SECRET=${API_SECRET}'''
withEnv(['API_URL=http://someurl', 'API_KEY=123', 'API_SECRET=456']) {
def envMap = getEnvMap()
echo "envMap:\n$envMap"
def tmpResolved = new SimpleTemplateEngine().createTemplate( tmp ).make( envMap )
writeFile file: 'test.env', text: tmpResolved.toString()
// Just for demo, to let me see the result
archiveArtifacts artifacts: 'test.env'
}
}
// Read all environment variables into a map.
// Here, #NonCPS must NOT be used, because we are calling a Jenkins step.
Map<String,String> getEnvMap() {
def envStr = sh(script: 'env', returnStdout: true)
return envStrToMap( envStr )
}
// Split a multiline string, where each line consists of key and value separated by '='.
// It is critical to use #NonCPS to make splitEachLine() work!
#NonCPS
Map<String,String> envStrToMap( String envStr ) {
def envMap = [:]
envStr.splitEachLine('=') {
envMap[it[0]] = it[1]
}
return envMap
}
The pipeline creates an artifact "test.env" with this content:
API_URL=http://someurl
API_KEY=123
API_SECRET=456

You can access variables by executing simple shell in scripted pipeline:
def variables = sh(script: 'env|sort', returnStdout: true)
Then programatically in Groovy convert it to list and iterate using each loop.
According to replacing variables, if you're not using any solution which can access env variables then you can use simple text operations like executing sed on that file.

Related

Terraform: Need to pass a bash script to 3 different Launch templates's userdata

I am trying to pass to 3 AWS launch template userdata a bash script. This script calls other scripts from Github depending on a specific variable. Since each launch template must call different scripts what is the best way to accomplish it. I am currently trying to configure a data source template_file but I canĀ“t find a way to do what I need.
This is a piece of the bash script where I put a variable that need to change its value depending on which launch template is being built every time:
#------------------------------------------------------------------------------------------
# Define here scripts (separated with 1 space) that will be executed on first run:
AMI_SCRIPTS="ami_base_lynis.sh ${ami_script}"
#------------------------------------------------------------------------------------------
download_and_run_scripts
This is the template file data source:
data "template_file" "AMIs"{
template = "${file("../AMIs/s1_aws_userdata.sh")}"
vars = {
ami = var.dci_appserver_ami
}
}
And this is the user data attribute:
user_data_base64 = base64encode(data.template_file.AMIs.rendered)
This is not working for me as it will replace the variable has the same value for all 3 launch templates. How can I assign each time a different value?
The syntax you used for user_data_base64 tells me that you're using Terraform v0.12 or later, so you should no longer use template_file as shown in the template_file documentation:
In Terraform 0.12 and later, the templatefile function offers a built-in mechanism for rendering a template from a file. Use that function instead, unless you are using Terraform 0.11 or earlier.
Because of that, I'm going to answer using the templatefile function instead.
Inside each of your launch template resource blocks, you can call templatefile with different values for the template variables in order to get a different result each time:
resource "aws_launch_template" "example1" {
# ...
user_data = base64encode(templatefile("${path.module}/../AMIs/s1_aws_userdata.sh", {
ami = var.dci_appserver_ami
ami_script = "script1.sh"
}))
}
resource "aws_launch_template" "example2" {
# ...
user_data = base64encode(templatefile("${path.module}/../AMIs/s1_aws_userdata.sh", {
ami = var.dci_appserver_ami
ami_script = "script2.sh"
}))
}
resource "aws_launch_template" "example3" {
# ...
user_data = base64encode(templatefile("${path.module}/../AMIs/s1_aws_userdata.sh", {
ami = var.dci_appserver_ami
ami_script = "script3.sh"
}))
}
You could in principle factor out constructing the templates into a local value if you want to do it more systematically, but since your question didn't include any indication that you are doing anything special with the launch templates here I've just written the simplest possible approach, where each launch template has its own template-rendering expression.

Jmeter - How to put each member of the forEach loop into variable

Based on this thread - Jmeter - how to return multiple ID(s) based on the array (match JSON path with array)
I managed to get ID's, for every single member of the array.
Now I need to alternate the code and to have a variable for every single ID.
What i tried is:
vars.get('array').replace("[", "").replace("]", "").split(", ").each { country ->
def result = new groovy.json.JsonSlurper().parse(prev.getResponseData()).payload.find { entry -> entry.name == country.trim() }
vars.put("tim" + ${__counter(,)}, result.id as String);
}
But, I am only able to get a single variable.
What should I do in order to save every single result.id, into variables like:
tim1, tim2, tim3...
Don't inline JMeter Functions or Variables into Groovy scripts.
As per JMeter Documentation:
The JSR223 test elements have a feature (compilation) that can significantly increase performance. To benefit from this feature:
Use Script files instead of inlining them. This will make JMeter compile them if this feature is available on ScriptEngine and cache them.
Or Use Script Text and check Cache compiled script if available property.
When using this feature, ensure your script code does not use JMeter variables or JMeter function calls directly in script code as caching would only cache first replacement. Instead use script parameters.
So I would rather recommend amending your code as follows:
vars.get('array').replace("[", "").replace("]", "").split(", ").eachWithIndex { country, index ->
def result = new groovy.json.JsonSlurper().parse(prev.getResponseData()).payload.find { entry -> entry.name == country.trim() }
if (result != null) {
vars.put("tim" + (index + 1), result.id as String);
}
}
Demo:
More information: Apache Groovy - Why and How You Should Use It

How do you output variables to Ansible? [duplicate]

This question already has answers here:
How to pass terraform outputs variables into ansible as vars_files?
(5 answers)
Closed 2 years ago.
I followed the answer here in this question and
I have created a template file tf_ansible_vars_file.yml.tpl like below
tf_share_location: "${share_location}"
and a terra_render.tf like below
# Define an Ansible var_file containing Terraform variable values
data "template_file" "tf_ansible_vars_file" {
template = "${file("/home/deployment_root/app4/tf_ansible_vars_file.yml.tpl")}"
vars = {
share_location = var.share_location
# gitlab_backup_bucket_name = aws_s3_bucket.gitlab_backup.bucket
}
}
# Render the Ansible var_file containing Terrarorm variable values
resource "local_file" "tf_ansible_vars_file" {
content = data.template_file.tf_ansible_vars_file.rendered
filename = "/home/deployment_root/app4/tf_ansible_vars_file.yml"
}
I already have a variables.tf file in which i have declared that variable
variable "share_location" {
type = string
}
and in the terraform.tfvars gave the value as null
share_location = null
when i run terraform apply i get the below error
Error: failed to render : <template_file>:1,23-37: Unknown variable; There is no variable named "share_location".
on terra_render.tf line 2, in data "template_file" "tf_ansible_vars_file":
2: data "template_file" "tf_ansible_vars_file" {
My understanding is it will create a file as mentioned in that answer, but it is not working.
How do you output variables to Ansible?
If you are generating a supported format like YAML, you do not need a template file.
You can generate YAML directly from Terraform data structures as follows:
resource "local_file" "tf_ansible_vars_file" {
content = yamlencode(
{
tf_share_location = var.share_location
# gitlab_backup_bucket_name = aws_s3_bucket.gitlab_backup.
}
)
filename = var.ansible_vars_filename
}
variable "ansible_vars_filename" {
type = string
default = "/home/deployment_root/app4/tf_ansible_vars_file.yml"
}
Applying that with -var="share_location=example_location" will yield a file like this:
"tf_share_location": "example_location"
Whether or not the quoting the configuration variable names matters depends on Ansible. It shouldn't as it's still valid YAML. Terraform's yamlencode quotes those regardless of whether they need to be, which is regrettable.
I have extracted ansible_vars_filename to a variable as you may want to make that configurable.
I have also left in the commented out gitlab_backup_bucket_name as adding it to the YAML file is as simple as uncommenting it.
You can learn more about yamlencode here:
https://www.terraform.io/docs/configuration/functions/yamlencode.html
In the below code, instead of var.share_location , i need to give the variable that i used in terraform in my case
${data.azurerm_storage_account.new.name}/sharename and after that i can remove that from variables.tf as well as terraform.tfvars as i am getting the value generated. Thanks
Old code :
data "template_file" "tf_ansible_vars_file" {
template = "${file("/home/deployment_root/app4/tf_ansible_vars_file.yml.tpl")}"
vars = {
share_location = var.share_location
# gitlab_backup_bucket_name = aws_s3_bucket.gitlab_backup.bucket
}
}
New Code:
# Define an Ansible var_file containing Terraform variable values
data "template_file" "tf_ansible_vars_file" {
template = "${file("/home/deployment_root/app4/tf_ansible_vars_file.yml.tpl")}"
vars = {
share_location = "${data.azurerm_storage_account.new.name}/sharename"
# gitlab_backup_bucket_name = aws_s3_bucket.gitlab_backup.bucket
}
}

How to create local variables at runtime for any keyword test in TestComplete

I need to create a set of local variables at the beginning of a Keyword test and then use them later while executing the Test.
Is there any possibility to create local variables dynamically as like project variables which can be created dynamically.
Project.variables.<variable_name> = "project_variable_value"
in the similar fashion can we create any variable associated to any keyword test
Keywordtests.<generic_keyword_test_name>.variables.<variable_name> = "local_variable_value"
Sure, you can do this. Please see this example:
function Test11()
{
if (KeywordTests.Test1.Variables.VariableExists("MyVariable") == false) {
KeywordTests.Test1.Variables.AddVariable("MyVariable", "String");
}
KeywordTests.Test1.Variables.MyVariable = "test value";
Log.Message(KeywordTests.Test1.Variables.MyVariable);
}
Information on the AddVariable method can be found in the AddVariable Method help topic.

GString inside a Gstring in gradle/groovy

I need to use a variable as a part of a string that will be used to address another variable in a gstring.
In syntesys, what I'd like to do is: ${${it}_checkout}
The whole code line would be:
def checkouts = repos.collect{"${it} = ${${it}_checkout} "}
With repos being a list of repositories to checkout.
Each repo has an property called <repo>_checkout.
For instance, if I have two repos, called foo and bar, I'll have two variables called foo_checkout and bar_checkout, containing the branches to be checkouted.
I'm trying to construct the following string: "foo=$foo_checkout bar=$bar_checkout".
That will be translated to "foo=master bar=dev"
Is there a way ?
Yeh, just do:
def checkouts = repos.collect{ "$it = ${it}_checkout" }
Or, depending on how you declare your properties, you can do:
root_checkout = 'woo'
repo_checkout = 'yay'
['root', 'repo'].collect { r -> "$r = ${getProperty(r + '_checkout')}" }

Resources