jenkins extended parameter plugin groovy script - groovy

The website for the plugin says that you can create a groovy script to run to determine the parameter list.
how is this resolved though? The instructions don't say anything.
In what context is the script run?
What am i supposed to return from the script?
What directory is the cwd of the script? is it the environment variable WORKSPACE?
there is an extra field called variable bindings. How is this used?

I had to dig into the source code to find the answer to these questions so i hope this helps everyone else.
1. In what context is the script run?
The script is run inside a groovy.lang.GroovyShell. This class is currently from the Groovy 1.8.5 library. here is an excerpt from the code:
// line 419 - 443 of the ExtendedChoiceParamaterDefinition
else if(!StringUtils.isBlank(groovyScript)) {
try {
GroovyShell groovyShell = new GroovyShell();
setBindings(groovyShell, bindings);
Object groovyValue = groovyShell.evaluate(groovyScript);
String processedGroovyValue = processGroovyValue(isDefault, groovyValue);
return processedGroovyValue;
}
catch(Exception e) {
}
}
else if(!StringUtils.isBlank(groovyScriptFile)) {
try {
GroovyShell groovyShell = new GroovyShell();
setBindings(groovyShell, bindings);
groovyScript = Util.loadFile(new File(groovyScriptFile));
Object groovyValue = groovyShell.evaluate(groovyScript);
String processedGroovyValue = processGroovyValue(isDefault, groovyValue);
return processedGroovyValue;
}
catch(Exception e) {
}
}
2. What am i supposed to return from the script?
As the above code demonstrates, the script should return a string with whatever delimiter you have specified in the paramater or a String[] array. here is a snippet of the function that processes the value returned from the script:
// line 450 - 465 of ExtendedChoiceParameterDefinition
private String processGroovyValue(boolean isDefault, Object groovyValue) {
String value = null;
if(groovyValue instanceof String[]) {
String[] groovyValues = (String[])groovyValue;
if(!isDefault) {
value = StringUtils.join((String[])groovyValue, multiSelectDelimiter);
}
else if(groovyValues.length > 0) {
value = groovyValues[0];
}
}
else if(groovyValue instanceof String) {
value = (String)groovyValue;
}
return value;
}
3. What directory is the cwd of the script? is it the environment variable WORKSPACE?
Does it matter? You can access the environment variable WORKSPACE from within the script using
Map<String, String> props = System.getenv();
def currentDir = props.get('WORKSPACE');
4. there is an extra field called variable bindings. How is this used?
This is a property file formatted key=value file. these names are then resolvable in the groovy script.
e.g.
key1=foo
prop2=bar

For parse json object (from parametres) to groovy object - Parsing and producing JSON
import groovy.json.JsonSlurper
def jsonSlurper = new JsonSlurper()
def object = jsonSlurper.parseText('{ "myList": [4, 8, 15, 16, 23, 42] }')
println(object.myList)

Related

How can I rectify static scope error when referencing a static variable in my groovy script?

I have a script which exports excel worksheets to csv, and I am attempting to escape commas, quotes and the like. I have created a static method that encodes the value of the cell being accessed. This method references a static variable which stores a Pattern.compile() value.
I have tried using def rxquote within the method but this gives me a different error stating that using static modifier before declaring my rxquote variable is illegal. Code is below followed by error message.
#!/usr/bin/env groovy
#Grab(group = 'org.apache.poi', module = 'poi', version = '4.1.0')
#Grab(group = 'org.apache.poi', module = 'poi-ooxml', version = '4.1.0')
import java.util.regex.*
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.apache.poi.ss.usermodel.*
static Pattern rxquote = Pattern.compile("\"")
static private String encodeValue(String value) {
boolean needQuotes = false;
if ( value.indexOf(',') != -1 || value.indexOf('"') != -1 ||
value.indexOf('\n') != -1 || value.indexOf('\r') != -1 ){
needQuotes = true;
}
Matcher m = rxquote.matcher(value)
if ( m.find() ) {
needQuotes = true
value = m.replaceAll("\"\"")
}
if ( needQuotes ) {
return "\"" + value + "\""
}
else return value;
}
//for(){
// ... export csv code (which works on its own)
//}
Error message on compile:
Apparent variable 'rxquote' was found in a static scope but doesn't refer to a local variable, static field or class. Possible causes:
You attempted to reference a variable in the binding or an instance variable from a static context.
You misspelled a classname or statically imported field. Please check the spelling.
You attempted to use a method 'rxquote' but left out brackets in a place not allowed by the grammar.
# line 27, column 17.
Matcher m = rxquote.matcher(value);
^
I've tried researching the issue and have found several similar questions here, but none of the solutions appear to apply to this situation as far as I can tell. I expected a static declaration of the variable to avoid this problem, but it seems there's something I'm missing.
you can't declare static variable in groovy script.
it's allowed only in groovy/java class.
error does not correspond to situation.
should be : Modifier 'static' not allowed here.
as workaround for static variables you can use some class:
class Const{
static String bar = 'test'
}
static private String foo() {
return Const.bar
}
foo()

