What is the effect of #NonCPS in a Jenkins pipeline script - jenkins

I have a pipeline script in Jenkins.
I used to get this exception:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException:
Scripts not permitted to use method groovy.json.JsonSlurperClassic
parseText java.lang.String
I looked the exception up and I found some indications that I should annotate the method where theexception occurs with #NonCPS. I did this, without really understanding what this does.
After that however, an Exception that I was throwing in that method was no longer caught by a try clause.
So what's the idea behind #NonCPS? What are the effects of using it?

The exception that you are seeing is due to script security and sandboxing. Basically, by default, when you run a pipeline script, it runs in a sandbox which only allow usage of certain methods and classes. There are ways to whitelist operations, check the link above.
The #NonCPS annotation is useful when you have methods which use objects which aren't serializable. Normally, all objects that you create in your pipeline script must be serializable (the reason for this is that Jenkins must be able to serialize the state of the script so that it can be paused and stored on disk).
When you put #NonCPS on a method, Jenkins will execute the entire method in one go without the ability to pause. Also, you're not allowed to reference any pipeline steps or CPS transformed methods from within an #NonCPS annotated method. More information about this can be found here.
As for the exception handling: Not 100% sure what you are experiencing; I've tried the following and it works as expected:
#NonCPS
def myFunction() {
throw new RuntimeException();
}
try {
myFunction();
} catch (Exception e) {
echo "Caught";
}
and
#NonCPS
def myFunction() {
throw new RuntimeException();
}
def mySecondFunction() {
try {
myFunction();
} catch (Exception e) {
echo "Caught";
}
}
mySecondFunction();
and finally:
#NonCPS
def myFunction() {
throw new RuntimeException();
}
#NonCPS
def mySecondFunction() {
try {
myFunction();
} catch (Exception e) {
echo "Caught";
}
}
mySecondFunction();
All print "Caught" as expected.

Related

Exceptions skipped in scripted pipeline

