Ansible: check if variable equals string - variables

I have an ansible variable passed in on the command line as such:
ansible-playbook -e environment=staging ansible/make_server.yml
I want to load in some variables in my role dependeing on the value of environment. I have tried a lot of different methods such as:
- include_vars: staging_vars.yml
when: environment | staging
and
- include_vars: staging_vars.yml
when: "{{environment}} == "staging"
and
- include_vars: staging_vars.yml
when: "{{environment}} | match('staging')"
but nothing seems to work. How do I do this?
Details:
I am using ansible 1.7.2

Be careful with a variable called environment, it can cause problems because Ansible uses it internally. I can't remember if it's in the docs, but here's a mailing list thread:
https://groups.google.com/forum/#!topic/ansible-project/fP0hX2Za4I0
We use a variable called stage.
It looks like you'll end up with a bunch of these in a row:
- include_vars: testing_vars.yml
when: stage == "testing"
- include_vars: staging_vars.yml
when: stage == "staging"
- include_vars: production_vars.yml
when: stage == "production"
But you could also just include your environment:
- include_vars: "{{ stage }}_vars.yml"
Or, use the vars_files on a playbook level:
vars_files:
- vars/{{ stage }}_vars.yml

Related

Gitlab CI conditionally override a template variable based on which files were changes in a MR

I have template that looks like this:
/templates/.copy-echo.yml:
workflow:
rules:
- if: '$CI_COMMIT_REF_NAME == "master"'
variables:
FILE_PATH: /test_conf_1.txt
DESTINATION_HOST: somehost
stages:
- copy
- echo
copy step 1/2:
rules:
- changes:
- ${FILE_PATH}
stage: copy
script: |
echo "Add copy here!"
copy step 2/2:
rules:
- changes:
- ${FILE_PATH}
stage: echo
script: |
printenv
echo ${DESTINATION_HOST}
Now in my .gitlab-ci.yml:
include: '/templates/copy-echo.yml'
variables:
FILE_PATH: /test_conf_1.txt
DESTINATION_HOST: somehost2
Now what I want is conditionally assign a value to DESTINATION_HOST variable depending on which file got changed in merged request.
For e.g. if the merge request had updates to file test_conf_2.txt then the value for DESTINATION_HOST should be somehost2 and if the merge request had updates to file test_conf_3.txt then the value for DESTINATION_HOST should be somehost3.
Is it possible to achieve this?
You can use rules:variables: https://docs.gitlab.com/ee/ci/yaml/#rulesvariables
Example:
my job:
script: printenv
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes:
- test_conf_2.txt
variables:
DESTINATION_HOST: "somehost2"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes:
- test_conf_3.txt
variables:
DESTINATION_HOST: "somehost3"
But it doesn't work if you launch pipeline manually:
You should use rules: changes only with branch pipelines or merge
request pipelines. You can use rules: changes with other pipeline
types, but rules: changes always evaluates to true when there is no
Git push event. Tag pipelines, scheduled pipelines, manual pipelines,
and so on do not have a Git push event associated with them. A rules:
changes job is always added to those pipelines if there is no if that
limits the job to branch or merge request pipelines.

Ansible task includes undefined var, despite being defined in defaults/main.yml

I am trying to create a Galaxy role for our org's internal galaxy, which I am testing first locally. In our org we use a common list of defaults across all roles.
Ansible is throwing me a "The task includes an option with an undefined variable The error was: 'redis_download_url' is undefined" error when running my playbook, despite me having defined the variable in defaults/main.yml:
# Download
redis_version: "6.2.3"
redis_download_url: "https://download.redis.io/releases/redis-{{ redis_version }}.tar.gz"
When running my simple role/playbook.yml
---
- hosts: all
become: true
tasks:
- include: tasks/main.yml
Linked to tasks/main.yml
---
- name: Check ansible version
assert:
that: "ansible_version.full is version_compare('2.4', '>=')"
msg: "Please use Ansible 2.4 or later"
- include: download.yml
tags:
- download
- include: install.yml
tags:
- install
It should pull the tar file from tasks/download.yml as stated:
---
- name: Download Redis
get_url:
url: "{{ redis_download_url }}"
dest: /usr/local/src/redis-{{ redis_version }}.tar.gz
- name: Extract Redis tarball
unarchive:
src: /usr/local/src/redis-{{ redis_version }}.tar.gz
dest: /usr/local/src
creates: /usr/local/src/redis-{{ redis_version }}/Makefile
copy: no
The redis_download_url var is defined in defaults/main.yml which as I understand ansible should be able to locate there. I also have similar vars defined in defaults/task.yml eg.
redis_user: redis
redis_group: "{{ redis_user }}"
redis_port: "6379"
redis_root_dir: "/opt/redis"
redis_config_dir: "/etc/redis"
redis_conf_file: "{{ redis_config_dir }}/{{ redis_port }}.conf"
redis_password: "change-me"
redis_protected_mode: "yes"
and I assume they are also not able to be found/seen by ansible (but it does not get that far). I have also checked all file permissions and they seem to be fine.
Apologies in advance if the question is badly formatted.
As per documentation:
If you include a task file from a role, it will NOT trigger role behavior, this only happens when running as a role, include_role will work.
To get the role functionality of reading variables from defaults/main.yml, you'll need to use include_role or roles: [].
- hosts: all
become: true
tasks:
- include_role:
name: myrole
OR
- hosts: all
become: true
roles:
- myrole

