I have a Jenkinspipeline written in Groovy looking like this:
@SuppressWarnings(["GroovyAssignabilityCheck", "unused"])
class AWSApplicationPipelines {
Object context;
Configuration configuration;
Object environment;
BuildJobDescriptor jobs;
AWSApplicationPipelines(Object context, Configuration configuration, Object environment) {
this.context = context
this.configuration = configuration
this.environment = environment
}
private void readJobs(String directory){
this.context.dir(directory) {
this.jobs = BuildJobDescriptor.fromJSONString(this.context.readFile(environment.JOB_FILE_NAME))
}
}
void awsApplicationCreatePipeline() {
try{
this.readJobs(jobDirectory())
this.context.parallel(this.prepareClusterCreationInParallel(BuildJobType.CREATE, jobs.free))
} catch (Exception e) {
this.context.currentBuild.result = "FAILURE"
throw e
} finally {
Notifier.sendBuildStatusNotifications(context, environment)
}
}
void awsApplicationDestroyPipeline() {
try{
this.readJobs(jobDirectory())
this.context.parallel(this.prepareClusterDestructionInParallel(BuildJobType.DESTROY, jobs.free))
} catch (Exception e) {
this.context.currentBuild.result = "FAILURE"
throw e
} finally {
Notifier.sendBuildStatusNotifications(context, environment)
}
}
private Map<String, Closure> prepareClusterCreationInParallel(BuildJobType jobType, List<BuildJob> jobs){
return jobs.collectEntries { job ->
return [(jobType.capitalizedName() + " " + job.description): {
executeOneJob(job, jobType)
executeMultipleJobs(job, jobType)
}]
}
}
private Map<String, Closure> prepareClusterDestructionInParallel(BuildJobType jobType, List<BuildJob> jobs){
return jobs.collectEntries { job ->
return [(jobType.capitalizedName() + " " + job.description): {
executeMultipleJobs(job, jobType)
executeOneJob(asDestroyJob(job), jobType)
}]
}
}
private void preparePipeline(String directory, Closure<BuildJobDescriptor> pipeline) {
try {
this.context.dir(directory) {
BuildJobDescriptor allJobs = BuildJobDescriptor.fromJSONString(this.context.readFile(environment.JOB_FILE_NAME))
pipeline(allJobs)
}
} catch (Exception e) {
this.context.currentBuild.result = "FAILURE"
throw e
} finally {
Notifier.sendBuildStatusNotifications(context, environment)
}
}
def executeOneJob = { BuildJob job, BuildJobType jobType ->
executeJob(jobType, job.description, job)
}
private void executeJob(BuildJobType jobType, String stageName, BuildJob job) {
this.context.stage(jobType.capitalizedName() + ' ' + stageName + ' Cluster') {
this.context.build(job: job.name, parameters: job.parameters)
}
}
def executeMultipleJobs = { BuildJob job, BuildJobType jobType ->
executeJobsInParallel(job, jobType, ' services of ' + job.description + ' Cluster')
}
private void executeJobsInParallel(BuildJob job, BuildJobType jobType, String stageName) {
this.context.stage(jobType.capitalizedName() + stageName) {
List<BuildJob> dependantClusterServices = jobs.dependent.findAll { job.name == it.cluster }
if (BuildJobType.CREATE == jobType) {
this.context.parallel(this.executeJobs(jobType, dependantClusterServices))
} else {
this.context.parallel(this.executeJobs(jobType, asDestroyJobs(dependantClusterServices)))
}
}
}
private Map<String, Object> executeJobs(BuildJobType jobType, List<BuildJob> jobs) {
return jobs.collectEntries { job ->
return [(jobType.capitalizedName() + " " + job.description): {
this.context.build(job: job.name, parameters: job.parameters)
}]
}
}
private List<BuildJob> asDestroyJobs(List<BuildJob> jobs) {
return jobs.collect { it.toDestroyJob() }
}
private BuildJob asDestroyJob(BuildJob job) {
return job.toDestroyJob()
}
private String jobDirectory() {
return "AWS_" + this.configuration.profile
}
}
It executes other Jenkinsjobs in parallel with dependencies, reading them from a Json File:
{"free": [
{
"name": "dev-rocketchat-cluster",
"description": "RocketChat",
"parameters": []
}],
"dependent": [
{
"name": "dev-rocketchat-notifier",
"description": "RocketChat Notifier",
"parameters": [],
"cluster": "dev-rocketchat-cluster"
}
]}
It worked perfectly to some point. After adding some more jobs to the json file it stopped working with the following exception:
an exception which occurred:
in field com.cloudbees.groovy.cps.impl.FunctionCallEnv.locals
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@24a6e424
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@281b3516
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@2276d8cb
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@565ad816
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@1e527e65
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@59e54766
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.LoopBlockScopeEnv@7e3eb488
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@20b3a2d1
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.ClosureCallEnv@4de6f191
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@785dfa91
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@4faa3519
in field com.cloudbees.groovy.cps.Continuable.e
in object org.jenkinsci.plugins.workflow.cps.SandboxContinuable@15d40753
in field org.jenkinsci.plugins.workflow.cps.CpsThread.program
in object org.jenkinsci.plugins.workflow.cps.CpsThread@1346f429
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.threads
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@6e71d071
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@6e71d071
Also: an exception which occurred:
in field com.cloudbees.groovy.cps.impl.FunctionCallEnv.locals
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@57f53087
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@6b5718e7
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@64769870
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@10e07939
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.LoopBlockScopeEnv@ab4624b
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@3b8bd3fc
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.ClosureCallEnv@da4fd38
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@209c2b02
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@6808606f
in field com.cloudbees.groovy.cps.Continuable.e
in object org.jenkinsci.plugins.workflow.cps.SandboxContinuable@12596682
in field org.jenkinsci.plugins.workflow.cps.CpsThread.program
in object org.jenkinsci.plugins.workflow.cps.CpsThread@483849df
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.threads
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@6e71d071
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@6e71d071
Also: an exception which occurred:
in field com.cloudbees.groovy.cps.impl.FunctionCallEnv.locals
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@6f2ff0a5
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@9ee4c72
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@49b65473
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@fe49e29
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@146757db
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@414099f6
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.LoopBlockScopeEnv@3bb32ed
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@1c66d08e
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.ClosureCallEnv@68c3185d
in field com.cloudbees.groovy.cps.impl.ProxyEnv.parent
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@48222852
in field com.cloudbees.groovy.cps.impl.CallEnv.caller
in object com.cloudbees.groovy.cps.impl.FunctionCallEnv@175f68a9
in field com.cloudbees.groovy.cps.Continuable.e
in object org.jenkinsci.plugins.workflow.cps.SandboxContinuable@12596682
in field org.jenkinsci.plugins.workflow.cps.CpsThread.program
in object org.jenkinsci.plugins.workflow.cps.CpsThread@483849df
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.threads
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@6e71d071
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup@6e71d071
Caused: java.io.NotSerializableException: java.lang.reflect.Field
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:926)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:568)
at org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.java:65)
at org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.java:56)
at org.jboss.marshalling.MarshallerObjectOutputStream.writeObjectOverride(MarshallerObjectOutputStream.java:50)
at org.jboss.marshalling.river.RiverObjectOutputStream.writeObjectOverride(RiverObjectOutputStream.java:179)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:344)
at java.util.HashMap.internalWriteEntries(HashMap.java:1793)
at java.util.HashMap.writeObject(HashMap.java:1363)
at sun.reflect.GeneratedMethodAccessor43.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.jboss.marshalling.reflect.JDKSpecific$SerMethods.callWriteObject(JDKSpecific.java:156)
at org.jboss.marshalling.reflect.SerializableClass.callWriteObject(SerializableClass.java:191)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1028)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1082)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1040)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1082)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1040)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1019)
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:920)
at org.jboss.marshalling.river.RiverMarshaller.doWriteFields(RiverMarshaller.java:1082)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1040)
at org.jboss.marshalling.river.RiverMarshaller.doWriteSerializableObject(RiverMarshaller.java:1019)
After some googleing i found it, there seems to be an issue with non-serializable objects. Though i wonder why it did work at all up to the point of failure. And how do i find out how to fix this?
The solutions seems to be to extract the non-serializable code into a @NonCPS annotated method, yet i have no clue where though.
Since JSONSlurper seems to be a problem, is there an issue with this part?
import groovy.json.JsonSlurper
class BuildJobDescriptor {
static BuildJobDescriptor fromJSONString(String data) {
Map raw = new JsonSlurper().parse(data.getBytes())
return new BuildJobDescriptor(raw)
}
List<BuildJob> free
List<BuildJob> dependent
private BuildJobDescriptor(Map jobDescriptions) {
this.free = jobDescriptions.free.collect { new BuildJob(it as Map) }
this.dependent = jobDescriptions.dependent.collect { new BuildJob(it as Map) }
}
}