Search code examples
ansibleredis-cluster

How can I pass variables to command module's argv parameter in Ansible?


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"

Solution

  • 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