How can I detect <EOF> of a "CSV data set config" in a groovy script sampler in JMeter?

I want to know, how it's possible to react on a in a groovy script.
I'm using a While controller to iterate through all lines in the CSV and generate JMeter variables before my actual testplan. I need to do this several times for different CSV files, therefore I don't want to stop the thread at in the While controller.
I imagined something like this:
if (${CSV_VALUE1} != "<EOF>")
{
def variableName = sprintf('%1$sVALUE',[${CSV_VALUE2}])
vars.put(variableName,${CSV_VALUE1});
}
CSV_VALUE1 is the value for the JMeter variable and CSV_VALUE2 is the name of the variable.
Testplan
I also appreciate better solutions, which iterate through every row of the CSV file and generate JMeter variables according to my conventions of it. A constraint is, that it has to be done in only one single thread group (=> No stopping of threads on EOF)
You can use "BeanShell" to read "CSV file", below is sample csv file, which has below data
answer1,0
answer2,1
answer3,2
...
answerX,X-1
To read this file use below "Beanshell" script
import java.text.*;
import java.io.*;
import java.util.*;
String filename = "oprosnik_" + vars.get("fileNum") + ".csv";
ArrayList strList = new ArrayList();
try {
File file = new File(filename);
if (!file.exists()) {
throw new Exception ("ERROR: file " + filename + " not found");
}
BufferedReader bufRdr = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF8"));
String line = null;
Integer i = 0;
while((line = bufRdr.readLine()) != null) {
strList.add(line);
i++;
}
bufRdr.close();
counter = Integer.parseInt(vars.get("counter"));
if (counter != i) {
String[] variables = strList.get(counter).split(",");
vars.put("answer",variables[0]);
vars.put("answerNum",variables[1]);
counter++;
vars.put("counter",Integer.toString(counter));
}
else {
vars.put("answer","<EOF>");
vars.put("eol","<EOF>");
vars.put("counter","0");
}
}
catch (Exception ex) {
IsSuccess = false;
log.error(ex.getMessage());
System.err.println(ex.getMessage());
}
catch (Throwable thex) {
System.err.println(thex.getMessage());
}
For reference check following link
You can handle this <EOF> case using If Controller and While Controller combination like:
While Controller: condition ${__javaScript("${CSV_VALUE1}" != "<EOF>",)}
If Controller: condition "${CSV_VALUE1}" != "<EOF>"
READ - Action Models
JSR223 Sampler
...
See Using the While Controller in JMeter article for details
It's possible to detect the end of file for a CSV data set by using a simple if-condition with quotes for the executing block:
if ("${CSV_VALUE1}" != "<EOF>")
{
//Code to execute if the condition is satisfied
}

groovy print environments from groovy.config

