Catching an exception when loading a groovy file - groovy

I have these 3 groovy files:
A.groovy:
// ...
stage("Test")
{
throw new Exception("This script fails")
}
B.groovy:
// ...
stage("Test")
{
// Nothing, want this to pass
}
main.groovy:
// ...
m = [:]
status = [:]
scripts = ["A", "B"]
for script in scripts
{
m["${script}"] =
{
stage("${script}")
{
try
{
load "${script}.groovy"
status["${script}"] = true
}
catch (Exception e)
{
status["${script}"] = false
}
}
}
}
parallel m
for result_iterator in status:
print "${result_iterator.key} resulted in ${result_iterator.value}"
The code above is a sketch of the real code =)
When I run main.groovy to see the results in the status dictionary, I only get to see B. A threw an exception and thus it didn't add itself to the dictionary. Is there a way I can catch A's exception somehow?

Problem 1
You got bitten by the way Groovy closures capture variables outside of their scope. The references to script inside the closure will receive the value of script at the time when the closure is run, which will be the last value of script. So both closures actually load "B.groovy" and the exception is never thrown!
The fix is simple, just create a local variable inside of the loop that captures the current value of script:
for( script in scripts ) {
def theScript = script
// Now only use theScript inside of the closure!
}
Problem 2
You are not serializing write access to the status variable from parallel threads. This could result in very nasty, hard-to-reproduce bugs.
The fix is to use Map.asSynchronized() to create a synchronized map:
status = [:].asSynchronized()
Complete 'main.groovy' fix
m = [:]
status = [:].asSynchronized() // because multiple threads write to this variable
scripts = ["A", "B"]
for( script in scripts ) {
def theScript = script // Important to pass the current value into the closure!
m[ theScript ] = {
stage( theScript ) {
try {
load "${theScript}.groovy"
status[ theScript ] = true
}
catch (Exception e) {
status[ theScript ] = false
}
}
}
}
parallel m
for( result_iterator in status ) {
print "${result_iterator.key} resulted in ${result_iterator.value}"
}
Note: I have also removed the unnecessary string interpolations.

Related

Skip MissingPropertyException while using GroovyShell.evaluate

Is there a way to skip MissingPropertyException while using GroovyShell.evaluate?
def sharedData = new Binding()
def shell = new GroovyShell(sharedData)
shell.evaluate("a=5; b=1") // works fine
// How to not get MissingPropertyException, or silently ignore property 'a'
shell.evaluate("a; b=1") // MissingPropertyException for 'a'
I know about the Expando solution, is there a way to do it without defining a class?
A very minimal approach would be to override Binding.getVariable.
Note that this is very straight forward: "all exceptions" are ignored - you might want to have better logging or more accurate error handling.
import groovy.lang.*
class NoOpBinding extends Binding {
#Override
Object getVariable(String name) {
try {
return super.getVariable(name)
}
catch (Throwable t) {
println "Ignoring variable=`$name`"
return null
}
}
}
def shell = new GroovyShell(new NoOpBinding())
shell.evaluate("a; b=1") // MissingPropertyException for 'a'
// → Ignoring variable=`a`
println shell.getVariable('b')
// → 1
You could do something like this:
def binding = [:].withDefault { }
def shell = new GroovyShell(binding as Binding)
shell.evaluate 'a; b=1'

Jenkins pipeline parallel step with dynamically built list

I have a Jenkins job that has some weird behavior and I'm not sure what's going on.
node(){
myFuncs = [:]
String[] myFuncsToCall = ["one", "two"]
for (int i = 0; i < myFuncsToCall.size(); i++) {
func = myFuncsToCall[$i]
myFuncs[func] = {
"${func}"()
}
}
parallel myFuncs
}
def one(){
echo "one"
}
def two(){
echo "two"
}
It's interesting, because the job output shows parallel branching for both 'one' and 'two', but only calls method 'two' for both parallel branches.
I imagine there's something groovy related that I'm missing here, but am hoping it's possible to dynamically call method names like this so I can have large sets of dynamic inputs to jobs that need to run with variable inputs.
func is not a local variable but a dynamically defined property on the main script. If no type or the keyworkd def specified to the left from the var name then groovy will try to resolve it and Jenkins configures the main script in such a way that func will be assinged to the main script as a new property. Basically, it creates a global var since there is only one instance of the main script (Jenkinsfile itself).
Therefore, this global var gets changed in the loop and stores the last result only ("two"). When it comes to running the closures every closure the value from the global var. Hence, "two".
To change this behaviour simple add def or explicit type to make func a local variable.
node(){
def myFuncs = [:]
String[] myFuncsToCall = ["one", "two"]
for (int i = 0; i < myFuncsToCall.size(); i++) {
def func = myFuncsToCall[i]
myFuncs[func] = {
"${func}"()
}
}
parallel myFuncs
}
def one(){
echo "one"
}
def two(){
echo "two"
}

