salt: List variables do not parse into templates properly

Description of Issue/Question

Setup

Manage the file:

random-config:
  file.managed:
    - name: /etc/random.conf
    - source: salt://module/templates/random.conf.j2
    - template: jinja
    - defaults:
        host_list: ["host-1.domain.com","host-2.domain.com","host-3.domain.com"]

The line in the template module/templates/random.conf.j2:

random.property: {{ host_list }}

Steps to Reproduce Issue

Create a salt and pillar module, set up a file.managed in the module and set one variable in the file. Then set the variable with something like: [“host-1.domain.com”,“host-2.domain.com”,“host-3.domain.com”,“host-4.domain.com”]

If the String [“host-1.domain.com”,“host-2.domain.com”,“host-3.domain.com”] is present in the template where it is supposed to be this is recognized as a List. Using test=True I can see that Salt sees the value in the template as [u"host-1.domain.com",u"host-2.domain.com",u"host-3.domain.com"] even though the value is what it’s supposed to be. Salt then tries to change this value to [“host-1.domain.com”,“host-2.domain.com”,“host-3.domain.com”] but instead changes it to [u"host-1.domain.com",u"host-2.domain.com",u"host-3.domain.com"]

I have tried multiple methods of circumventing this, but nothing has worked. (tried piping to a join(‘’), tried changing the variables to be between quotes, double and single.) I believe this may be a Jinja issue?

Versions Report

Master: Salt Version: Salt: 2018.3.0

Dependency Versions: cffi: 1.6.0 cherrypy: Not Installed dateutil: 1.5 docker-py: Not Installed gitdb: Not Installed gitpython: Not Installed ioflo: Not Installed Jinja2: 2.7.2 libgit2: Not Installed libnacl: Not Installed M2Crypto: Not Installed Mako: Not Installed msgpack-pure: Not Installed msgpack-python: 0.5.1 mysql-python: Not Installed pycparser: 2.14 pycrypto: 2.6.1 pycryptodome: 3.4.3 pygit2: Not Installed Python: 2.7.5 (default, Aug 4 2017, 00:39:18) python-gnupg: Not Installed PyYAML: 3.11 PyZMQ: 15.3.0 RAET: Not Installed smmap: Not Installed timelib: Not Installed Tornado: 4.2.1 ZMQ: 4.1.4

System Versions: dist: centos 7.4.1708 Core locale: UTF-8 machine: x86_64 release: 3.10.0-693.21.1.el7.x86_64 system: Linux version: CentOS Linux 7.4.1708 Core

Minion: salt-minion 2018.3.0 (Oxygen)

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 2
  • Comments: 22 (15 by maintainers)

Commits related to this issue

Most upvoted comments

The solution with the quotes does not work in my instance as well. (same salt version)

I’m using my list that get’s converted to Unicode to look for other values inside my configuration. So I’m having the problem: 'dict object' has no attribute u'some_string' But it does have the attribute 'some_string'.

Any ideas?

(@noaginzbursky)

As noted elsewhere in this comment thread, when a data structure is dumped using a jinja placeholder, jinja simply runs repr() on the variable: Using the json or tojson filter is equivalent to running json.dumps() on the data structure, resulting in a representation of that data without the u prefixes:

Even if I already did a json dumps in the state?

ID:
  file.managed:
    - template: jinja
    - context: 
        a: {{ someVar | json }}

I use the json filter in the state that passes the context to the jinja template. Why would I have to do a dump of the data again from within the template? I guess I was just expecting all the data I pass to the json filter to be … filtered.

You are misunderstanding how this works. You are using the json filter to coax a Python data structure to JSON so that it can be loaded as YAML back into a Python dictionary (where the strings internally will be unicode).

These are two independent Jinja templates (the SLS file and the file being managed), and they are compiled separately. If you were passing JSON into the Jinja context, you wouldn’t be able to treat it like a nested dictionary (i.e. {{ a.b.c.d }}).

Given that I ported Jinja’s tojson filter to Salt for the 2018.3.3 release (and later), and that Jinja >= 2.9 already has this filter, in my opinion this issue is fixed.

What you are expecting is for invalid YAML to be treated as valid YAML, which is problematic to say the least. Salt has tried for a while to do this via adding several hacks to our custom YAML loader, but this has in turn caused several bugs in the loader. Not only that, but removing the hacks has allowed us to support using the libyaml versions of PyYAML’s SafeLoader in the upcoming Fluorine release, resulting in a significant performance boost.

Given that removing these hacks has not only simplified our custom YAML loader, but also made it more reliable and faster, I don’t see us ever going back and attempting to make non-YAML be processed as YAML.

Yes, Jinja uses repr() on data structures. This is not likely to ever change, and I honestly don’t think it should, but you’re welcome to take it up with the Jinja maintainers. Jinja and YAML are two separate projects, and it’s not necessarily in Jinja’s interests to make rendered data structures parse as valid YAML.

Since JSON is a subset of YAML, you can dump a Python data structure to JSON using Jinja’s tojson filter and the u prefix will be removed from each string:

{{ myvar | tojson }}

This filter is new in Jinja 2.9, so you may need to upgrade Jinja to use it. Alternatively, I have ported this filter into Salt for the upcoming 2018.3.3 release, which will make it available to those who have older Jinja installed.