how do I print available environments from a config file? What is the form of the ojbect ConfigSlurper creates?
I tried
def config2 = new ConfigSlurper().parse(new File('obieeadmincfg.groovy').toURL())
config2.config.environments.each { println ${it} }
and
println prettyPrint(toJson(config2))
and
for ( i in 0 ..config2.config.environments.size()-1)
println config2.config.environments[i]
groovy.config
//Config3.groovy
obieadmin {
//default values
serverurl = "http://default.mycompany.com"
}
environments {
pldev01 {
obieeadmin {
serverurl = 'devgdwobi03.x.com'
}
}
plsbx02 {
obieeadmin {
serverurl = 'devgdwobi03.x.com'
}
}
}
I'm afraid you can't do this out-of box.
But using a bit of Groovy Metaprogramming it's achievable.
Groovy config slurper parses proper Groovy file, and you can do the same with GroovyShell. You can catch call to environment method providing closure in binding. In that closure you have to collect all top-level method calls(with same methodMissing).
Providing base script with property and method missing handlers, you can suppress runtime errors, and execute script without much care to other properties.
Not the nicest path, but it works.
package test
import org.codehaus.groovy.control.CompilerConfiguration
class A extends Script {
def propertyMissing(String name) { null }
def propertyMissing(String name, def arg) {}
def methodMissing(String name, def args) {}
#Override Object run() { null }
}
class NameCollector {
def envs = []
def methodMissing(String name, def args) { envs << name }
}
// configure interceptors.
def configuration = new CompilerConfiguration()
configuration.scriptBaseClass = 'test.A'
def nc = new NameCollector()
def environments = { Closure it ->
it.delegate = nc;
it.resolveStrategy = Closure.DELEGATE_ONLY
it()
}
// execute config script
new GroovyShell([environments: environments] as Binding, configuration).evaluate(new File("config.groovy"))
nc.envs // Return, print them.
Not sure if this is going to work forever but currently you can do it by overriding the setting for the 'environments' conditional block, like this:
def config = """environments {
dev {
foo = "bar"
}
prod {
foo = "baz"
}
}"""
def configSlurper = new ConfigSlurper()
configSlurper.registerConditionalBlock('environments', null)
assert configSlurper.parse(config).environments.keySet() == ['dev', 'prod'].toSet()

Groovy DSL - shortcut for creating objects

is there a way in Groovy to replace some code like the below:
Task a = new Task('a')
Process p = new Process('p')
with something easier, like:
task a
process p
where task and process can be method calls that create the object and return it or add it to the script Map.
The main problem I currently have is that I cannot use a because it is not defined.
To create objects and name them without assigning to a variable, you can use a binding. Create and keep a reference to the a closure's binding, and have the utility methods task and process associate the new instance with the name. For example:
def scriptBinding = new Binding()
def task = { String name ->
scriptBinding[name] = new Task(name)
}
def process = { String name ->
scriptBinding[name] = new Process(name)
}
def script = {
task 'a'
process 'b'
println a
println b
}
script.binding = scriptBinding
script()
Note that you have to quote a and b so they are interpreted as strings instead of undefined variables. If you'd like to omit quotes, you can use a custom Binding object that evaluates undefined symbols as their string representation like this:
class SymbolAsStringBinding extends Binding {
Object getVariable(String name) {
try {
super.getVariable(name)
} catch (MissingPropertyException e) {
name
}
}
boolean hasVariable(String name) {
true
}
}
With that addition, you can write the script as:
def script = {
task a
process b
println a
println b
}
Try this:
class Task {
String name
Task(name) { this.name = name }
String toString() { "task: $name".toString() }
}
def propertyMissing(String name) { this.getBinding()[name] = name }
def task(name) { this.getBinding()[name] = new Task(name) }
task a
println a
this will produce:
task: a
essentially when you reach the
task a
statement the propertyMissing() will put in the binding a variable named a with content it's name.
Later the task() function will replace the variable a in the binding with the new Task passing as name the name of the missing varible a

runtime invocation of method with arguments in groovy

For simplicity let's say I have code similar to this:
def testMethod(String txt) {
return txt;
}
public String evaluate(String expression) {
//String result = "${testMethod('asdasdasd')}";
String result = "${expression}";
return result;
}
I need the expression value which is passed to method "evaluate" to be executed.
in case of calling
// everything works perfectly well,
String result = "${testMethod('samplestring')}";
in case of calling
// (when expression = testMethod) - everything works perfectly well,
String result = "${expression}"("samplestring");
in case of calling
// (when expression = testMethod('samplestring')) - it's not working.
// I see testMethod('samplestring') as the result, but I need it to be evaluated.
String result = "${expression}"
How can I do that?
Thanks.
Thus should work as well;
Eval.me( "${expression}" )
Edit
As pointed out, this won't work as it stands, you need to pass in the script that contains the method with Eval.x like so:
def testMethod(String txt) {
txt
}
public String evaluate(String expression) {
String result = Eval.x( this, "x.${expression}" )
result
}
println evaluate( "testMethod('samplestring')" )
That will print samplestring
You may use the GroovyShell class for this purpose, but you will need to define a Binding AFAIK. This works in the Groovy Console:
def testMethod(txt) {
"$txt!";
}
def evaluate(String expression) {
def binding = new Binding(['testMethod': testMethod])
new GroovyShell(binding).evaluate(expression)
}
evaluate('testMethod("Hello World")');

Resources