Ansible: Use array to create a variable - arrays

To deploy the ldap.conf with Ansible, I have to create one variable from an array, to use in a template ldap.conf.j2:
nss_base_group {{ ldap_base_group }}
The variable array:
---
ldap_groups: [ 'ORACLE', 'MY_SQL', 'POSTGR' ]
This has to result in one parameter, enhanced with al lot of static characters:
ldap_base_group:"dc=foo,dc=com?sub?(&(|(memberof:1.2.654.123456.1.5.2468:=cn=ORACLE,ou=Groups,dc=foo,dc=com)(memberof:1.2.654.123456.1.5.2468:=cn=MY_SQL,ou=Groups,dc=foo,dc=com)(memberof:1.2.654.123456.1.5.2468:=cn=POSTGR,ou=Groups,dc=foo,dc=com))(!(userAccountControl:1.2.654.123456.1.5.654:=2)))"
This is the same parameter made readable (not usable because of newlines, spaces etc):
ldap_base_group: "dc=foo,dc=com?sub?
(&
(|
(memberof:1.2.654.123456.1.5.2468:=cn=ORACLE,ou=Groups,dc=foo,dc=com)
(memberof:1.2.654.123456.1.5.2468:=cn=MY_SQL,ou=Groups,dc=foo,dc=com)
(memberof:1.2.654.123456.1.5.2468:=cn=POSTGR,ou=Groups,dc=foo,dc=com)
)
(!
(userAccountControl:1.2.654.123456.1.5.654:=2)
)
)"
Does anyone has found a nice solution to do this in Ansible? Maybe there is an other way to do this, I am eager to know alternatives too.

If you template this you should be able to do something along these lines:
ldap_base_group: "dc=foo,dc=com?sub?
(&
(|
{% for group in ldap_groups %}
(memberof:1.2.654.123456.1.5.2468:=cn={{ group }},ou=Groups,dc=foo,dc=com)
{% endfor %}
)
(!
(userAccountControl:1.2.654.123456.1.5.654:=2)
)
)"
If you need everything in a single line then just flattening this out should work fine.

Related

How can I find out if my variable is an object or an array with twig?

I try to find out if my variable is an object or an array:
{% if variable is iterable %}It is an array{% else %}it is an object{% endif %}
But in cases I get the result:
It is an array
You are correct, the twig iterable test has shortcomings, since objects can be iterable as well. PHP has handy functions like is_array and is_object, however, it is not possible to access regular PHP function in Twig directly. So, we need to write a Twig extension/Test, i.e. add a new Twig_SimpleTest to check if an item is_array. You can add this test to your app configuration after the general twig bootstrap.
$isArray= new Twig_SimpleTest('array', function ($value) {
return is_array($value);
});
$twig->addTest($isArray);
and simply us it like this:
{% if var is array%} It is an array
{% else %} It is an object{% endif %}

How to use template parameters in page content in hugo

Is it possible to use template parameters in the content of a post with Hugo? E.g. if I have the following parameters:
[params.company]
name = "My Company"
Can I then do something like this in the content of a post?
This site, {{ .Site.BaseURL }} is operated by {{ params.company.name }}
I've tried, but it's literally printing the above instead of interpolating the variables.
1. Front matter way
As far as I'm aware, it's not possible* to put variables within the markdown file's content because MD parser would strip them, but it's possible to do it using custom variables on the front matter of each .md content file. The Hugo engine can target any fields you set in front matter. Front matter fields can be unique as well.
In your case, the template which is called to show the rendered .MD file has access to front matter parameters and you can change template's behaviour (like add classes of extra <div>'s) or even pull the content right from the parameter.
For example, on my .md files I have:
---
title: "Post about the front matter"
<more key pairs>
nointro: false
---
The key nointro: true would make my first paragraph to be normal size. Otherwise, if key is absent or false, first paragraph would be shown at larger font size. Technically, it's adding a custom class on a <div>.
In the template, I tap into the custom parameter nointro this way:
parent template which shows your markdown file, which has front matter parameters:
<div class="article-body {{ if .Params.nointro }} no_intro {{ end }}">
{{ .Content }}
</div><!-- .article-body -->
Notice I can't put variables within {{ .Content }}, but I can outside of it.
For posterity, that's piece of the content from a file hugo/themes/highlighter/layouts/partials/blog-single-content.html, it's a partial for single post content. You can arrange your partials any way you like.
Obviously, that's Boolean parameter flag, but equally it could be content which you could use directly:
MD file's top:
---
title: "One of our clients"
<more key pairs>
companyname: "Code and Send Ltd"
---
Text content is put here.
Then, reference it like this (extra insurance against blank value using IF):
Anywhere in Hugo template files:
{{ if .Params.companyname }}{{ .Params.companyname }}{{ end }}
2. Using config.(toml/yaml/json)
Now, looking at your example, "This site is operated by " would almost warrant a custom field in more global location, for example, hugo/config.toml. If I wanted to add a companyname into my config, I'd do it this way:
hugo/config.toml:
BaseURL = "_%%WWWPATH%%_"
languageCode = "en-uk"
title = "Code and Send"
pygmentsUseClasses = true
author = "Roy Reveltas"
theme = "Highlighter"
[params]
companyname = ""
Then I'd use it anywhere via {{ .Site.Params.headercommentblock }}.
I guess if you want your client pages to be static pages then single location might not be the best and you might want to tap into front-matter. Otherwise, if it's a site's footer, this way is better. Alternatively, you could even put this data even on data files.
3. Using custom placeholders and replacing via Gulp/npm scripts
I said not possible*, but it's possible, although unconventional and more risky.
I had such setup when I needed two builds for my website: 1) Prod and 2) Dev. Prod URL's were coming from two places: CDN and my server. Dev had to come from a single place in a static folder because I wanted to see images and was often working offline on a train.
To solve that, I used two custom variables in all my templates (including markdown content): _%%WWWPATH%%_ and _%%CDNPATH%%_. I came up with this unique pattern myself by the way, feel free to adapt it. Then, I put it also on hugo/config.toml as:
hugo/config.toml:
BaseURL = "_%%WWWPATH%%_"
After Hugo happily generated the website with those placeholders, I finished off the HTML's using a Grunt task:
grunt file:
replace: {
dev: {
options: {
patterns: [{
match: /_%%CDNPATH%%_+/g,
replacement: function () {
return 'http://127.0.0.1:1313/'
}
}, {
match: /_%%WWWPATH%%_+/g,
replacement: function () {
return 'http://127.0.0.1:1313/'
}
}...
For posterity, I recommend Gulp and/or npm scripts, I'd avoid Grunt. This is my older code example above from the days when Grunt was the best.
If you go this route, it's riskier than Hugo params because Hugo won't error-out when your placeholder values are missing or anything else wrong happens and placeholders might spill into the production code.
Going this route you should add multiple layers of catch-nets, ranging from simple following Gulp/Grunt/npm scripts step which searches for your placeholder pattern to pre-commit hooks ran via Husky on npm scripts that prevent from committing any code that has certain patterns (for example, %%_). For example, at a very basic level, you would instruct Husky to search for anything before allowing committing this way:
package.json of your repo:
"scripts": {
"no-spilled-placeholders": "echo \"\n\\033[0;93m* Checking for spilled placeholders:\\033[0m\"; grep -q -r \"%%_\" dist/ && echo \"Placeholders found! Stopping.\n\" && exit 1 || echo \"All OK.\n\"",
"precommit": "npm run no-spilled-placeholders"
},
Basically, grep for pattern %%_ and exit with error code if found. Don't forget to escape the code because it's JSON. I use similar (more advanced) setup in production and nothing slips through. In proper setup you should creatively look for anything mis-typed, including: %_, _%, %__, __% and so on.
Normal Go template is not supported in the markdown file, but shortcodes are:
{{< param "company.name" >}}
To access arbitrary other Go template values, create a custom shortcode for it and call that from your markdown file.
For your example, you need the site's baseUrl, so save this as layouts/shortcodes/base_url.html:
{{ .Site.BaseURL }}
And write this in your markdown file:
+++
[company]
name = "My Company"
+++
This site, {{< base_url >}} is operated by {{< param "company.name" >}}
There is also the shortcode param : {{< param "companyName" >}} : https://gohugo.io/content-management/shortcodes/#param

iterating through a Django model field produces unwanted format

Python3.4 Django1.9 Noob here... :)
I am trying to loop through a Django field but am not getting the desired results, here's the picture:
the models field "info" can hold strings and in some cases python lists. My issue in this case is when it's a list and I have to iterate through it...
e.g.:
say the "info" field has something like this: ['2016-02-23', '2016-03-01'] and I try to loop through it on the Django1.9 template like this:
{% for i in dates %} {{i}}<br /> {% endfor %}
I expect to see something like this:
2016-02-23
2016-03-01
instead I get this:
[
'
2
0
1
6
-
0
2
-
2
3
'
,
'
2
0
1
6
-
0
3
-
0
1
'
]
which makes me think the mySQL field holding the data is fooling me into thinking this is a python list... my ability to change the format of the database field are limited... any help would be appreciated!
models.py
class Events(....
info = models.CharField(max_length=150, blank=True)
views.py
dates_list = []
obj_dates = Events.objects.filter(id=id)
for i in obj_dates:
dates_list.append(i.info)
context = {"dates": dates_list)
Django 1.9 template
{% for i in dates %} {{i}} {% endfor %}
just looking at your code
obj_dates = Events.objects.filter(id=id)
is only going to get you one entry (as you are filtering by id) - you are returning a queryset with only one object.
When you use:
for i in obj_dates:
dates_list.append(i.info)
you are appending a string "['2016-02-23', '2016-03-01']"
to dates_list. That is to say that dates_list is just a list containing a single string - it appears to be a list containing a list but it is a list containing a string.
in python strings are iterable, so the template is iterating through one character in the string at a time, thus returning you
[
'
2
...
Solutions
A. The hacky way
dates_list.append(list(i.info))
that will convert that string "['2016-02-23', '2016-03-01']" into a list
B. a more django-ish approach
Ask yourself can you store dates in a better fashion that as text in a charfield?
One way to do it is to use a dateField then use m2m so you can store multiple dates.
class Events(models.Model)
info = models.CharField(max_length=150, blank=True)
dates = models.ManyToManyField(Dates, related_name='events', blank=True)
class Dates(models.Model)
date = models.DateField(auto_now=False, auto_now_add=False)
Thanks to luke_aus for the help!
I also found something that could work in some cases...
If you see yourself in a situation having to unpack or loop through a Python list stored in a CharField, try this... it worked very well in my case...
Views.py
import ast
date_list = []
for i in obj:
l = {
'dates': ast.literal_eval(i.info), # i.info is a CharField containing a python list
}
dates_list.append(l)
Template
{% for d in dates %}
{{d}}<br>
{% endfor %}
2016-02-23
2016-03-01

The equivalent of this loop in Jinja

I would like to know how can I perform this loop in Jinja :
for r,s in (v,t):
#Do something
When I try to write something similar in Jinja(Flask), I had an error :
ValueError: too many values to unpack
v and t are lists of dicts .
Found : In jinja I should use zipped
{% for s, t in zipped %}
#do something
{% endfor %}
where zipped = zip (s,t)

How to get length of (or count) of datastore entities through a reference collection definition in google datastore from client side

I have 1 to many datastore relationship between 2 Entities (google datastore) --- i.e. an instance in the Restaurant_Table can have many reviews from the Review_Table - as labeled by collection "restaurant_reviews"
As I spin through each restaurant (in a loop) to a list each of them in my template through jinja, I want to display the number or count of reviews there are for a single restaurant. All restaurants are passed from server in the form of a query from the datastore in variable restaurants as shown in the jinja2 logic in my template.
I'm getting an error that the query object TypeError: object of type 'Query' has no len(). Any other ideas on how to get the # of reviews for a single restaurant?? help!
{% for each in restaurants %}
<script>
var html_output = "";
var review_count ={{each.restaurant_reviews|length}};
...
{% endfor %}
DATASTORE ENTITY definition is:
class Review_Table(db.Model):
date_submission = db.DateTimeProperty(required=True, indexed=True)
course_id = db.ReferenceProperty(Restaurant_Table,
indexed=True, collection_name='restaurant_reviews')
To get the number of entities represented by the query you can use the count method:
db.Query(Kind).count()
You can't do this within a jinja template -- you would need to build your output in your handler, and then render to your template.
However, as the docs say the .count() method "is faster by a constant factor than actually retrieving all of the results, but the running time still scales linearly with the sum of offset + limit." Meaning, this could get really, really slow if you get a large number of restaurants/ reviews.
A better option, in my opinion, would be to add an extra property to your Restaurants model "num_reviews", which your handler would update every time a new review is added -- making very slightly more expensive/slower writes would become hugely cheaper/faster reads because no extra querying would have to be done.
Performance could be an issue, but this will accomplish the task:
var review_count = {% for n in restaurant_reviews %}{% if loop.index == 1 %}{{ loop.length }}{% endif %}{% endfor %};

Resources