I am building a yaml string within Jenkins scripted pipeline that is parametrized. The problem is that I get error interpolating string with a variable that is a list of maps. I don't think I can use external libraries in the jenkins pipeline. There are some Jenkins plugins that are installed. Not sure which libs are available to serialize these objects properly into json or yaml. FWIW I am very inexperienced in groovy. I usually write Python.
def yaml, volumeMounts
volumeMounts = [
['mountPath': '/build/toolchain', 'name': 'volume-0'],
['mountPath': '/build/apps', 'name': 'volume-1'],
['mountPath': '/horizonci/automation', 'name': 'volume-2'],
['mountPath': '/home/jenkins', 'name': 'workspace-volume']
]
volumes = [
['name': 'volume-0', 'nfs': ['path': '/toolchain', 'readOnly': true, 'server': 'tools.company.com']],
['name': 'volume-1', 'nfs': ['path': '/apps', 'readOnly': true, 'server': 'tools.company.com']],
['name': 'volume-2', 'nfs': ['path': '/ifs/standard/devops', 'readOnly': true, 'server': 'my.server.company.com']]
]
yaml = """
kind: Pod
spec:
containers:
- volumeMounts: ${volumeMounts}
volumes: ${volumesYml.toString()}
"""
println(yaml)
ERROR:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `io.fabric8.kubernetes.api.model.VolumeMount` from Array value (token `JsonToken.START_ARRAY`) at [Source: (String)"{"apiVersion":"v1","kind":"Pod","spec":{"containers":[{"name":"app","image":"jenkins/agent:latest","volumeMounts":[["mountPath:/build/toolchain","name:volume-0"], ["mountPath:/build/apps","name:vol"[truncated 1028 chars]; line: 1, column: 420] (through reference chain: io.fabric8.kubernetes.api.model.Pod["spec"]->io.fabric8.kubernetes.api.model.PodSpec["containers"]->java.util.ArrayList[0]->io.fabric8.kubernetes.api.model.Container["volumeMounts"]->java.util.ArrayList[0]) at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1601) at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:324) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:187) at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3548) at io.fabric8.kubernetes.client.utils.Serialization.unmarshalJsonStr(Serialization.java:311) at io.fabric8.kubernetes.client.utils.Serialization.unmarshalYaml(Serialization.java:306) at io.fabric8.kubernetes.client.utils.Serialization.unmarshal(Serialization.java:245) Caused: io.fabric8.kubernetes.client.KubernetesClientException: An error has occurred. at io.fabric8.kubernetes.client.KubernetesClientException.launderThrowable(KubernetesClientException.java:64) at io.fabric8.kubernetes.client.KubernetesClientException.launderThrowable(KubernetesClientException.java:53) at io.fabric8.kubernetes.client.utils.Serialization.unmarshal(Serialization.java:249) at io.fabric8.kubernetes.client.dsl.base.OperationSupport.unmarshal(OperationSupport.java:656) at io.fabric8.kubernetes.client.dsl.base.BaseOperation.load(BaseOperation.java:315) at io.fabric8.kubernetes.client.dsl.base.BaseOperation.load(BaseOperation.java:86) at org.csanchez.jenkins.plugins.kubernetes.PodTemplateUtils.parseFromYaml(PodTemplateUtils.java:566) at org.csanchez.jenkins.plugins.kubernetes.PodTemplateUtils.validateYamlContainerNames(PodTemplateUtils.java:595) at org.csanchez.jenkins.plugins.kubernetes.pipeline.PodTemplateStepExecution.start(PodTemplateStepExecution.java:142) at org.jenkinsci.plugins.workflow.cps.DSL.invokeStep(DSL.java:319) at org.jenkinsci.plugins.workflow.cps.DSL.invokeMethod(DSL.java:193) at org.jenkinsci.plugins.workflow.cps.CpsScript.invokeMethod(CpsScript.java:122) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:48) 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) Caused: java.lang.RuntimeException: Failed to parse yaml:
I tried using literal strings but the code looks ugly. Hard to read and maintain.
Solution is to convert Groovy objects to JSON and serialize JSON objects using built-in JSON library that is importable in Jenkins pipelines. Working with yaml strings was tricky and gave up on that. Here is the solution:
import groovy.json.JsonOutput
def yaml, volumeMounts
volumeMounts = [
['mountPath': '/build/toolchain', 'name': 'volume-0'],
['mountPath': '/build/apps', 'name': 'volume-1'],
['mountPath': '/horizonci/automation', 'name': 'volume-2'],
['mountPath': '/home/jenkins', 'name': 'workspace-volume']
]
volumes = [
['name': 'volume-0', 'nfs': ['path': '/toolchain', 'readOnly': true, 'server': 'tools.company.com']],
['name': 'volume-1', 'nfs': ['path': '/apps', 'readOnly': true, 'server': 'tools.company.com']],
['name': 'volume-2', 'nfs': ['path': '/ifs/standard/devops', 'readOnly': true, 'server': 'my.server.company.com']]
]
yaml = """
kind: Pod
spec:
containers:
- volumeMounts: ${JsonOutput.toJson(volumeMounts)}
volumes: ${JsonOutput.toJson(volumes)}
"""
println(yaml)