I've been getting an odd error in Jenkins when trying to run readFile() within a shared library, so that I can determine if a Dockerfile is pulling from ECR or docker hub. The error is :-
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: org.myorg.DockerManager.readFile() is applicable for argument types: (java.lang.String) values: [DockerServiceDescription/Dockerfile]
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:58)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:54)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.methodCall(DefaultInvoker.java:20)
at org.proj.DockerManager.login(/tmp/persistence/jobs/PROJ/jobs/builds/jobs/proj-subproj/branches/PROJ-4190/builds/47/libs/Global/src/org/me/DockerManager.groovy:26)
The method that's triggering the error is :-
/**
Login to docker
*/
void login() {
dockerfileHandle = readFile(dockerfile)
dockerfileLines = dockerfileHandle.readLines()
def dockerfileFROM = dockerfileLines.find{ dockerfileLine-> dockerfileLine =~ /^FROM / }
println("FROM: " + dockerfileFROM)
if (dockerfileFROM =~ /\.ecr\./ ) {
println("Using ECR")
ecrlogin()
} else {
println("Using DOCKER HUB")
dockerhublogin()
}
}
however that same code worked perfectly well within the pipeline code, it just breaks when moved into a shared library.
Any ideas?
Access to pipeline steps is done via an instance of CPSScript that represents the running pipeline. To use it in a class of shared library, you have to pass this into your library, either as constructor or method argument:
/**
Login to docker
*/
void login(def script) { // actually CPSScript, but that way, no import is needed
dockerfileHandle = script.readFile(dockerfile)
//...
}
This from your actual Jenkins file, you then call it like:
myInstance.login(this)
Also note that you println calls would result in output in the Jenkins logs, not the build logs, what you are aiming for is script.echo()
Related
]I am a junior dev trying to lear about Jenkins, I have been learning on my own for a couple of months. Currently I have a pipeline (just for learning purposes) which runs static analysis on a folder, and then publish it, I have been able to send a report through email using jelly templates, from there I realized it is posbile to instantiate the classes of a plugin to use its methods so I went to the cppcheck javadoc and did some trial and error so I can get some values of my report and then do something else with them, so I had something like this in my pipeline:
pipeline {
agent any
stages {
stage('analysis') {
steps {
script{
bat'cppcheck "E:/My_project/Source/" --xml --xml-version=2 . 2> cppcheck.xml'
}
}
}
stage('Test'){
steps {
script {
publishCppcheck pattern:'cppcheck.xml'
for (action in currentBuild.rawBuild.getActions()) {
def name = action.getClass().getName()
if (name == 'org.jenkinsci.plugins.cppcheck.CppcheckBuildAction') {
def cppcheckaction = action
def totalErrors = cppcheckaction.getResult().report.getNumberTotal()
println totalErrors
def warnings = cppcheckaction.getResult().statistics.getNumberWarningSeverity()
println warnings
}
}
}
}
}
}
}
which output is:
[Pipeline] echo
102
[Pipeline] echo
4
My logic (wrongly) tells me that if I can access to the report and statistics classes like that and uses their methods getNumberTotal() and getNumberWarningSeverity() respectively, therefore I should be able to also access the DiffState class in the same way and use the valueOf() method to get an enum of the new errors. But adding this to my pipeline:
def nueva = cppcheckaction.getResult().diffState.valueOf(NEW)
println nueva
Gives me an error:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: No such field found: field org.jenkinsci.plugins.cppcheck.CppcheckBuildAction diffState
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.unclassifiedField(SandboxInterceptor.java:425)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(SandboxInterceptor.java:409)
...
I can see in the javadoc there is a diffState class with a valueOf() method, but I cannot access to it is therre any other way to get the new errors between the last build and the current one?
I see 2 issues that could be causing this:
CppcheckResult doesn't have a member variable diffState so you can't access it obviously
If you check the javadoc of CppcheckResult the class does have:
private CppcheckReport report;
public CppcheckStatistics getReport()
and
private CppcheckStatistics statistics;
public CppcheckStatistics getStatistics()
there is no member (and getter method) for diffState so maybe try to call:
/**
* Get differences between current and previous statistics.
*
* #return the differences
*/
public CppcheckStatistics getDiff(){
my suggestion: cppcheckaction.getResult().getDiff().valueOf(NEW). Furthermore CppcheckWorkspaceFile does have a method getDiffState().
Please have a look at the script approval of your Jenkins (see here).
The syntax error might appear because Jenkins (Groovy Sandbox) blocks the execution of an (for the Jenkins) "unknown" and potential dangerous method.
Jenkins settings - Script Approval - Approve your blocked method
I want to use groovy closures with collections in Jenkinsfile:
// other parts removed for brevity
steps {
script {
def testList = ["item1", "item2", "item3"]
testList.stream().map{it+".jpeg"}.each{println it}
}
}
// other parts removed for brevity
However, it gives error:
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: java.util.stream.ReferencePipeline$Head.map() is applicable for argument types: (org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [org.jenkinsci.plugins.workflow.cps.CpsClosure2#248ba046]
Possible solutions: map(java.util.function.Function), max(java.util.Comparator), min(java.util.Comparator), wait(), dump(), grep()
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:153)
at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:161)
at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:165)
at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.methodCall(SandboxInvoker.java:17)
at WorkflowScript.run(WorkflowScript:49)
at ___cps.transform___(Native Method)
at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:86)
...
I am fighting the same problem--constructs in Groovy which I use all the time, like all the handy collection methods, are breaking.
It turns out that in a Jenkinsfile, the code is passed through a transformer called Groovy CPS which enables Jenkins to stop and restart execution of a stage. In the process, stuff gets changed from its original class, and this causes the kind of error you're seeing here, because you're mixing non-CPS code (the Java lib methods) with CPS code (anything inside the Jenkinsfile).
Here's the link to the documentation of this behavior...but I didn't find it very helpful, so I am reverting to doing stuff like I would in Java (pre-lambda) with lots of for loops :(
While it’s not as handy as locally defined closures, you can, in some cases, define methods in the global scope and then rely on them via the Groovy method pointer operator.
void doSomething(final String key, final String value) {
println "${key} → ${value}"
}
pipeline {
stages {
stage('Process data') {
steps {
script {
final Map myData = [
"foo": "bar",
"plop": "yo",
]
myData.each(this.&doSomething)
}
}
}
}
}
I am trying to have one common shared library which has actual pipeline as code, something like this under vars/demo.groovy
def call(Map pipelineParams) {
pipeline {
agent {
docker { image 'centos:latest' }
}
stages {
stage("Env Variables") {
steps {
sh "printenv"
//echo ${SERVICE_NAME}
}
}
stage("test") {
steps {
sh "printenv"
}
}
}
}
}
And I will be accessing the shared Library from my Jenkinsfile like this.
library identifier: 'mylibraryname#master',
//'master' refers to a valid git-ref
//'mylibraryname' can be any name
retriever: modernSCM([
$class: 'GitSCMSource',
//credentialsId: 'your-credentials-id',
remote: 'GIT URL'
])
demo()
It is working as expected but I want to send an extra environment variable or override existing variables from my Jenkinsfile without making changes to shared library which has my pipeline as code . can you help me with how could this be achieved?
I have tried giving the variables as below:
demo {
service = 'test'
var1 = 'value'
}
And tried them accessing this way:
def call(Map pipelineParams) {
pipeline {
agent {
docker { image 'centos:latest' }
}
stages {
stage("Env Variables") {
steps {
sh "printenv"
echo "pipelineParams.service"
}
}
stage("test") {
steps {
sh ' echo "hello pipelineParams.service '
}
}
}
}
}
But getting the following error:
[Pipeline] End of Pipeline
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: demo.call() is applicable for argument types: (org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [org.jenkinsci.plugins.workflow.cps.CpsClosure2#cad5c94]
Possible solutions: call(java.util.Map), wait(), any(), wait(long), main([Ljava.lang.String;), each(groovy.lang.Closure)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:58)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:64)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:54)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:160)
at org.kohsuke.groovy.sandbox.GroovyInterceptor.onMethodCall(GroovyInterceptor.java:23)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:157)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:142)
at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:158)
The signature of this is a map as defined here
def call(Map pipelineParams)
So you need to use a map. (https://www.baeldung.com/groovy-maps)
What this would look like is:
demo([
service: 'test',
var1: 'value'
])
Your echo statement is also not quite right, it should be:
echo(pipelineParams.service)
There is no need to use a GString or a string at all because it is a groovy variable.
Note: I use () in my examples. Strictly speaking you don't need them. I just prefer to code this way so its clear these are method arguments.
You could also just create env vars. Though I probably don't recommend it and would do it more like the way you are currently planning.
withEnv([SERVICE='test', VAR1='value]) {
demo([:]) // I put an empty map here, if you did this you would change your call method to not have the map though
}
This way above ensures the env vars only live until the end of the closure (the last } brace)
Another way, but this just sets them and they will exist globally and for the rest of the run. Not great.
env.SERVICE = 'test'
env.VAR1 = 'value'
demo([:])
in either case, in your shared lib you would check:
echo env.SERVICE
Again though I think its better the first way your doing it. Accepting a map argument and using those. env vars can be global and cause issues sometimes depending on what your running.
I am writing some code for generating Jenkins jobs and I am using Kotlin for the logic to generate the Jenkins jobs. The Jenkins plugin I am using is the Jenkins Job DSL plugin which is written in Groovy to generate the jobs. I am having trouble setting the definition parameter when calling from the Kotlin code to the Groovy code due to not knowing how to create an appropriate groovy.lang.Closure object.
Here my my Kotlin code:
val pipelineJob = dslFactory.pipelineJob("my-job")
// pipelineJob.definition(JOB_DEFINITION_GOES_HERE) <-- this is the part I can't figure out
Here is the code in Groovy that I am trying to port to work in Kotlin:
dslFactory.pipelineJob("my-job").with {
definition {
cps {
script("deleteDir()")
sandbox()
}
}
}
Here is the definition of the method I am calling:
void definition(#DslContext(WorkflowDefinitionContext) Closure definitionClosure) {
Other Links:
DslFactory
When I run the following code on the Jenkins workflow (Jenkins 1.609.1 ,workflow 1.8) I get error of 'NotSerializableException' (also below).
However, if I move the "build job" outside the "for" scope it works fine (the job is activated). Any ideas why this behavior?
node('master') {
ws('/opt/test) {
def file = "/ot.property"
def line = readFile (file)
def resultList = line.tokenize()
for(item in resultList )
{
build job: 'testjob_1'
}
}
}
Got error:
Running: End of Workflow
java.io.NotSerializableException: java.util.ArrayList$Itr
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:860)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1032)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:988)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:854)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1032)
.....
I thnk it is because it's trying to serialize the unserializable item iterator on resultList as soon as it hits the build job step. See here for guidance on use of nonserializable variables:
https://github.com/jenkinsci/workflow-plugin/blob/master/TUTORIAL.md#serialization-of-local-variables
As a workaround to safely iterate using the workflow plugin, you need to us C-style loops. Try this instead:
for ( int i = 0; i < resultList.size; i++ ) {
etc...
According to CloudBees Platform Help page:
By design the pipeline can only keep records of Serializable objects. If you still need to keep an intermediate variable with a non serializable object, you need to hide it into a method and annotate this method with #NonCPS.
So you should transform your code into a function with #NonCPS helper method.
Related Jenkins bug: JENKINS-27421.