Issue
Note:
I posted this question back when I only had a master node in my Jenkins environment. This made serialization a lesser issue as there were no other nodes to communicate with, thus the code here will not run as expected on a multi-node environment (for instance, readFile
will only read files from the master node).
In my experience it's better to have your configuration written in other formats (JSON, YAML, Groovy which are all supported natively in Jenkins), or use external tools (such as xmllint
on Linux) if you don't have control over the file's format.
Original question:
I have an XML file which I'd like to use as input for a pipeline script. Problem is the XMLParser isn't serializable so I put it in a NonCPS function, but I lost the Node object because of that.
This is the pipeline script:
def buildPlanPath = 'C:\\buildPlan_test.xml'
@NonCPS
groovy.util.Node getBuildPlan(path) {
new XmlParser().parseText(readFile(path))
}
node {
//def buildPlan = new XmlParser().parseText(readFile(buildPlanPath))
groovy.util.Node buildPlan = getBuildPlan(buildPlanPath)
println buildPlan.getClass()
println buildPlan
println buildPlan.branch
}
This is an input sample:
<branch name='mybranch'>
<stage>
<job name='job11' />
<job name='job12' />
</stage>
<stage>
<job name='job21' />
<job name='job22' />
<job name='job23' />
</stage>
<stage>
<job name='job31' />
</stage>
</branch>
This is the result:
Started by user admin
[Pipeline] node
Running on master in C:\Jenkins\workspace\pipeline-develop
[Pipeline] {
[Pipeline] readFile
[Pipeline] echo
class java.lang.String
[Pipeline] echo
<branch name='mybranch'>
<stage>
<job name='job11' />
<job name='job12' />
</stage>
<stage>
<job name='job21' />
<job name='job22' />
<job name='job23' />
</stage>
<stage>
<job name='job31' />
</stage>
</branch>
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
groovy.lang.MissingPropertyException: No such property: branch for class: java.lang.String
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:53)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.getProperty(ScriptBytecodeAdapter.java:458)
at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.getProperty(DefaultInvoker.java:25)
at com.cloudbees.groovy.cps.impl.PropertyAccessBlock.rawGet(PropertyAccessBlock.java:17)
at WorkflowScript.run(WorkflowScript:16)
at ___cps.transform___(Native Method)
at com.cloudbees.groovy.cps.impl.PropertyishBlock$ContinuationImpl.get(PropertyishBlock.java:62)
at com.cloudbees.groovy.cps.LValueBlock$GetAdapter.receive(LValueBlock.java:30)
at com.cloudbees.groovy.cps.impl.PropertyishBlock$ContinuationImpl.fixName(PropertyishBlock.java:54)
at sun.reflect.GeneratedMethodAccessor327.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
at com.cloudbees.groovy.cps.impl.ConstantBlock.eval(ConstantBlock.java:21)
at com.cloudbees.groovy.cps.Next.step(Next.java:58)
at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:154)
at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:164)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:276)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$000(CpsThreadGroup.java:78)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:185)
at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:183)
at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:47)
at java.util.concurrent.FutureTask.run(Unknown Source)
at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:112)
at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Finished: FAILURE
I'm using Jenkins 2.7 with pipeline 2.1, which are currently the latest.
Solution
In the end I think my approach was wrong: I decided to convert the XML file into a separate groovy script and load it within the pipeline
Update: Recently people started editing my answer for clarity, but the fact is that I just ditched storing my configuration in XML files and opted for groovy scripts, which gave me more flexibility. I understand it may not be a common practice, but it suits my needs.
For example - instead of:
config.xml:
<settings>
<floopi>2</floopi>
</settings>
I used:
config.groovy:
[
floopi: 2
]
And in the pipeline script:
stage('init') {
def settings = load('config.groovy')
echo "floopi: ${settings.floopi}"
}
I hope that's a better answer :)
Answered By - towel
Answer Checked By - Clifford M. (JavaFixing Volunteer)