Jenkins Library / Groovy Script - Wrap Code dynamically

I'm developing a Jenkins shared library right now.
I wasn't able to figure how to easily "wrap" a code inside a function without copy-pasting the whole code. For example: If a developer sets a value to true, then I want to wrap the whole code inside a function. Right now I want to use this to allow e.g. the gitlabIntegration to be turned off from the Jenkinsfile.
Example:
// vars/stageWrapper.groovy
def call(Map parameters = [:], body) {
stage(stageName) {
if (pushtoGitlab) {
gitlabCommitStatus(stageName) {
if (!containerName) body()
else {
container(containerName) {
body()
}
}
}
} else {
if (!containerName) body()
else {
container(containerName) {
body()
}
}
}
}
}
let the user select if the stage should be pushed to gitlab via the gitlabCommitStatus wrapper.
switch to a specified container or use default container (if none is specified)
To realize this I currently repeat the code, which I really don't like...
Is there any way of achieving the same, but without repeating the same code over and over?
Thank You!
In Groovy you can reuse a Closure in different DSL-Builders by setting it's delegate to builder's delegate.
Something like this should work:
def containerNameBody = { body ->
if (!containerName)
body()
else
container(containerName) {
body()
}
}
def call(Map parameters = [:], body) {
stage(stageName) {
containerNameBody.delegate = delegate
if (pushtoGitlab)
gitlabCommitStatus(stageName) {
containerNameBody body
}
else
containerNameBody body
}
}
How about following approach to pass down the param into function, then decide how to do inside the function by the param value.
def gitHub(gitHubOn) {
}
def gitLab(gitLabOn) {
}
def call(Map parameters = [:], body){
//some code....
foo=bar
gitLab(parameters.gitLabOn)
gitHub(parameters.gitHubOn)
body()
}

to def or not to def #groovy

There is a groovy script that has a function defined and used in multiple threads.
I found that time to time it mixes some variable values with other threads.
The problem appears when developer forgot to declare variable like this:
def f ( x ) {
y = "" + x
println y
}
The problem disappears when developer declares variable
def f ( x ) {
def y = "" + x
println y
}
In classes there is no way to use undefined variables.
The reason is that in the scripts the undefined variable acts as an instance variable of the script-class. Actually this is a binding for external variables that could be passed into the script.
Here is a part of script that demonstrates the problem of using undefined variables in several threads.
void f(String x){
y=""+x; //if you put def at this line it'll work fine
Thread.sleep(333);
//usually developers expected that `y` is a local variable,
//but without declaration it belongs to script-class
if( !y.equals(x) ) println("failure: x=$x y=$y");
}
//thead 1 start
Thread.start{
for(int i=0;i<20;i++){
f( i.toString() )
Thread.sleep(100);
}
}
//thead 2 start
Thread.start{
for(int i=0;i<20;i++){
f( i.toString() )
Thread.sleep(150);
}
}
//main thread sleep.
Thread.sleep(2000);
println("done");
this code will print out failures when x not equals y (literally)
Write a compiler configuration using a scriptBaseClass to disallow undeclared variables and the usage of the script's own binding.
This is the base script (my DefBase.groovy file):
abstract class NoUndefShallPass extends Script {
void setProperty(String name, val) {
// seems like groovy itself set 'args' in the binding, probably from CL
assert name == 'args',
"Error in '$name'; variables should be declared using 'def'"
}
}
def configuration = new org.codehaus.groovy.control.CompilerConfiguration()
configuration.setScriptBaseClass(NoUndefShallPass.class.name)
def shell = new GroovyShell(this.class.classLoader, new Binding(), configuration)
shell.evaluate new File('/tmp/Defing.groovy')
And the script. It will thrown an AssertionError if the setProperty tries to use the binding:
void f(String x){
y=""+x; //if you put def at this line it'll work fine
Thread.sleep(333);
if( !y.equals(x) ) println("failure: x=$x y=$y");
}
def t1 = Thread.start{
20.times { i ->
f( i.toString() )
Thread.sleep(100);
}
}
def t2 = Thread.start{
20.times { i ->
f( i.toString() )
Thread.sleep(150);
}
}
Thread.sleep(2000);
t1.join()
t2.join()
println("done");
it's poorly described here as "the binding"
http://groovy.codehaus.org/Scoping+and+the+Semantics+of+%22def%22

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

Resources