Ansible ambiguous env value - ansible

I'm getting the following warning in Ansible:
[WARNING]: Non-string value found for env option. Ambiguous env options should be wrapped in quotes to avoid YAML parsing. This will become an error in Ansible 2.8. Key: PORT; value will be treated as: 12345
So I went and looked up the origin of this value and wrapped all instances of it in quotes. Or so I thought. I'm still getting the warning.
So I went to the place in the code where it appeared and it seems to be this:
docker_container:
env: '{{ params | combine(extra_params, {"PORT": my_port|int + amount|int * 10 })}}'
This is a setup for dealing with multiple instances of the same container, each getting a unique port, as to not interfere with one another.
And I'm not sure how to fix that without breaking that setup. Can it be cast to string again after the calculation is done? Should I do it beforehand? What's the best option here?

As the ansible documentation for the docker_container module under env states
Values which might be parsed as numbers, booleans or other types by the YAML parser must be quoted (e.g. "true") in order to avoid data loss.
so you have to convert your result to a quoted string.
env: '{{ params | combine(extra_params, {"PORT": (my_port|int + amount|int * 10) | string })}}'

Related

Expand complex ansible variable

I have distinct ansible variables which are combined in a single command. More precisely, the system defines a variable with default value:
# This exists in an ansible variable definition file
instrumentor = ''
At some the python script that "creates" the ansible commands specifies the extra arguments to override the default values. I'm jumping through some hoops (using \" and \') because the desired value contain spaces:
# This is a line from python script
instrumentation_specs =
'-e \'instrumentor=\"/software/cuda-memcheck --tool racecheck --log-file /tmp/memlog\"\''
On the ansible side then the command is structured as
;; This exists in a jinja template (.j2 extension)
;; command is what the start.yml script will be running
command="{{ instrumentor }}" my_program
Unfortunately I cannot get this to work. The command is reported to expand to something that contains extra ' and ":
start.yml (...) -e 'instrumentor="/software/cuda-memcheck --tool racecheck --log-file /tmp/memlog"' (...)
The weird thing is that
if I create another variable before command that only contains instrumentor, it is reported to have the exact value that I need
instr_tmp = "{{ instrumentor }}"
command = "{{ instr_tmp }}" my_program
i.e. the extra ' and " don't appear in instr_tmp but do appear in command
If I ommit the \" and \' on the python side (when creating the string that is used to provide the extra arguments) everything that's after a space gets omitted.
Anyone knows how to specify such a variable override, i.e. how the string should look like when I specify a variable that contains multiple space separated strings (instrumentor), but that variable is supposed to be plugged into another ansible variable (command).

How do I use a yaml anchor in an Ansible when conditional?

I'm trying to check the value of a variable in a role, but I don't know what the variable name will be beforehand, as it is set outside the role.
I tried to use a yaml anchor to achieve this like so:
- set_fact:
set_anchor: &job_status_data_key "{{ job_running_check_key }}"
[...]
- name: Set current state
set_fact:
loop_finished_successfully: true
when: *job_status_data_key != "Running"
Where job_running_check_key at the start, is set outside the role and contains the variable name I'll be checking against.
but ansible appears not to appreciate pointers in the conditionals
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)
Syntax Error while loading YAML.
did not find expected key
The error appears to be in 'path/to/role/task.yml': line 15, column 30, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
loop_finished_succesfully: true
when: *job_status_data_key != "Running"
^ here
There's probably a proper syntax to achieve this, but I haven't managed to figure it out until now.
You're confusing ansible features with YAML features.
A YAML alias is a reference to an existing node. An ansible condition is a Jinja2 expression without quotes.
YAML doesn't know about ansible or Jinja. A YAML alias must be part of the YAML structure. Jinja, on the other hand, does not know about YAML. The consequences are:
*job_status_data_key != "Running"
is invalid YAML. The YAML syntax defines that an alias starts with * and contains a single name of an anchor. The following !=… content is therefore a syntax error. Now you might think „okay, let's quote it then so that YAML reads it as string and Jinja can evaluate it“:
'*job_status_data_key != "Running"'
This is now a valid YAML scalar. If given as value for when:, it will be evaluated as Jinja expression. The thing is, Jinja doesn't know that *job_status_data_key is supposed to be a YAML alias and in fact, at the time this expression is evaluated, the information about the anchored value of that name has already vanished.
The bottom line is: Don't use anchors and aliases for variables. They are a tool designed for serializing cyclic data structures inside YAML. Ansible offers variables and you should use those instead.

