Vamsi Namuduri
Vamsi Namuduri

Reputation: 41

Ansible/Jinja "from_yaml" filter sorting keys in the original file

I am trying to read a YML file from a remote node in Ansible and make modifications and update it.

When I use "from_yaml" Jinja filter, I notice that the keys are sorted and when I update the file on remote, it will cause issues.

Is there a way to set Sort_Keys=False on the from_yaml filter?

Original file:

key1:
  key5:
    key3: value3
  key2:
    key5: value4
  key6:
    key7: value5

After applying "from_yaml" filter (the keys are sorted):

key1:
  key2:
    key3: value3
  key5:
    key5: value4
  key6:
    key7: value5

Upvotes: 1

Views: 1041

Answers (2)

Vladimir Botka
Vladimir Botka

Reputation: 68044

YAML standard says that there is no order of keys in a dictionary. Python3 recently(I don't remember the version. Probably 3.6 or 3.7. You can find it in the release notes.) introduced ordered keys in dictionaries. This is what you see as a result of the filter from_yaml. You'll see the dictionary also ordered without any filter when Ansible uses recent Python3. For example,

- hosts: localhost
  vars:
    key1:
      key5:
        key3: value3
      key2:
        key5: value4
      key6:
        key7: value5
  tasks:
    - debug:
        var: key1

gives (abridged)

  key1:
    key2:
      key5: value4
    key5:
      key3: value3
    key6:
      key7: value5

But, Jinja, even when using Python3.8, doesn't sort the dictionaries (I don't know why). For example,

    - debug:
        msg: |
          {% for k,v in key1.items() %}
          {{ k }}: {{ v }}
          {% endfor %}

gives (abridged)

  msg: |-
    key5: {'key3': 'value3'}
    key2: {'key5': 'value4'}
    key6: {'key7': 'value5'}

To answer your question, unfortunately, there is no such option as Sort_Keys=False. Moreover, in YAML, there is no guarantee in what order the keys of a dictionary are stored. If you want to be sure that you write a dictionary in a specific order you have to keep the list of the keys. For example,

    - debug:
        msg: |
          {% for k in list_of_keys %}
          {{ k }}: {{ key1[k] }}
          {% endfor %}
      vars:
        list_of_keys: [key5, key2, key6]

Upvotes: 2

phanaz
phanaz

Reputation: 1524

The imported data is organized in the data structure of a dict. In contrast to an array (or list), in which elements are ordered in their position and addressed via an index, the values of a dict are addressed via the respective key. However, you have no possibility to define or influence the order. Ansible always outputs a data structure of a Dict sorted by the keys.

The example you gave is wrong. Because the data structure is not changed even if the output is sorted by keys. key1.key5.key3 still returns the value value3. But in your example "After applying "from_yaml" filter" this value does not exist anymore, in your case value3 is now under key1.key2.key3. Your output does not match the output Ansible gives when the from_yaml filter is applied to said input.


Data file mydata.txt:

key1:
  key5:
    key3: value3
  key2:
    key5: value4
  key6:
    key7: value5

Ansible Task:

- debug:
    msg: "{{ lookup('file', 'mydata.txt') | from_yaml }}"

Output:

TASK [debug] *****************************
ok: [localhost] => {
    "msg": {
        "key1": {
            "key2": {
                "key5": "value4"
            },
            "key5": {
                "key3": "value3"
            },
            "key6": {
                "key7": "value5"
            }
        }
    }
}

Upvotes: 1

Related Questions