I'm trying to write an Ansible role that configures Redis cluster.
The shell command that creates the cluster is:
$ /usr/bin/redis-cli --user admin --pass mypass --cluster create 10.226.2.194:6379 10.226.2.196:6379 10.226.2.195:6379 --cluster-replicas 1 --cluster-yes
I pass username, password, IP addresses of Redis servers and number of replicas as extra-vars
in shell script
#!/bin/sh
ansible-playbook /etc/ansible/playbook-redis.yml -vv \
--extra-vars='redis_admin_user=admin redis_admin_password=mypass' \
--extra-vars='redis_cluster_members="10.226.2.194:6379 10.226.2.196:6379 10.226.2.195:6379" redis_cluster_replicas=1'
After reading the documentation for the command module I wrote the following task:
- name: Create Redis cluster
ansible.builtin.command:
argv:
- /usr/bin/redis-cli
- "--user {{ redis_admin_user }}"
- "--pass {{ redis_admin_password }}"
- "--cluster create {{ redis_cluster_members }}"
- "--cluster-replicas {{ redis_cluster_replicas }}"
- --cluster-yes
But when I run it I get the following error:
"Unrecognized option or bad number of args for: '--user admin'"
which seems to be error of redis-cli that it doesn't recognize the argument.
I was able to get around the username and password by changing the task as following:
- name: Create Redis cluster
ansible.builtin.command:
argv:
- /usr/bin/redis-cli
- --user
- "{{ redis_admin_user }}"
- --pass
- "{{ redis_admin_password }}"
- --cluster create
- "{{ redis_cluster_members }}"
- --cluster-replicas
- "{{ redis_cluster_replicas }}"
- --cluster-yes
This is not exactly nice looking, but at least it passes the username and the password, because the following task works:
- name: Get server info
ansible.builtin.command:
argv:
- /usr/bin/redis-cli
- --user
- "{{ redis_admin_user }}"
- --pass
- "{{ redis_admin_password }}"
- info
But now it can't get past the --cluster create
argument:
"Unrecognized option or bad number of args for: '--cluster create'"
I also tried - --cluster create "{{ redis_cluster_members }}"
and
- --cluster
- create
- "{{ redis_cluster_members }}"
to no avail.
What is the correct syntax to run this command?
P.S.
The following task works correctly:
- name: Get Redis version
ansible.builtin.command:
argv:
- /usr/bin/redis-cli
- --version
and returns the version.
P.P.S The following also works
- name: Create Redis cluster
ansible.builtin.command:
cmd: "/usr/bin/redis-cli --user {{ redis_admin_user }} --pass {{ redis_admin_password }} --cluster create {{ redis_cluster_members }} --cluster-replicas {{ redis_cluster_replicas }} --cluster-yes"
Got help from Ansible devs at GitHub. The reason for this behavior is how command module and redis-cli utility work with arguments.
argv
adds delimiters when passing the command to the command line. So this:
- name: Create Redis cluster
ansible.builtin.command:
argv:
- /usr/bin/redis-cli
- "--user {{ redis_admin_user }}"
- "--pass {{ redis_admin_password }}"
- "--cluster create {{ redis_cluster_members }}"
- "--cluster-replicas {{ redis_cluster_replicas }}"
- --cluster-yes
turns to (mind the single quotation marks)
/usr/bin/redis-cli '--user admin' '--pass mypass' '--cluster create 10.226.2.194:6379 10.226.2.196:6379 10.226.2.195:6379' '--cluster-replicas 1' '--cluster-yes'
If you enter the above command directly you'll get the same error when running the task - "Unrecognized option or bad number of args for: '--user admin'"
This is because how redis-cli delimits arguments. From redis-cli documentation:
When
redis-cli
parses a command, whitespace characters automatically delimit the arguments.
So, the correct command should be (note the single quotation marks)
/usr/bin/redis-cli '--user' 'admin' '--pass' 'mypass' '--cluster' 'create' '10.226.2.194:6379' '10.226.2.196:6379' '10.226.2.195:6379' '--cluster-replicas' '1' '--cluster-yes'
But for the Ansible task there is a problem - redis_cluster_members
variable.
The solution suggested to me at GitHub:
- name: Create Redis cluster
ansible.builtin.command:
argv: "{{ redis_argv | flatten }}"
vars:
redis_argv:
- /usr/bin/redis-cli
- --user
- "{{ redis_admin_user }}"
- --pass
- "{{ redis_admin_password }}"
- --cluster
- create
- "{{ redis_cluster_members | split }}"
- --cluster-replicas
- "{{ redis_cluster_replicas }}"
- --cluster-yes
It works, but I decided to use a workaround.
In vars/main.yml
I defined a variable
redis_create_cluster_command: >
/usr/bin/redis-cli --user {{ redis_admin_user | quote }} --pass {{ redis_admin_password | quote }}
--cluster create {{ redis_cluster_members }}
--cluster-replicas {{ redis_cluster_replicas | quote }} --cluster-yes
For the explanation about >
please read this article, particularly the second part.
The quote
filter is for, as suggested in command module's documentation, "to avoid injection issues". Note that it's missing from redis_cluster_members
line because it too breaks the arguments.
The task itself:
- name: Create Redis cluster
ansible.builtin.command:
cmd: "{{ redis_create_cluster_command }}"
creates: "{{ redis_etc }}/redis_cluster.created"
no_log: true