I have a scripted jenkins pipeline with the code like this.
func_stage_1() {
try {
stage1
} catch {
}
}
func_stage_1()
Initially when I did not have the stage inside function, pipeline used to fail if a stage failed and exceptions were printed. After putting it inside function, it continues the whole pipeline even though one stage fails. How to fix this?
Main functionality behind adding exception logic is to continue the process even though the stage is failed.
If you want to make the remaining process stop, if exception occurred then you have to do it in below way.
func_stage_1() {
try {
stage1
} catch(Exception err {
error "${err}" // stops the execution of remaining stages by throwing the exception
}
}
func_stage_1()

Quit a specflow scenario, or avoid execution based on a precondition

I want to be able to avoid a specflow scenario execution, or quit at the first step, depending on external configuration. Is that possible?
Like
Background:
Given we have a satisfied precondition
the precondition fails and the scenario stops without returning an error
IUnitTestRuntimeProvider interface should be injected
https://docs.specflow.org/projects/specflow/en/latest/Execution/SkippingScenarios.html
however, I then run into the fact that this interface can not be resolved
https://github.com/SpecFlowOSS/SpecFlow/issues/2076
the resolution of this does not work for me in a .Net 5, XUnit project, with Visual Studio 2019
If a step does not throw an exception, then the step passes. You could refactor that particular step into a private method, then simply wrap the method call in a try-catch inside an if statement:
[Given(#"we have a satisfied precondition")]
public void GivenWeHaveASatisfiedPrecondition()
{
if (/* check your external config here */)
{
try
{
PerformPrecondition();
}
catch (Exception ex)
{
Console.WriteLine("Step failed, but continuing scenario." + Environment.NewLine + Environment.NewLine + ex);
}
}
else
{
// Failure in this step should fail the scenario.
PerformPrecondition();
}
}
private void PerformPrecondition()
{
// Your step logic goes here
}
You haven't specified where your external configuration is at, but there are a thousand ways to do this.

Jenkinsfile: Why `java.lang.NoSuchMethodError: No such DSL method` is not caught?

I have the following code in the Jenkinsfile which calls an "optional" method, and if it does not exists it just shall print a message
...
try {
verifyDeployment(pipelineParams)
} catch (err) {
echo "[INFO] No `verifyDeployment` defined, thus ignoring this step"
}
...
However when I run the pipeline the catch seems not to work at I get the following error:
java.lang.NoSuchMethodError: No such DSL method 'verifyDeployment' found among steps
What do I miss?
You are using a wrong groovy syntax.
See the correct groovy syntax for catching exceptions: Syntax catching exceptions
try {
//Protected code
} catch(ExceptionName e1) {
//Catch block
}
It turns out you need to explicitly catch NoSuchMethodError as follows
...
try {
verifyDeployment(pipelineParams)
} catch (NoSuchMethodError ex) {
echo "[INFO] No `verifyDeployment` defined, thus ignoring this step"
}
...
You can also be more generic with the catching type and
catch (Throwable ex)
See the type hierarchy:
https://docs.oracle.com/javase/7/docs/api/java/lang/NoSuchMethodError.html
But be sure to read up on "catch throwable practice" and decide if it's right for you. I sometimes catch throwable only for logging followed by a rethrow.

Jenkins plugin - how to get currently executing job?

I am building a jenkins pipeline plugin (methods to be invoked from a pipeline) and need to get retrieve information about the currently running job, which invoked my methods.
There are a couple of questions I found talking about it, for example here - Jenkins Plugin How to get Job information.
Yet I can't figure out how to use this information. I do have access to the Jenkins instance, but don't have any info about the current project, job, build, etc. How can I get hold of that info?
Note, this is a pipeline steps plugin, there is no perform method in it.
Ok, after search, I finally found the answer in the most obvious of all places - documentation for writing pipeline steps plugins and the corresponding API documentation.
The way to do it is from the Execution class. Inside it, just call getContext(), which returns StepContext, which then has .get method to get the rest of the things you need:
public class MyExecution extends SynchronousNonBlockingStepExecution<ReturnType> {
...
#Override
protected ReturnType run() throws Exception {
try {
StepContext context = getContex();
// get currently used workspace path
FilePath path = context.get(FilePath.class);
//get current run
Run run = context.get(Run.class);
// ... and so on ...
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
...
}

How to print each element of Multi-line String Parameter?

I've Pipeline job in Jenkins (v2.7.1) where I'd like to print each element of Multi-line String parameter (Params) with 3 strings in each line: Foo, Bar, Baz as an input.
So I've tried the following syntax (using split and each):
Params.split("\\r?\\n").each { param ->
println "Param: ${param}"
}
but it fails with:
java.lang.UnsupportedOperationException: Calling public static java.lang.Object
org.codehaus.groovy.runtime.DefaultGroovyMethods.each(java.lang.Object,groovy.lang.Closure) on a CPS-transformed closure is not yet supported (JENKINS-26481); encapsulate in a #NonCPS method, or use Java-style loops
at org.jenkinsci.plugins.workflow.cps.GroovyClassLoaderWhitelist.checkJenkins26481(GroovyClassLoaderWhitelist.java:90)
which suggest to encapsulate in a #NonCPS method, or use Java-style loops.
So I've tried to encapsulate in a #NonCPS method like:
#NonCPS
def printParams() {
Params.split("\\r?\\n").each { param ->
println "Param: ${param}"
}
}
printParams()
but it fails with:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods println groovy.lang.Closure java.lang.Object
Without the function (as per first example), adding #NonCPS at the beginning it complains about unexpected token.
I also tried Java-style syntax as suggested by using for operator (similar as here):
String[] params = Params.split("\\r?\\n")
for (String param: params) {
println "Param: ${param}"
}
which seems to work in plain Groovy, but it fails in Jenkins with:
java.io.NotSerializableException: java.util.AbstractList$Itr
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:860)
Which syntax I should use to make it work?
The code works fine when disabling a Use Groovy Sandbox option and adding #NonCPS helper method. Alternatively, as suggested by #agg3l, proceed to Jenkins management to permit this method access.
So the working code is (same as the 2nd example):
#NonCPS
def printParams() {
Params.split("\\r?\\n").each { param ->
println "Param: ${param}"
}
}
printParams()
I know it's an old post but this is my way to do it, hopefully help anyone else
params.readLines().each {
println it
if (it) {
// if you want to avoid make operation with empty lines
}
}

Resources