Ansible changes the empty string from string to NoneType

Ansible changes the empty string from string to NoneType

Ansible amazes me almost every time I have to use it. They have this nice habit of randomly changing around things, for the sake of change. That means that every 6 months or so when I have to do something with it, a week is spent fixing all the new breaks they just introduced.

The most recent brainfuck comes from code like this that handles optional user variables:

eff_reload_cmd: "{% if reload_cmd is defined and reload_cmd | string | length %}{{ reload_cmd }}{% endif %}"

{% if eff_reload_cmd | length %}
(do whatever)
{% endif %}

The idea is simple: the variable may or may not be set. If it is, use it, otherwise ignore it. And this worked just fine. Check if the variable is defined, check for length, done.

But then comes the clever Ansible dev and says noooo, that’s not how it works. That’s not how any of this works. Why don’t we change the type of the empty string to NoneType? Now if you try to print it, or do anything with it, all you get is:

TASK [repo : debug] ************************************************************
task path: /repo/tasks/cron.yml:2
ok: [127.0.0.1] => {
    "msg": null
}

Why, seriously, why? What does that solve? But who cares, right? Just check for length anyway, right? Surely, the length of a NoneType “null” is definitely 0, right?

[ERROR]: Task failed: The filter plugin 'ansible.builtin.length' failed: object of type 'NoneType' has no len()

Task failed.
Origin: /repo/tasks/cron.yml:15:3

13     enabled: true
14
15 - name: Deploy acme.sh cronjob
     ^ column 3

<<< caused by >>>

The filter plugin 'ansible.builtin.length' failed: object of type 'NoneType' has no len()
Origin: /repo/templates/acme.j2

fatal: [127.0.0.1]: FAILED! => {
    "changed": false,
    "msg": "Task failed: The filter plugin 'ansible.builtin.length' failed: object of type 'NoneType' has no len()"
}

PLAY RECAP *********************************************************************
127.0.0.1                  : ok=8    changed=0    unreachable=0    failed=1    skipped=1    rescued=0    ignored=0

Gee, isn’t that amazing? Not only is the type changed from str to NoneType, they also don’t have a length attribute for NoneType. Helpful!

Alright then, just cast this NoneType empty string to string, that’ll do, we get our nice empty string back. Right? Well, guess what:

TASK [repo : debug] ************************************************************
ok: [127.0.0.1] => {
    "msg": "None"
}

That’s right. If you cast a null NoneType empty string (are you still with me?) to string, you get the None string with a length of 4.

What. The. Actual. Fuck. Ansible, go home, you’re drunk. It’s almost as if they worked really, really hard to break my code, no matter what.

Soooo ummm, how about… cast to string, check for length, AND also check if it’s not a NoneType? So the previous

{% if eff_reload_cmd | length %}

becomes

{% if eff_reload_cmd | string | length and eff_reload_cmd | type_debug != 'NoneType' %}

I really, honestly, truthfully don’t see why this is needed now, and why they had to do this.

Welp, just another day with Ansible I guess.

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *