pythonansiblejinja2trailing-whitespace

How to remove trailing whitespace from jinja template with multiple strings separated by spaces?


I am really struggling on this for some reason and cannot figure this out. I've tried multiple variations with no luck. Basically, I am injecting multiple LDAP endpoints into my nslcd.conf via Jinja2 templating and Ansible, however I am left with a trailing whitespace no matter what I do. On some nodes it's causing the nslcd service to fail to load, so I'm trying to yank the whitespace but having horrible luck. Any suggestions? Here is the basic code -

uri = {% for host in ldap_hosts %}{{ ldap_protocol }}://{{ host }} {% endfor %}

base {{ ldap_base_dn }}
binddn {{ ldap_bind_dn }}
bindpw {{ ldap_bind_password }}
rootpwmoddn {{ ldap_bind_dn }}

Output -

uri = ldaps://ldaps.fake.domain.net ldaps://ldaps.alsofake.domain.net
base DC=something,DC=local
binddn CN=ldap_bind_user,OU=ServiceAccounts,OU=Groups,OU=Accounts,DC=something,DC=local
bindpw yoink
rootpwmoddn CN=ldap_bind_user,OU=ServiceAccounts,OU=Groups,OU=Accounts,DC=something,DC=local

What I am trying to get is -

uri = ldaps://ldaps.fake.domain.net ldaps://ldaps.alsofake.domain.net

not

uri = ldaps://ldaps.fake.domain.net ldaps://ldaps.alsofake.domain.net

I have tried every combination of uri = {% for host in ldap_hosts %}{{ ldap_protocol }}://{{ host }} {% endfor %} I can think of.

{%- for host in ldap_hosts %} ... {%+ endfor -%} - completely removes the new line and puts base up on the uri line

{%+ for host in ldap_hosts -%} ... {%+ endfor %} - looks normal but trailing whitespace still exists

{% for host in ldap_hosts %} ... {%- endfor -%} - removes newline and puts base on the uri line

{%+ for host in ldap_hosts -%} ... {%+ endfor -%} - removes newline and puts base on the uri line

{%+ for host in ldap_hosts -%} ... ://{{ host }}{% endfor %} - smashes both endpoints together instead

Basically any option I try either removes the newline and puts base on the uri line, or makes it look normal but I find a whitespace at the end. Yes, I know there is a space in here - ://{{ host }} {% but that is to separate the endpoints on the line so it has fallback options when one endpoint isn't responding. This has been working fine for years up until literally this week when a device finally said "what is this whitespace" and failed the service instead.

Update:

uri = {% for host in ldap_hosts %}{{ ldap_protocol }}://{{ host }}{% if not loop.last %} {% endif %}{% endfor %} did the trick! Thanks JonSG


Solution

  • Minus and plus signs control both whitespaces and newlines. So, just move the space to the beginning of the line and use {%- for ... %} to eliminate the need to deal with the newlines and simply remove the leading space from the beginning of the loop:

    uri =  {%- for host in ldap_hosts %}{{ ldap_protocol }}://{{ host }}{% endfor %}
    
    base {{ ldap_base_dn }}
    binddn {{ ldap_bind_dn }}
    bindpw {{ ldap_bind_password }}
    rootpwmoddn {{ ldap_bind_dn }}
    

    Alternatively, you can omit the loop and prepare the full line using map, join, and some regex to add the protocol prefix to each element of the list (you can also move the preparation out of the template by the way - to reduce the complexity or to reuse the value):

    uri = {{ ldap_hosts | map('regex_replace', '^(\\w)', ldap_protocol + '://\\1' | join(' ') }}
    base {{ ldap_base_dn }}
    binddn {{ ldap_bind_dn }}
    bindpw {{ ldap_bind_password }}
    rootpwmoddn {{ ldap_bind_dn }}
    

    However, I'd prefer the first way as it looks cleaner (and probably wouldn't even cause the syntax errors if you don't trim the leading space).