How can I add keys to a hash variable in Ansible YAML - environment-variables

I've got a variable in Ansible that I use to pass environment variables to a task. However, I've got another playbook that uses the role, and I'd like to tack more values onto the variable. How can I accomplish this? For example, I want to have a different ORACLE_HOME depending on which type of server I'm running the playbook against.
--- group_vars/application.yml
environment_vars:
PIP_EXTRA_INDEX_URL=https://my.local.repo/pypi
--- group_vars/ubuntu.yml
environment_vars:
ORACLE_HOME: '/usr/lib/oracle/instantclient_11_2'
--- group_vars/centos.yml
environment_vars:
ORACLE_HOME: '/opt/oracle/instantclient_11_2'
--- roles/test_role/tasks/main.yml
- name: Install Python Requirements
pip:
name: my_app==1.0
environment: environment_vars
--- main.yml
- hosts: application
roles:
- role: test_role
--- inventory
[application:children]
ubuntu
centos

I'd do it this way:
environment_vars:
ORACLE_HOME: >
{% if ansible_distribution=='CentOS' %}
/opt/oracle/instantclient_11_2
{% elif ansible_distribution == 'Debian' %}
/usr/local/oracle/instantclient_11_2
{% else %}
/dev/null
{% endif %}
It may need some tweaking to get rid of white space.

Related

How to convert system.string to a win module path type

My overall goal is to determine whether or not the file control.lnk exists in the Windows administrator's desktop. The challenge of this (as i currently see it) is to get the complete path/file spec of such into win_stat as a valid path argument. Originally, i used variations of $env:USERPROFILE and appended \Desktop\control.lnk to such but got system.string is not path errors (dialog to that effect). Lastly, i tried to derive this path into an Ansible variable via Powershell task and then use the ansible variable ...
- name: Get Profile path
# This will get logon user path; different than ansible_environment path
ansible.windows.win_powershell:
script:
$env:USERPROFILE
register: profilePath
- name: Debug Play essentials
debug:
msg:
- "UserProfile path: {{ profilePath.output}}"
... and this produces the path as i expect. However (in all cases), i still get errors of the form:
msg: argument for path is of type System.String and we were unable to convert to path: string value contains invalid path characters, cannot convert to path" }
Here's the win_stat task i've been trying along with the path-failures I've tried as comments ...
` - name: Are Shortcuts installed
ansible.windows.win_stat:
# This is a typical without the variable ...
# path: C:\Users\Administrator.PAxAnsible\Desktop\Control.lnk
# Using $env:USERPROFILE directly doesn't work; is resolved as type system.string and not of type path | cannot covert ...
# $env:USERPROFILE\Desktop\Control.lnk
# '$env:USERPROFILE\Desktop\Control.lnk'
# "$env:USERPROFILE\Desktop\Control.lnk" -invalid syntax; cannot commit
# "$env:USERPROFILE\\Desktop\\Control.lnk"
# '$env:USERPROFILE\\Desktop\\Control.lnk'
# '"$env:USERPROFILE\\Desktop\\Control.lnk"'
# $env:USERPROFILE\\Desktop\\Control.lnk
# "$env:USERPROFILE/Desktop/Control.lnk"
# $env:USERPROFILE/Desktop/Control.lnk
# '"$env:USERPROFILE"/Desktop/Control.lnk' -string contains invalid path characters, cannot convert to path"
# '$env:USERPROFILE + /Desktop/Control.lnk'
# $env:USERPROFILE + '/Desktop/Control.lnk'
# Template error ...
# '"{{ $env:USERPROFILE}}"/Desktop/Control.lnk' -unexpected char '$' at 4.
#
# System.String issues while using a derived path (>> is how it's translated) ...
# '{{ profilePath.output}}/Desktop/Control.lnk' >> "['C:\\\\Users\\\\Administrator']/Desktop/Control.lnk"
# '{{ profilePath.output}}\\Desktop\\Control.lnk' >> "['C:\\\\Users\\\\Administrator']\\\\Desktop\\\\Control.lnk"
# "{{ profilePath.output}}\Desktop\Control.lnk" >> syntax error; cannot community
# '"{{ profilePath.output}}\Desktop\Control.lnk"' >> "\"['C:\\\\Users\\\\Administrator']\\Desktop\\Control.lnk\""
# "{{ profilePath.output}}\\Desktop\\Control.lnk" >> "['C:\\\\Users\\\\Administrator']\\Desktop\\Control.lnk"
path: '"{{ profilePath.output}}"\\Desktop\\Control.lnk'
register: Shortcuts
when: AppServShortcuts != "none"
I see that the bordering brackets as are of the output are not being removed when combined with the static text as is within the path argument. -I suspect this will have to be done before using the output variable as a component of the path argument. As for using $env:USERPROFILE directly as a path component, I haven't figured out a syntax that works.
I'm hoping someone out there can advise correct syntax to make this win_stat query work ;) ...

Parse YAML using shell

I have a yaml something of this sort and need to parse and get the value of 'url' here.
name: Yaml_Test
servers:
- name: host_ip
host: host_name
port: 443
scheme: https
variables:
- constant:
name: url
value: https://url_here
With the current versions of both kislyuk/yq and mikefarah/yq, use select to filter the array items:
.variables[].constant | select(.name == "url").value
https://url_here
Using kislyuk/yq, you may want to add the -r option to output raw text:
yq -r '…' file.yaml
Using an older version of mikefarah/yq, you need to specify the command e or eval:
yq e '…' file.yaml
Edit: Although not recommended, here's as requested in a comment a grep-only solution which heavily relies on how the YAML document is represented textually, based on the sample provided (i.e. no other occurrence of name: url etc.):
grep -A1 'name: url' file.yaml | grep -o 'http.*'
https://url_here

How to overwrite log4j.properties in confluent zookeeper

We are facing an issue that we are not able to overwrite log4j.properties.
Here is our code
Dockerfile
FROM confluentinc/cp-zookeeper:6.2.0
#RUN chmod 777 /etc/kafka/connect-log4j.properties
USER root
COPY ./log4j.properties /etc/kafka/log4j.properties
log4j.properties
log4j.rootLogger=INFO, ROLLINGFILE
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d] %p %m (%c)%n
log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLINGFILE.Threshold=DEBUG
log4j.appender.ROLLINGFILE.File=/var/log/zookeeper.log
Note :- We are using this confluent zookeeper 'confluentinc/cp-zookeeper:6.2.0'
Or, you could copy a template file instead
FROM confluentinc/cp-zookeeper:6.2.0
COPY --chown=appuser:appuser ./log4j.properties.template /etc/confluent/docker/log4j.properties.template
where
log4j.rootLogger={{ env["ZOOKEEPER_LOG4J_ROOT_LOGLEVEL"] | default('INFO') }}, rollingfile
log4j.appender.rollingfile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingfile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingfile.File=/var/log/kafka/zookeeper.log
log4j.appender.rollingfile.layout.ConversionPattern=[%d] %p %m (%c)%n
log4j.appender.rollingFile.MaxFileSize=10MB
log4j.appender.rollingfile.MaxBackupIndex=1
log4j.appender.rollingFile.append=true
{% if env['ZOOKEEPER_LOG4J_LOGGERS'] %}
{% set loggers = parse_log4j_loggers(env['ZOOKEEPER_LOG4J_LOGGERS']) %}
{% for logger,loglevel in loggers.items() %}
log4j.logger.{{logger}}={{loglevel}}, rollingfile
{% endfor %}
{% endif %}
You shouldn't copy in a log4j file. This gets overridden at runtime with a Jinja2 template defined here - https://github.com/confluentinc/kafka-images/blob/master/zookeeper/include/etc/confluent/docker/log4j.properties.template
To set custom loggers, simply add an environment variable for ZOOKEEPER_LOG4J_LOGGERS to the existing image rather than creating your own
You should also use Docker logging bridges rather than force the process to write to a file within the container

Ansible shell module returns error when grep results are empty

I am using Ansible's shell module to find a particular string and store it in a variable. But if grep did not find anything I am getting an error.
Example:
- name: Get the http_status
shell: grep "http_status=" /var/httpd.txt
register: cmdln
check_mode: no
When I run this Ansible playbook if http_status string is not there, playbook is stopped. I am not getting stderr.
How can I make Ansible run without interruption even if the string is not found?
grep by design returns code 1 if the given string was not found. Ansible by design stops execution if the return code is different from 0. Your system is working properly.
To prevent Ansible from stopping playbook execution on this error, you can:
add ignore_errors: yes parameter to the task
use failed_when: parameter with a proper condition
Because grep returns error code 2 for exceptions, the second method seems more appropriate, so:
- name: Get the http_status
shell: grep "http_status=" /var/httpd.txt
register: cmdln
failed_when: "cmdln.rc == 2"
check_mode: no
You might also consider adding changed_when: false so that the task won't be reported as "changed" every single time.
All options are described in the Error Handling In Playbooks document.
Like you observed, ansible will stop execution if the grep exit code is not zero. You can ignore it with ignore_errors.
Another trick is to pipe the grep output to cat. So cat exit code will always be zero since its stdin is grep's stdout. It works if there is a match and also when there is no match. Try it.
- name: Get the http_status
shell: grep "http_status=" /var/httpd.txt | cat
register: cmdln
check_mode: no

why ansible always replaces double quotes with single quotes in templates?

I am trying to generate Dockerfiles with Ansible template - see the role source and the template in Ansible Galaxy and Github
I need to genarate a standard Dockerfile line like:
...
VOLUME ["/etc/postgresql/9.4"]
...
However, when I put this in the input file:
...
instruction: CMD
value: "[\"/etc/postgresql/{{postgresql_version}}\"]"
...
It ends up rendered like:
...
VOLUME ['/etc/postgresql/9.4']
...
and I lose the " (which renders the Dockerfiles useless)
Any help ? How can I convince Jinja to not substitute " with ' ? I tried \" , |safe filter, even {% raw %} - it just keeps doing it!
Update:
Here is how to reproduce the issue:
Go get the peruncs.docker role from galaxy.ansible.com or Github (link is given above)
Write up a simple playbook (say demo.yml) with the below content and run: ansible-playbook -v demo.yml. The -v option will allow you to see the temp directory where the generated Dockerfile goes with the broken content, so you can examine it. Generating the docker image is not important to succeed, just try to get the Dockerfile right.
- name: Build docker image
hosts: localhost
vars:
- somevar: whatever
- image_tag: "blabla/booboo"
- docker_copy_files: []
- docker_file_content:
- instruction: CMD
value: '["/usr/bin/runit", "{{somevar}}"]'
roles:
- peruncs.docker
Thanks in advance!
Something in Ansible appears to be recognizing that as valid Python, so it's getting transformed into a Python list and then serialized using Python's str(), which is why you end up with the single-quoted values.
An easy way to work around this is to stick a space at the beginning of the value, which seems to prevent it from getting converted into Python:
- name: Build docker image
hosts: localhost
vars:
- somevar: whatever
- image_tag: "blabla/booboo"
- docker_copy_files: []
- docker_file_content:
- instruction: CMD
value: ' ["/usr/bin/runit", "{{somevar}}"]'
roles:
- peruncs.docker
This results in:
CMD ["/usr/bin/runit", "whatever"]

Resources