How to create Gitlab CI rules that are evaluated as AND instead of OR

The following gitlab ci job will run if the variable $CI_COMMIT_TAG is set OR if the ./versions.txt file has changed.
some-job:
script:
- echo "Do some fancy stuff.";
rules:
- if: $CI_COMMIT_TAG
when: always
- changes:
- ./versions.txt
However, what I need is for this job to run when $CI_COMMIT_TAG is set AND ./versions.txt is changed. I don't want the job to run if only one of these evaluates to true. This was the behaviour in only/changes feature, but the only (and except)-feature is less powerful and deprecated.
Is what I want currently possible with gitlab ci?
From Docs:
In the following example:
We run the job manually if Dockerfile or any file in docker/scripts/ has changed AND $VAR == "string value". Otherwise, the job will not be included in the pipeline.
docker build:
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
rules:
- if: '$VAR == "string value"'
changes: # Will include the job and set to when:manual if any of the follow paths match a modified file.
- Dockerfile
- docker/scripts/*
when: manual
Your code will look something like this.
some-job:
script:
- echo "Do some fancy stuff.";
rules:
- if: $CI_COMMIT_TAG
changes:
- versions.txt
when: manual

How to have a gitlab CI job to be triggred only manual on branches and always automatically on master?

I have a pages job that I want to run manual on branches, but automatically triggered on master:
pages:
stage: deploy
cache:
paths:
- public
script:
- scripts/pages.sh
artifacts:
paths:
- public
expire_in: 2 days
So I want a combination of:
only:
- master
when: always
only:
- branches
except:
- master
when: manual
Is that possible?
This should be possible to do if you use GitLab CI rules. This is an example where the shell is powershell and it shows the current time and branch/tag name:
pages:
script:
- mkdir public
- date > public\index.html
- $CI_COMMIT_REF_NAME >> public\index.html
artifacts:
paths:
- public
rules:
- if: '$CI_COMMIT_BRANCH == "master"'
when: always
- if: '$CI_COMMIT_BRANCH == null'
when: never
- when: manual
GitLab matches each individual rule from top to bottom. If the branch is named 'master', the job gets marked with when: always. If the branch name is null, this is a tag, and the job is marked with never. If this is not a branch named master, nor a tag, this is a normal branch, and the job is marked with manual.
As Aleksey Tsalolikhin described, you can remove this rule:
- if: '$CI_COMMIT_BRANCH == null'
when: never
You will then get the option to run the pipeline for your tags as well, like this:
If this is what you want or not, that is up to you.
I've tweaked the answer by MrBerta -- the third command was missing the echo command.
I also changed the slashes from backslashes to regular forward slashes so I can use the Linux shell rather than Powershell.
It now works.
Here is the gitlab-ci.yml file -- with credit to MrBerta.
pages:
script:
- mkdir public
- date > public/index.html
- echo $CI_COMMIT_REF_NAME >> public/index.html
artifacts:
paths:
- public
rules:
- if: '$CI_COMMIT_BRANCH == "master"'
when: always
- if: '$CI_COMMIT_BRANCH == null'
when: never
- when: manual
I tried pushing to master, and my GitLab Pages content was updated as expected; and I tried pushing to a feature branch, and the manual "Play" button came up in the CI/CD pipeline UI.
When I pushed a tag (with detached head, i.e., not on any branch), I could not test it -- GitLab CI did not run a pipeline automatically, and when I tried to Run Pipeline, and picked my tag, GitLab threw an error: "The form contains the following error: No stages / jobs for this pipeline."
So, I would simplify this to:
pages:
script:
- mkdir public
- date > public/index.html
- echo $CI_COMMIT_REF_NAME >> public/index.html
artifacts:
paths:
- public
rules:
- if: '$CI_COMMIT_BRANCH == "master"'
when: always
- when: manual
This pages job runs manually on branches (and tags but I couldn't test it), but automatically triggered on master, as the original poster requested.
You will need to define two stages. you can either copy/paste or use anchors:
.deploy_stage: &deploy_stage
stage: deploy
cache:
paths:
- public
script:
- scripts/pages.sh
artifacts:
paths:
- public
expire_in: 2 days
deploy_manual:
<<: *deploy_stage
only:
- branches
when: manual
deploy_master:
<<: *deploy_stage
only:
- master

Ansible block vars

How can I set in Ansible block vars (visible only for tasks in block) ?
I tried:
---
- hosts: test
tasks:
- block:
- name: task 1
shell: "echo {{item}}"
with_items:
- one
- two
but it seems that it's a wrong way.
If you want to define a variable for a block:
- block:
- debug:
var: var_for_block
vars:
var_for_block: "value for var_for_block"
If you want to "loop over blocks" as your code suggests - you can't. It's not implemented in Ansible.
Follow this thread.
For now consider saving the tasks to a separate file and use include instead.
#Shasha99
you might be able to include a file with a block in it so you can still benefit from the try/catch
includeFile.yml :
- block
- name: Finding the package.
shell: rpm -qa | grep "{{pkgName}}"
register: package
- name: Uninstalling the package.
shell: rpm -e "{{package}}"
always:
- debug: msg="this always executes"
main.yml:
---
- hosts: all
vars:
- packageList : ["pkg1","pkg2","pkg3","pkg4"]
tasks:
- include: includeFile.yml pkgName="{{item}}"
with_items: packageList