Merge lp:~harlowja/cloud-init/changeable-templates into lp:~cloud-init-dev/cloud-init/trunk
- changeable-templates
- Merge into trunk
Proposed by
Joshua Harlow
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
cloud-init Commiters | Pending | ||
Review via email: mp+208994@code.launchpad.net |
Commit message
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
-
Switch to jinja & adjust tpls
- 965. By Joshua Harlow
-
Add some basic template tests
Revision history for this message
Scott Moser (smoser) wrote : | # |
- 966. By Joshua Harlow
-
Add basic renderer support and more robust import handling
- 967. By Joshua Harlow
-
Log the renderer type when rendering files
- 968. By Joshua Harlow
-
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) |
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:
content. replace( '$' + k, v)
for k, v in params: