Jinja2 templated file with json safe multiline include - ansible

I have a problem with a Jinja2 template I'm writing (called from Ansible).
The resultant file is a JSON file that I will send to an API (either with Ansible URI module or with curl). The template looks like this and basically works:
{
"description" : "my description",
"pipeline": "{% include 'root/pipeline.j2' %}"
}
The problem is that the content of root/pipeline.j2 is quite complex and includes multiple lines, quote characters and any number of other things that make the json file I'm creating invalid. What I want to do is parse the included file through a filter to convert it to a JSON valid string; something like this:
{
"description" : "my description",
"pipeline": "{% include 'root/pipeline.j2' | to_json %}"
}
But that doesn't work, probably because the filter is acting on the filename, not the included content.
Just for a little clarity when I create the template at the moment I see pipeline gets set to something like this:
"pipeline": "input {
"input1" {
<snipped>
"
It should appear thus:
"pipeline": "input {\n \"input1\" {<snipped>"
NB: I'm only giving the first couple of lines and I am using 'snipped' where I have remove the rest of the config.
Can anyone tell me how I can use an include within a jinja2 template that renders the result as a single line valid json string?
Thanks in advance for any assistance.

I finally managed to find a solution to my own question. Within the template that provides the JSON that is an API payload I am now setting a variable to the content of the pipeline template, which makes it easy to filter it with to_json:
{% set pipeline = lookup('template', 'root/pipeline.j2') %}
{
"description" : "my description",
"pipeline": {{ pipeline | to_json }}
}
I will leave this quesiton answer open for a while in case anyone can supply a better answer or explain why this is not a good one.
Thanks.

Related

How to render a literal 'null' in Jinja2 Template

I'm working on an ansible role that allow users to interact with a REST API. I create json.j2 templates that allow me to build the message payload and eventually submit. One of the fields expects either a string value ("") or null.
{
"value": "{{ example.value | default(null, true) }}"
}
This doesn't work and I get this error:
The task includes an option with an undefined variable. The error was: 'null' is undefined
I need that null value and I need to come in as the default value if no other value is provided.
How do I do this?
As pointed in the comments, null has no meaning in Python, None is the Python representation of what your are looking for.
Now, if you want to convert this to a JSON value, then there is a to_json filter in Ansible, so:
'{ "value": {{ example.value | default(None, true) | to_json }} }'
Would end up as:
{ "value": null }

Passing sub vars to Ansible command line

I have an ansible playbook that gets its vars passed in from an extra-vars.json file. It gets passed in at the command line with --extra-vars "#extra-vars.json".
This is an abbreviated version of the var file
{
"source" : {
"access_token" : "abc",
"git_instance_url" : "foo.com",
"repo" : "some-group/some-project/some-repo"
},
"target" : {
"access_token" : "xyz",
"git_instance_url" : "foo.bar.com",
"repo_path" : "lorem/ipsum"
}
}
Because of the var structure, when I call the vars in my playbook I have to use dot notation i.e. {{ source.repo_path }} or {{ target.access_token }}. My problem is that I would like to remove a couple of these vars from the extra-vars.json and pass them individually at the command line. If I remove source.git_instance_url from extra-vars.json I can pass it in without any precedence conflicts.
My issue is that I can't figure out how to pass dot notation vars in at the command line. I don't want to change my playbook to do this. If I pass in --extra-vars "source.git_instance_url=bar.baz.com" I get an error source is undefined.
I tried using bracket notation source[git_instance_url]=bar.baz.com with no success.
Is there a way to pass dot notation vars at the command line or am I going to have to change my playbook from {{ source.git_instance_url }} ==> {{ source_git_instance_url }} to be able to accomplish this?
When passing extra vars, these are always "string variables". I learned it the hard way when trying to pass in boolean variables.
You could pass them as json:
ansible-playbook -e '{"source": { "git_instance_url": "foo" }}' playbook.yml
But I don't know right now, if they get merged with the source var from your vars file. I'd guess, one overwrites the other. So you probably end up with either the source var from your string or with the one from the file.

Inserting template name as class

When creating a Go template, you can give it a name, like in this example, "my_home_template":
var tmplHome = template.Must(template.New("my_home_template").Funcs(funcMap).ParseFiles("templates/base.tmpl", "templates/content_home.tmpl"))
How can I get that template name and use it inside the actual template file?
Ultimately I just want to define a convenient css class, like so:
<body class="my_home_template">
Here's a working solution, taking mkopriva's advice:
When executing a template, pass some custom parameter with dummy data. Here, I just create a "PageHome" parameter to pass to the template, and value is a simple "1", but it could be any value:
tmplHome.ExecuteTemplate(w, "base", map[string]interface{}{"PageHome": "1", "Data": events, "UserFirstName": &u.FirstName, "UserProfilePic": &u.ProfilePic})
Then, inside the template itself, a simple if statement to check if the parameter exists, and do something accordingly:
{{ if .PageHome }}
<body class="PageHome">
{{ else }}
<body>
{{ end }}
All my other template executions don't pass a "PageHome" parameter at all, so the if statement never passes as true for them.
There's probably a more advanced solution using a functions via a template function map, and having a consistent "PageType":"something" parameter in all template executions, but in the end you still have to define a parameter per template execution and still have to build up if statements in your templates anyways.

How do I interpolate $key$s in a file without applying a template?

In Jekyll it is possible to have a file that looks like
---
title: Whatever
layout: null
---
<h1>{{ title }}</h1>
The {{ title }} will be interpolated, but the layout: null means that the file contents won’t be wrapped in any kind of template.
What is the equivalent in Hakyll? In other words, if I have a self-contained file like
<h1>$title$</h1>
what kind of block do I need to pass to compile in order to have the $title$ value interpolated, without wrapping the page’s contents in some template?
The answer (thanks to erasmas in the #hakyll channel!) is to compile your page using
getResourceBody >>= applyAsTemplate theContext
where theContext is the instance of Context String that contains the fields you want.
Here’s a toy example of how you’d use this compiler:
main :: IO ()
main = hakyll $ do
match "index.html" $ do
route idRoute
compile $ getResourceBody >>= applyAsTemplate indexContext
indexContext :: Context String
indexContext = mconcat
[ constField "title" "My Website"
, defaultContext
]

Different subject and body for different mails in single Mandrill API call

I am using Mandrill API(ruby) for sending mail. In 'rcpt' option I can give multiple email address.
Is there a way to change Subject according to email address?
Ex..
"to"=>
[{"type"=>"to",
"email"=>"user1#email.com",
"name"=>"User1 Name"},
{"type"=>"to",
"email"=>"user2#email.com",
"name"=>"User2 Name"}],
"metadata" => {
},
I want Subject like this
"Hi, #{username} you have a new mail"
Similarly is it possible to make dynamic email body according to email-address?
This seemed to have worked for me
I am a front-end dev and I was tasked to set a custom subject in mandrill.
So what I found was, in order to put dynamic content in the body of the email, I had to use double curly braces {{ client_name }} in the code.
This was already linked to variables that have been created by backend...
The task was to make the subject
the same as the client name with an added message like this...
"Hi John, hope you are well"
So I found that when logging in to Mandrill and going to your template,
You have the option to set your subject there...
Assuming your backend variables have been set up, you can actually use double curly braces in Mandrill where you set the subject
So my final result in Mandrill itself, looked like this
"Hi {{ client_name }}, hope you are well."
This seemed to have worked for me, however I did not set up all the variables in the backend, but this might help some of you at least.
It is not possible (or I didn't find the way) to send different subjects in one call
Only one subject can be set according to the documentation and my tests
https://mandrillapp.com/api/docs/messages.ruby.html#method=send-template
For dynamic content you can set variables in your template for example |TITLE|
and replace it in your code using merge_vars
merge_vars: [
{
rcpt: #user1.email,
vars: [
{name: "TITLE", content:"#{#user2.full_name} sent you a new message"}
]
},
{
rcpt: #user2.email,
vars: [
{name: "TITLE", content:"#{#user1.full_name} well received your message"}
]
}
]
The only solution for me is to make to calls using the same template but different parameters

Resources