escaping text for inclusion in an xml template

867 views
Skip to first unread message

sebastien....@gmail.com

unread,
Nov 7, 2016, 9:15:59 AM11/7/16
to Ansible Project
Hello,

I'm trying to use ansible to set up a small team buildfarm with jenkins. This is my first use of ansible.

I'm successfully using ansible 2.2's jenkins_job module as in:

  tasks:
    - jenkins_job:
        name: feeds
        config: "{{ lookup('file', '../jenkins/jobs/feeds/config.xml') }}"

The job is defined by the xml, which the module caries over to jenkins using jenkins' rest API. This is great.

Now, most of my jobs are written as jenkins "pipeline" jobs. The job xml mostly holds and wraps a groovy script, like this (dumb) one:


node ('linux'){
   sh 'test -d .qi || (rm -rf .qi; mkdir -p .qi && touch .qi/stamp)'
   checkout scm sh 'make'
}

Instead of editing the job/config.xml, I would prefer to edit the job/script.groovy, and embed it in the xml.

I tried using a role to do so:

 - { role: jenkinspipelinejob, name: 'feeds', script: "{{ lookup('file', 'jenkins/jobs/feeds/script.groovy') }}" }

With

$ cat roles/jenkinspipelinejob/tasks/main.yaml
- debug: msg="CONFIG {{ lookup('template', 'config.xml.j2') }}"
- jenkins_job:
    name: "{{ name }}"
    config: "{{ lookup('template', 'config.xml.j2') }}"

and

$ cat roles/jenkinspipelinejob/templates/config.xml.j2
<?xml version='1.0' encoding='UTF-8'?>
<flow-definition plugin="workflow-job@2.8">
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
      <triggers/>
    </org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
  </properties>
  <definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workfl...@2.21">
    <script>
{{ script }}
    </script>
    <sandbox>true</sandbox>
  </definition>
  <triggers/>
</flow-definition>

It does work, but requires me to escape my script.groovy for xml inclusion. For instance, instead of

sh 'test -d .qi || (rm -rf .qi; mkdir -p .qi && touch .qi/stamp)'

I need to write

sh 'test -d .qi || (rm -rf .qi; mkdir -p .qi &amp;&amp; touch .qi/stamp)'


I would like this escaping to be done automatically, how should I proceed?

I guess I could write a module, but there might be a simpler way.

I've looked for "xml escaping jinja filter" without any luck.

sebastien....@gmail.com

unread,
Nov 30, 2016, 9:41:16 AM11/30/16
to Ansible Project


On Monday, November 7, 2016 at 3:15:59 PM UTC+1, sebastien....@gmail.com wrote:
I would like this escaping to be done automatically, how should I proceed?

I've looked for "xml escaping jinja filter" without any luck.

For the record, the solution was to write my own xml-escaping filter plugin.

Which is easy enough:

$ cat filter_plugins/myfilters.py
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import xml.sax.saxutils
class FilterModule(object):
    ''' my jinja2 filters for ansible'''
    def filters(self):
        return {'escape_xml_attr' : xml.sax.saxutils.quoteattr,
                'escape_xml_data' : xml.sax.saxutils.escape}

Then, use it like this

 - { role: jenkinspipelinejob, name: 'feeds', script: "{{ lookup('file', 'jenkins/jobs/feeds/script.groovy') | escape_xml_data }}" }

asa...@googlemail.com

unread,
Apr 12, 2017, 4:19:29 AM4/12/17
to Ansible Project
As far as I can see the built-in Jinja filter escape does this, so for example

- name: escape
  command: echo "{{ 'a&b' | escape }}"

produces

TASK [test : escape] **************************************************
changed: [localhost] => {"changed": true, "cmd": ["echo", "a&amp;b"], "delta": "0:00:00.018286", "end": "2017-04-12 11:16:17.629886", "rc": 0, "start": "2017-04-12 11:16:17.611600", "stderr": "", "stdout": "a&amp;b", "stdout_lines": ["a&amp;b"], "warnings": []}

Alexey


среда, 30 ноября 2016 г., 21:41:16 UTC+7 пользователь sebastien....@gmail.com написал:

sebastien....@gmail.com

unread,
May 29, 2017, 11:47:38 AM5/29/17
to Ansible Project


On Wednesday, April 12, 2017 at 10:19:29 AM UTC+2, asa...@googlemail.com wrote:
As far as I can see the built-in Jinja filter escape does this, so for example

- name: escape
  command: echo "{{ 'a&b' | escape }}"

Indeed yes, thank you Alexey!

среда, 30 ноября 2016 г., 21:41:16 UTC+7 пользователь sebastien....@gmail.com написал:
On Monday, November 7, 2016 at 3:15:59 PM UTC+1, sebastien....@gmail.com wrote:
Then, use it like this

 - { role: jenkinspipelinejob, name: 'feeds', script: "{{ lookup('file', 'jenkins/jobs/feeds/script.groovy') | escape_xml_data }}" }

So this should become

- { role: jenkinspipelinejob, name: 'feeds', script: "{{ lookup('file', 'jenkins/jobs/feeds/script.groovy') | escape }}" }

but an even better way is to keep the "script" variable content unescaped, like:

- { role: jenkinspipelinejob, name: 'feeds', script: "{{ lookup('file', 'jenkins/jobs/feeds/script.groovy') }}" }

and escape it from the xml template:

$ cat roles/jenkinspipelinejob/
templates/config.xml.j2
<?xml version='1.0' encoding='UTF-8'?>
<flow-definition plugin="workflow-job@2.8">
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties>
    <org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
      <triggers/>
    </org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
  </properties>
  <definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workfl...@2.21">
    <script>
{{ script | escape }}
Reply all
Reply to author
Forward
0 new messages