Using date as an ENV variable in GitHub action

This is very funny but very frustrating problem. I am using an ENV variable, which specifies date. I provide an ISO 8601 compliant version and in application, I retrieve it and parse. When I specify it in GH action workflow, it is get parsed as a date (rather than a string) and formatted. Therefore, my application parsing fails.
Example:
.github/workflows/rust.yaml
env:
MY_DATE: '2020-10-07T12:00:00+01:00'
run: echo $MY_DATE
Result (GH action UI):
env:
TMOU_GAME_END: 10/07/2020 11:00:00
10/07/2020 11:00:00
It is specific to GitHub action and their yaml parsing, it works OK on Heroku, on various local setups, etc.
Things I tried and they don't work:
using no quotes, single quotes ('), double quotes (")
setting another ENV var, LC_TIME to en_DK.UTF-8
using !!str shorthand (see https://yaml.org/spec/1.2/spec.html, section Example 2.23. Various Explicit Tags); this one fails either with  The workflow is not valid. .github/workflows/rust.yml: Unexpected tag 'tag:yaml.org,2002:str' or with The workflow is not valid. .github/workflows/rust.yml: The scalar style 'DoubleQuoted | SingleQuoted' on line 29 and column 24 is not valid with the tag 'tag:yaml.org,2002:str'
Is there any help? Any secret parameter I can turn on? Any escaping sequence? I just wanna GH Actions yaml parser to treat the value as a string.
Surprisingly, it seems GitHub Actions workflow YAML parser does not fully implement the standard and using explicit typing (like !!str) does not work. You can, however, workaround it by setting the environment variable to the desired value not in the YAML file itself but dynamically during workflow execution using a dedicated workflow command:
steps:
- name: Dynamically set MY_DATE environment variable
run: echo "MY_DATE=2020-10-07T12:00:00+01:00" >> $GITHUB_ENV
- name: Test MY_DATE variable
run: echo ${{ env.MY_DATE }}
This should do the trick.

Multiline Template - string escaping in Ansible

I'm trying to get an effect similar to this article: https://gryzli.info/2017/12/21/ansible-debug-print-variables/
Which means in my case:
- name: Gather facts
vars:
msg: |
"{{ansible_distribution}}"
"{{ansible_distribution_major_version}}"
"{{ansible_distribution_release}}"
"{{ansible_distribution_version}}"
The problem ist that without quotes it throws an error to add the quotes. With quotes it throws the same error:
We could be wrong, but this one looks like it might be an issue with missing quotes. Always quote template expression brackets when theystart a value....
How do I correctly escape this multiline string so that Ansible can parse it? Or does this kind of code no longer work?
Q: "How do I correctly escape this multiline string so that Ansible can parse it?"
A: The problem is the indentation of the block. Quoting from Example 8.3. Invalid Block Scalar Indentation Indicators
ERROR:
A leading all-space line must not have too many spaces.
A following text line must not be less indented.
The text is less indented than the indicated level.
The correct syntax is (with or without quotation)
vars:
msg: |
"{{ansible_distribution}}"
"{{ansible_distribution_major_version}}"
"{{ansible_distribution_release}}"
"{{ansible_distribution_version}}"

How do I evaluate template expressions in my Ansible plugins?

I just wrote a small Ansible lookup plugin for fetching passwords from pass. I've set this variable in group_vars/all.yml:
pass_store: "{{ inventory_dir }}/passwords"
Inside my plugin I fetch this variable and pass it to os.environ["PASSWORD_STORE_DIR"].
The problem is that the value hasn't been evaluated. The literal value of the variable is used, not the evaluated value.
For example:
os.environ["PASSWORD_STORE_DIR"] = variables["pass_store"]
I now expect the environment variable PASSWORD_STORE_DIR to contain:
/home/tomas/my-project/passwords
Instead it contains:
{{ inventory_dir }}/passwords
Does the Ansible API provide some way of evaluating variables containing template expressions like this?
You can try this:
os.environ["PASSWORD_STORE_DIR"] = self._templar.template(variables["pass_store"])
This will evaluate the variable content and replace Jinja tokens by appropriate values.

Resources