Merge lp:~harlowja/cloud-init/changeable-templates into lp:~cloud-init-dev/cloud-init/trunk

Proposed by Joshua Harlow on 2014-03-03
Status: Merged
Merged at revision: 981
Proposed branch: lp:~harlowja/cloud-init/changeable-templates
Merge into: lp:~cloud-init-dev/cloud-init/trunk
Diff against target: 655 lines (+372/-159)
10 files modified
cloudinit/templater.py (+111/-4)
requirements.txt (+1/-0)
templates/chef_client.rb.tmpl (+15/-15)
templates/hosts.debian.tmpl (+11/-10)
templates/hosts.redhat.tmpl (+9/-8)
templates/hosts.suse.tmpl (+8/-6)
templates/resolv.conf.tmpl (+25/-34)
templates/sources.list.debian.tmpl (+29/-25)
templates/sources.list.ubuntu.tmpl (+57/-57)
tests/unittests/test_templating.py (+106/-0)
To merge this branch: bzr merge lp:~harlowja/cloud-init/changeable-templates
Reviewer Review Type Date Requested Status
cloud-init commiters 2014-03-03 Pending
Review via email: mp+208994@code.launchpad.net

Description of the change

Allow the usage of jinja templates

Jinja is a python 2.4->3.x compatible
templating engine, allow its optional
usage (until we can depreciate cheetah)
by allowing for specifying a template
file header that can define which template
engine to use.

For now support cheetah (the default) and
if specified support jinja as well. If neither
are available use a crappy renderer that can
expand basic bash-like variables (and that's
all it can do).

To post a comment you must log in.
964. By Joshua Harlow on 2014-03-05

Switch to jinja & adjust tpls

965. By Joshua Harlow on 2014-03-08

Add some basic template tests

Scott Moser (smoser) wrote :

Josh,
  Thank you.
  This looks great.
  The only thing I'd like to suggest is that we dont stack trace on failed import of jinja or cheetah, but rather fail to render loudly (possibly even with stack trace) if that engine is necessary.

  Ie, your code makes it so we only need cheetah if there are templates without the header and we fall back to that engine. No reason to stack trace then, as the default case we wont need them.

  Also, bonus points for a "no cheetah dependency cheetah rendering engine". Ie, if 'mport cheetah' fails, then we can log that as a warning, and try a builtin crappy renderer that does:
    for k, v in params:
       content.replace('$' + k, v)

966. By Joshua Harlow on 2014-07-16

Add basic renderer support and more robust import handling

967. By Joshua Harlow on 2014-07-16

Log the renderer type when rendering files

968. By Joshua Harlow on 2014-07-18

Add non braces matching and a few more tests

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cloudinit/templater.py'
2--- cloudinit/templater.py 2012-07-09 20:41:45 +0000
3+++ cloudinit/templater.py 2014-07-18 17:52:57 +0000
4@@ -20,13 +20,119 @@
5 # You should have received a copy of the GNU General Public License
6 # along with this program. If not, see <http://www.gnu.org/licenses/>.
7
8-from Cheetah.Template import Template
9-
10+import collections
11+import re
12+
13+try:
14+ from Cheetah.Template import Template as CTemplate
15+ CHEETAH_AVAILABLE = True
16+except (ImportError, AttributeError):
17+ CHEETAH_AVAILABLE = False
18+
19+try:
20+ import jinja2
21+ from jinja2 import Template as JTemplate
22+ JINJA_AVAILABLE = True
23+except (ImportError, AttributeError):
24+ JINJA_AVAILABLE = False
25+
26+from cloudinit import log as logging
27+from cloudinit import type_utils as tu
28 from cloudinit import util
29
30+LOG = logging.getLogger(__name__)
31+TYPE_MATCHER = re.compile(r"##\s*template:(.*)", re.I)
32+BASIC_MATCHER = re.compile(r'\$\{([A-Za-z0-9_.]+)\}|\$([A-Za-z0-9_.]+)')
33+
34+
35+def basic_render(content, params):
36+ """This does simple replacement of bash variable like templates.
37+
38+ It identifies patterns like ${a} or $a and can also identify patterns like
39+ ${a.b} or $a.b which will look for a key 'b' in the dictionary rooted
40+ by key 'a'.
41+ """
42+
43+ def replacer(match):
44+ # Only 1 of the 2 groups will actually have a valid entry.
45+ name = match.group(1)
46+ if name is None:
47+ name = match.group(2)
48+ if name is None:
49+ raise RuntimeError("Match encountered but no valid group present")
50+ path = collections.deque(name.split("."))
51+ selected_params = params
52+ while len(path) > 1:
53+ key = path.popleft()
54+ if not isinstance(selected_params, dict):
55+ raise TypeError("Can not traverse into"
56+ " non-dictionary '%s' of type %s while"
57+ " looking for subkey '%s'"
58+ % (selected_params,
59+ tu.obj_name(selected_params),
60+ key))
61+ selected_params = selected_params[key]
62+ key = path.popleft()
63+ if not isinstance(selected_params, dict):
64+ raise TypeError("Can not extract key '%s' from non-dictionary"
65+ " '%s' of type %s"
66+ % (key, selected_params,
67+ tu.obj_name(selected_params)))
68+ return str(selected_params[key])
69+
70+ return BASIC_MATCHER.sub(replacer, content)
71+
72+
73+def detect_template(text):
74+
75+ def cheetah_render(content, params):
76+ return CTemplate(content, searchList=[params]).respond()
77+
78+ def jinja_render(content, params):
79+ return JTemplate(content,
80+ undefined=jinja2.StrictUndefined,
81+ trim_blocks=True).render(**params)
82+
83+ if text.find("\n") != -1:
84+ ident, rest = text.split("\n", 1)
85+ else:
86+ ident = text
87+ rest = ''
88+ type_match = TYPE_MATCHER.match(ident)
89+ if not type_match:
90+ if not CHEETAH_AVAILABLE:
91+ LOG.warn("Cheetah not available as the default renderer for"
92+ " unknown template, reverting to the basic renderer.")
93+ return ('basic', basic_render, text)
94+ else:
95+ return ('cheetah', cheetah_render, text)
96+ else:
97+ template_type = type_match.group(1).lower().strip()
98+ if template_type not in ('jinja', 'cheetah', 'basic'):
99+ raise ValueError("Unknown template rendering type '%s' requested"
100+ % template_type)
101+ if template_type == 'jinja' and not JINJA_AVAILABLE:
102+ LOG.warn("Jinja not available as the selected renderer for"
103+ " desired template, reverting to the basic renderer.")
104+ return ('basic', basic_render, rest)
105+ elif template_type == 'jinja' and JINJA_AVAILABLE:
106+ return ('jinja', jinja_render, rest)
107+ if template_type == 'cheetah' and not CHEETAH_AVAILABLE:
108+ LOG.warn("Cheetah not available as the selected renderer for"
109+ " desired template, reverting to the basic renderer.")
110+ return ('basic', basic_render, rest)
111+ elif template_type == 'cheetah' and CHEETAH_AVAILABLE:
112+ return ('cheetah', cheetah_render, rest)
113+ # Only thing left over is the basic renderer (it is always available).
114+ return ('basic', basic_render, rest)
115+
116
117 def render_from_file(fn, params):
118- return render_string(util.load_file(fn), params)
119+ if not params:
120+ params = {}
121+ template_type, renderer, content = detect_template(util.load_file(fn))
122+ LOG.debug("Rendering content of '%s' using renderer %s", fn, template_type)
123+ return renderer(content, params)
124
125
126 def render_to_file(fn, outfn, params, mode=0644):
127@@ -37,4 +143,5 @@
128 def render_string(content, params):
129 if not params:
130 params = {}
131- return Template(content, searchList=[params]).respond()
132+ template_type, renderer, content = detect_template(content)
133+ return renderer(content, params)
134
135=== modified file 'requirements.txt'
136--- requirements.txt 2014-02-12 10:14:49 +0000
137+++ requirements.txt 2014-07-18 17:52:57 +0000
138@@ -2,6 +2,7 @@
139
140 # Used for untemplating any files or strings with parameters.
141 cheetah
142+jinja2
143
144 # This is used for any pretty printing of tabular data.
145 PrettyTable
146
147=== modified file 'templates/chef_client.rb.tmpl'
148--- templates/chef_client.rb.tmpl 2012-07-09 20:45:26 +0000
149+++ templates/chef_client.rb.tmpl 2014-07-18 17:52:57 +0000
150@@ -1,25 +1,25 @@
151-#*
152- This file is only utilized if the module 'cc_chef' is enabled in
153- cloud-config. Specifically, in order to enable it
154- you need to add the following to config:
155- chef:
156- validation_key: XYZ
157- validation_cert: XYZ
158- validation_name: XYZ
159- server_url: XYZ
160-*#
161+## template:jinja
162+{#
163+This file is only utilized if the module 'cc_chef' is enabled in
164+cloud-config. Specifically, in order to enable it
165+you need to add the following to config:
166+ chef:
167+ validation_key: XYZ
168+ validation_cert: XYZ
169+ validation_name: XYZ
170+ server_url: XYZ
171+-#}
172 log_level :info
173 log_location "/var/log/chef/client.log"
174 ssl_verify_mode :verify_none
175-validation_client_name "$validation_name"
176+validation_client_name "{{validation_name}}"
177 validation_key "/etc/chef/validation.pem"
178 client_key "/etc/chef/client.pem"
179-chef_server_url "$server_url"
180-environment "$environment"
181-node_name "$node_name"
182+chef_server_url "{{server_url}}"
183+environment "{{environment}}"
184+node_name "{{node_name}}"
185 json_attribs "/etc/chef/firstboot.json"
186 file_cache_path "/var/cache/chef"
187 file_backup_path "/var/backups/chef"
188 pid_file "/var/run/chef/client.pid"
189 Chef::Log::Formatter.show_time = true
190-
191
192=== modified file 'templates/hosts.debian.tmpl'
193--- templates/hosts.debian.tmpl 2013-01-15 21:34:51 +0000
194+++ templates/hosts.debian.tmpl 2014-07-18 17:52:57 +0000
195@@ -1,19 +1,19 @@
196-## This file (/etc/cloud/templates/hosts.tmpl) is only utilized
197-## if enabled in cloud-config. Specifically, in order to enable it
198-## you need to add the following to config:
199-## manage_etc_hosts: True
200-##
201-## Note, double-hash commented lines will not appear in /etc/hosts
202-#
203+## template:jinja
204+{#
205+This file (/etc/cloud/templates/hosts.tmpl) is only utilized
206+if enabled in cloud-config. Specifically, in order to enable it
207+you need to add the following to config:
208+ manage_etc_hosts: True
209+-#}
210 # Your system has configured 'manage_etc_hosts' as True.
211 # As a result, if you wish for changes to this file to persist
212 # then you will need to either
213 # a.) make changes to the master file in /etc/cloud/templates/hosts.tmpl
214 # b.) change or remove the value of 'manage_etc_hosts' in
215 # /etc/cloud/cloud.cfg or cloud-config from user-data
216-#
217-## The value '$hostname' will be replaced with the local-hostname
218-127.0.1.1 $fqdn $hostname
219+#
220+{# The value '{{hostname}}' will be replaced with the local-hostname -#}
221+127.0.1.1 {{fqdn}} {{hostname}}
222 127.0.0.1 localhost
223
224 # The following lines are desirable for IPv6 capable hosts
225@@ -23,3 +23,4 @@
226 ff02::1 ip6-allnodes
227 ff02::2 ip6-allrouters
228 ff02::3 ip6-allhosts
229+
230
231=== modified file 'templates/hosts.redhat.tmpl'
232--- templates/hosts.redhat.tmpl 2012-07-09 20:41:45 +0000
233+++ templates/hosts.redhat.tmpl 2014-07-18 17:52:57 +0000
234@@ -1,9 +1,10 @@
235-#*
236- This file /etc/cloud/templates/hosts.redhat.tmpl is only utilized
237- if enabled in cloud-config. Specifically, in order to enable it
238- you need to add the following to config:
239- manage_etc_hosts: True
240-*#
241+## template:jinja
242+{#
243+This file /etc/cloud/templates/hosts.redhat.tmpl is only utilized
244+if enabled in cloud-config. Specifically, in order to enable it
245+you need to add the following to config:
246+ manage_etc_hosts: True
247+-#}
248 # Your system has configured 'manage_etc_hosts' as True.
249 # As a result, if you wish for changes to this file to persist
250 # then you will need to either
251@@ -12,12 +13,12 @@
252 # /etc/cloud/cloud.cfg or cloud-config from user-data
253 #
254 # The following lines are desirable for IPv4 capable hosts
255-127.0.0.1 ${fqdn} ${hostname}
256+127.0.0.1 {{fqdn}} {{hostname}}
257 127.0.0.1 localhost.localdomain localhost
258 127.0.0.1 localhost4.localdomain4 localhost4
259
260 # The following lines are desirable for IPv6 capable hosts
261-::1 ${fqdn} ${hostname}
262+::1 {{fqdn}} {{hostname}}
263 ::1 localhost.localdomain localhost
264 ::1 localhost6.localdomain6 localhost6
265
266
267=== modified file 'templates/hosts.suse.tmpl'
268--- templates/hosts.suse.tmpl 2013-06-25 06:56:57 +0000
269+++ templates/hosts.suse.tmpl 2014-07-18 17:52:57 +0000
270@@ -1,9 +1,10 @@
271-#*
272- This file /etc/cloud/templates/hosts.suse.tmpl is only utilized
273- if enabled in cloud-config. Specifically, in order to enable it
274- you need to add the following to config:
275- manage_etc_hosts: True
276-*#
277+## template:jinja
278+{#
279+This file /etc/cloud/templates/hosts.suse.tmpl is only utilized
280+if enabled in cloud-config. Specifically, in order to enable it
281+you need to add the following to config:
282+ manage_etc_hosts: True
283+-#}
284 # Your system has configured 'manage_etc_hosts' as True.
285 # As a result, if you wish for changes to this file to persist
286 # then you will need to either
287@@ -22,3 +23,4 @@
288 ff02::1 ipv6-allnodes
289 ff02::2 ipv6-allrouters
290 ff02::3 ipv6-allhosts
291+
292
293=== modified file 'templates/resolv.conf.tmpl'
294--- templates/resolv.conf.tmpl 2013-01-17 05:09:49 +0000
295+++ templates/resolv.conf.tmpl 2014-07-18 17:52:57 +0000
296@@ -1,39 +1,30 @@
297-#
298+## template:jinja
299 # Your system has been configured with 'manage-resolv-conf' set to true.
300 # As a result, cloud-init has written this file with configuration data
301 # that it has been provided. Cloud-init, by default, will write this file
302 # a single time (PER_ONCE).
303 #
304-
305-#if $varExists('nameservers')
306-#for $server in $nameservers
307-nameserver $server
308-#end for
309-#end if
310-#if $varExists('searchdomains')
311-search #slurp
312-#for $search in $searchdomains
313-$search #slurp
314-#end for
315-
316-#end if
317-#if $varExists('domain')
318-domain $domain
319-#end if
320-#if $varExists('sortlist')
321-sortlist #slurp
322-#for $sort in $sortlist
323-$sort #slurp
324-#end for
325-
326-#end if
327-#if $varExists('options') or $varExists('flags')
328-options #slurp
329-#for $flag in $flags
330-$flag #slurp
331-#end for
332-#for $key, $value in $options.items()
333-$key:$value #slurp
334-#end for
335-
336-#end if
337+{% if nameservers is defined %}
338+{% for server in nameservers %}
339+nameserver {{server}}
340+{% endfor %}
341+
342+{% endif -%}
343+{% if searchdomains is defined %}
344+search {% for search in searchdomains %}{{search}} {% endfor %}
345+
346+{% endif %}
347+{% if domain is defined %}
348+domain {{domain}}
349+{% endif %}
350+{% if sortlist is defined %}
351+
352+sortlist {% for sort in sortlist %}{{sort}} {% endfor %}
353+{% endif %}
354+{% if options is defined or flags is defined %}
355+
356+options {% for flag in flags %}{{flag}} {% endfor %}
357+{% for key, value in options.iteritems() -%}
358+ {{key}}:{{value}}
359+{% endfor %}
360+{% endif %}
361
362=== modified file 'templates/sources.list.debian.tmpl'
363--- templates/sources.list.debian.tmpl 2013-02-21 15:29:06 +0000
364+++ templates/sources.list.debian.tmpl 2014-07-18 17:52:57 +0000
365@@ -1,28 +1,32 @@
366-\## Note, this file is written by cloud-init on first boot of an instance
367-\## modifications made here will not survive a re-bundle.
368-\## if you wish to make changes you can:
369-\## a.) add 'apt_preserve_sources_list: true' to /etc/cloud/cloud.cfg
370-\## or do the same in user-data
371-\## b.) add sources in /etc/apt/sources.list.d
372-\## c.) make changes to template file /etc/cloud/templates/sources.list.debian.tmpl
373-\###
374+## template:jinja
375+## Note, this file is written by cloud-init on first boot of an instance
376+## modifications made here will not survive a re-bundle.
377+## if you wish to make changes you can:
378+## a.) add 'apt_preserve_sources_list: true' to /etc/cloud/cloud.cfg
379+## or do the same in user-data
380+## b.) add sources in /etc/apt/sources.list.d
381+## c.) make changes to template file /etc/cloud/templates/sources.list.debian.tmpl
382+###
383
384 # See http://www.debian.org/releases/stable/i386/release-notes/ch-upgrading.html
385 # for how to upgrade to newer versions of the distribution.
386-deb $mirror $codename main contrib non-free
387-deb-src $mirror $codename main contrib non-free
388-
389-\## Major bug fix updates produced after the final release of the
390-\## distribution.
391-deb $security $codename/updates main contrib non-free
392-deb-src $security $codename/updates main contrib non-free
393-deb $mirror $codename-updates main contrib non-free
394-deb-src $mirror $codename-updates main contrib non-free
395-
396-\## Uncomment the following two lines to add software from the 'backports'
397-\## repository.
398-\## N.B. software from this repository may not have been tested as
399-\## extensively as that contained in the main release, although it includes
400-\## newer versions of some applications which may provide useful features.
401-# deb http://backports.debian.org/debian-backports $codename-backports main contrib non-free
402-# deb-src http://backports.debian.org/debian-backports $codename-backports main contrib non-free
403+deb {{mirror}} {{codename}} main contrib non-free
404+deb-src {{mirror}} {{codename}} main contrib non-free
405+
406+## Major bug fix updates produced after the final release of the
407+## distribution.
408+deb {{security}} {{codename}}/updates main contrib non-free
409+deb-src {{security}} {{codename}}/updates main contrib non-free
410+deb {{mirror}} {{codename}}-updates main contrib non-free
411+deb-src {{mirror}} {{codename}}-updates main contrib non-free
412+
413+## Uncomment the following two lines to add software from the 'backports'
414+## repository.
415+##
416+## N.B. software from this repository may not have been tested as
417+## extensively as that contained in the main release, although it includes
418+## newer versions of some applications which may provide useful features.
419+{#
420+deb http://backports.debian.org/debian-backports {{codename}}-backports main contrib non-free
421+deb-src http://backports.debian.org/debian-backports {{codename}}-backports main contrib non-free
422+-#}
423
424=== modified file 'templates/sources.list.ubuntu.tmpl'
425--- templates/sources.list.ubuntu.tmpl 2013-02-21 15:29:06 +0000
426+++ templates/sources.list.ubuntu.tmpl 2014-07-18 17:52:57 +0000
427@@ -1,60 +1,60 @@
428-\## Note, this file is written by cloud-init on first boot of an instance
429-\## modifications made here will not survive a re-bundle.
430-\## if you wish to make changes you can:
431-\## a.) add 'apt_preserve_sources_list: true' to /etc/cloud/cloud.cfg
432-\## or do the same in user-data
433-\## b.) add sources in /etc/apt/sources.list.d
434-\## c.) make changes to template file /etc/cloud/templates/sources.list.tmpl
435-\###
436+## template:jinja
437+## Note, this file is written by cloud-init on first boot of an instance
438+## modifications made here will not survive a re-bundle.
439+## if you wish to make changes you can:
440+## a.) add 'apt_preserve_sources_list: true' to /etc/cloud/cloud.cfg
441+## or do the same in user-data
442+## b.) add sources in /etc/apt/sources.list.d
443+## c.) make changes to template file /etc/cloud/templates/sources.list.tmpl
444
445 # See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
446 # newer versions of the distribution.
447-deb $mirror $codename main
448-deb-src $mirror $codename main
449-
450-\## Major bug fix updates produced after the final release of the
451-\## distribution.
452-deb $mirror $codename-updates main
453-deb-src $mirror $codename-updates main
454-
455-\## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
456-\## team. Also, please note that software in universe WILL NOT receive any
457-\## review or updates from the Ubuntu security team.
458-deb $mirror $codename universe
459-deb-src $mirror $codename universe
460-deb $mirror $codename-updates universe
461-deb-src $mirror $codename-updates universe
462-
463-\## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
464-\## team, and may not be under a free licence. Please satisfy yourself as to
465-\## your rights to use the software. Also, please note that software in
466-\## multiverse WILL NOT receive any review or updates from the Ubuntu
467-\## security team.
468-# deb $mirror $codename multiverse
469-# deb-src $mirror $codename multiverse
470-# deb $mirror $codename-updates multiverse
471-# deb-src $mirror $codename-updates multiverse
472-
473-\## Uncomment the following two lines to add software from the 'backports'
474-\## repository.
475-\## N.B. software from this repository may not have been tested as
476-\## extensively as that contained in the main release, although it includes
477-\## newer versions of some applications which may provide useful features.
478-\## Also, please note that software in backports WILL NOT receive any review
479-\## or updates from the Ubuntu security team.
480-# deb $mirror $codename-backports main restricted universe multiverse
481-# deb-src $mirror $codename-backports main restricted universe multiverse
482-
483-\## Uncomment the following two lines to add software from Canonical's
484-\## 'partner' repository.
485-\## This software is not part of Ubuntu, but is offered by Canonical and the
486-\## respective vendors as a service to Ubuntu users.
487-# deb http://archive.canonical.com/ubuntu $codename partner
488-# deb-src http://archive.canonical.com/ubuntu $codename partner
489-
490-deb $security $codename-security main
491-deb-src $security $codename-security main
492-deb $security $codename-security universe
493-deb-src $security $codename-security universe
494-# deb $security $codename-security multiverse
495-# deb-src $security $codename-security multiverse
496+deb {{mirror}} {{codename}} main
497+deb-src {{mirror}} {{codename}} main
498+
499+## Major bug fix updates produced after the final release of the
500+## distribution.
501+deb {{mirror}} {{codename}}-updates main
502+deb-src {{mirror}} {{codename}}-updates main
503+
504+## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
505+## team. Also, please note that software in universe WILL NOT receive any
506+## review or updates from the Ubuntu security team.
507+deb {{mirror}} {{codename}} universe
508+deb-src {{mirror}} {{codename}} universe
509+deb {{mirror}} {{codename}}-updates universe
510+deb-src {{mirror}} {{codename}}-updates universe
511+
512+## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
513+## team, and may not be under a free licence. Please satisfy yourself as to
514+## your rights to use the software. Also, please note that software in
515+## multiverse WILL NOT receive any review or updates from the Ubuntu
516+## security team.
517+# deb {{mirror}} {{codename}} multiverse
518+# deb-src {{mirror}} {{codename}} multiverse
519+# deb {{mirror}} {{codename}}-updates multiverse
520+# deb-src {{mirror}} {{codename}}-updates multiverse
521+
522+## Uncomment the following two lines to add software from the 'backports'
523+## repository.
524+## N.B. software from this repository may not have been tested as
525+## extensively as that contained in the main release, although it includes
526+## newer versions of some applications which may provide useful features.
527+## Also, please note that software in backports WILL NOT receive any review
528+## or updates from the Ubuntu security team.
529+# deb {{mirror}} {{codename}}-backports main restricted universe multiverse
530+# deb-src {{mirror}} {{codename}}-backports main restricted universe multiverse
531+
532+## Uncomment the following two lines to add software from Canonical's
533+## 'partner' repository.
534+## This software is not part of Ubuntu, but is offered by Canonical and the
535+## respective vendors as a service to Ubuntu users.
536+# deb http://archive.canonical.com/ubuntu {{codename}} partner
537+# deb-src http://archive.canonical.com/ubuntu {{codename}} partner
538+
539+deb {{security}} {{codename}}-security main
540+deb-src {{security}} {{codename}}-security main
541+deb {{security}} {{codename}}-security universe
542+deb-src {{security}} {{codename}}-security universe
543+# deb {{security}} {{codename}}-security multiverse
544+# deb-src {{security}} {{codename}}-security multiverse
545
546=== added file 'tests/unittests/test_templating.py'
547--- tests/unittests/test_templating.py 1970-01-01 00:00:00 +0000
548+++ tests/unittests/test_templating.py 2014-07-18 17:52:57 +0000
549@@ -0,0 +1,106 @@
550+# vi: ts=4 expandtab
551+#
552+# Copyright (C) 2014 Yahoo! Inc.
553+#
554+# Author: Joshua Harlow <harlowja@yahoo-inc.com>
555+#
556+# This program is free software: you can redistribute it and/or modify
557+# it under the terms of the GNU General Public License version 3, as
558+# published by the Free Software Foundation.
559+#
560+# This program is distributed in the hope that it will be useful,
561+# but WITHOUT ANY WARRANTY; without even the implied warranty of
562+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
563+# GNU General Public License for more details.
564+#
565+# You should have received a copy of the GNU General Public License
566+# along with this program. If not, see <http://www.gnu.org/licenses/>.
567+
568+from tests.unittests import helpers as test_helpers
569+
570+from cloudinit import templater
571+
572+
573+class TestTemplates(test_helpers.TestCase):
574+ def test_render_basic(self):
575+ in_data = """
576+${b}
577+
578+c = d
579+"""
580+ in_data = in_data.strip()
581+ expected_data = """
582+2
583+
584+c = d
585+"""
586+ out_data = templater.basic_render(in_data, {'b': 2})
587+ self.assertEqual(expected_data.strip(), out_data)
588+
589+ def test_detection(self):
590+ blob = "## template:cheetah"
591+
592+ (template_type, renderer, contents) = templater.detect_template(blob)
593+ self.assertIn("cheetah", template_type)
594+ self.assertEqual("", contents.strip())
595+
596+ blob = "blahblah $blah"
597+ (template_type, renderer, contents) = templater.detect_template(blob)
598+ self.assertIn("cheetah", template_type)
599+ self.assertEquals(blob, contents)
600+
601+ blob = '##template:something-new'
602+ self.assertRaises(ValueError, templater.detect_template, blob)
603+
604+ def test_render_cheetah(self):
605+ blob = '''## template:cheetah
606+$a,$b'''
607+ c = templater.render_string(blob, {"a": 1, "b": 2})
608+ self.assertEquals("1,2", c)
609+
610+ def test_render_jinja(self):
611+ blob = '''## template:jinja
612+{{a}},{{b}}'''
613+ c = templater.render_string(blob, {"a": 1, "b": 2})
614+ self.assertEquals("1,2", c)
615+
616+ def test_render_default(self):
617+ blob = '''$a,$b'''
618+ c = templater.render_string(blob, {"a": 1, "b": 2})
619+ self.assertEquals("1,2", c)
620+
621+ def test_render_basic_deeper(self):
622+ hn = 'myfoohost.yahoo.com'
623+ expected_data = "h=%s\nc=d\n" % hn
624+ in_data = "h=$hostname.canonical_name\nc=d\n"
625+ params = {
626+ "hostname": {
627+ "canonical_name": hn,
628+ },
629+ }
630+ out_data = templater.render_string(in_data, params)
631+ self.assertEqual(expected_data, out_data)
632+
633+ def test_render_basic_no_parens(self):
634+ hn = "myfoohost"
635+ in_data = "h=$hostname\nc=d\n"
636+ expected_data = "h=%s\nc=d\n" % hn
637+ out_data = templater.basic_render(in_data, {'hostname': hn})
638+ self.assertEqual(expected_data, out_data)
639+
640+ def test_render_basic_parens(self):
641+ hn = "myfoohost"
642+ in_data = "h = ${hostname}\nc=d\n"
643+ expected_data = "h = %s\nc=d\n" % hn
644+ out_data = templater.basic_render(in_data, {'hostname': hn})
645+ self.assertEqual(expected_data, out_data)
646+
647+ def test_render_basic2(self):
648+ mirror = "mymirror"
649+ codename = "zany"
650+ in_data = "deb $mirror $codename-updates main contrib non-free"
651+ ex_data = "deb %s %s-updates main contrib non-free" % (mirror, codename)
652+
653+ out_data = templater.basic_render(in_data,
654+ {'mirror': mirror, 'codename': codename})
655+ self.assertEqual(ex_data, out_data)