ansible

Stop playbook run when condition is not met


I am trying to figure it out how to stop instantly my playbook when tests are failed for specific branch, master in this case.

This is my Ansible code:

  - name: 'Run Tests'
    shell: testing.sh | tee "testing.log"
    register: testing

  - name: 'Show log'
    debug: 
      msg: "{{ testing.stdout_lines }}"

  - name: 'Set fact'
    set_fact:
       tests: >-
        {{
          testing.stdout_lines | join(' ') | regex_findall('(\d+)\s*failed') | map('int') | max == 0
        }}

Solution

  • For example, given the script for testng

    shell> cat /tmp/testing.sh
    #!/usr/bin/sh
    echo 101 ${1}
    echo 102 ${2}
    echo 103 ${3}
    

    gives

    shell> /tmp/testing.sh ok failed failed
    101 ok
    102 failed
    103 failed
    

    In the play for testing, declare the number of failed lines and terminate the play if any test fails

    shell> cat pb.yml
    - hosts: localhost
    
      vars:
    
        test_regex: '\d+\s*failed'
        tests_failed: "{{ out.stdout_lines | select('search', test_regex) | length }}"
    
      tasks:
    
        - command: "/tmp/testing.sh {{ p1 }} {{ p2 }} {{ p3 }}"
          register: out
    
        - debug:
            var: out.stdout_lines
    
        - debug:
            var: tests_failed
    
        - assert:
            that: tests_failed == 0
            fail_msg: "ERR number of failed tests: {{ tests_failed }}"
    

    gives, for example

    shell> ansible-playbook pb.yml -e "p1=ok p2=failed p3=failed"
    
    PLAY [localhost] ****************************************************************************************************
    
    TASK [command] ******************************************************************************************************
    changed: [localhost]
    
    TASK [debug] ********************************************************************************************************
    ok: [localhost] => 
        out.stdout_lines:
        - 101 ok
        - 102 failed
        - 103 failed
    
    TASK [debug] ********************************************************************************************************
    ok: [localhost] => 
        tests_failed: '2'
    
    TASK [assert] *******************************************************************************************************
    fatal: [localhost]: FAILED! => 
        assertion: tests_failed == 0
        changed: false
        evaluated_to: false
        msg: 'ERR number of failed tests: 2'
    
    PLAY RECAP **********************************************************************************************************
    localhost: ok=3    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
    

    If you sum the selected numbers

        test_regex: '(\d+)\s*failed'
        tests_failed: "{{ out.stdout_lines | map('regex_findall', test_regex)
                                           | flatten
                                           | map('int')
                                           | sum }}"
    

    the play will fail

        msg: 'ERR number of failed tests: 205'
    

    Notes:

    Use rc to return the error code. For example, the script

    shell> cat /tmp/testing.sh 
    #!/usr/bin/sh
    $1
    

    returns 0

    shell> /tmp/testing.sh true
    shell> echo $?
    0
    

    or 1

    shell> /tmp/testing.sh false
    shell> echo $?
    1
    

    In a play, let the task fail if rc > 1

    shell> cat pb.yml
    - hosts: localhost
    
      tasks:
    
        - command: "/tmp/testing.sh {{ param | d('true') }}"
          register: out
          failed_when: out.rc > 1
    
        - debug:
            var: out.rc
    

    gives (abridged) 0

    shell> > ansible-playbook pb.yml
      ...
    ok: [localhost] => 
        out.rc: '0'
    

    or 1

    shell> > ansible-playbook pb.yml -e param=false
      ...
    ok: [localhost] => 
        out.rc: '1'
    

    Now, you can stop the play instantly. For example,

        - assert:
            that: out.rc < 2
            fail_msg: "ERR rc={{ out.rc }}"
    

    Fit the script rc and conditions to your needs.


    Example of a complete playbook for testing

    - hosts: localhost
    
      tasks:
    
        - command: "/tmp/testing.sh {{ param | d('true') }}"
          register: out
          failed_when: out.rc > 1
    
        - debug:
            var: out.rc
    
        - assert:
            that: out.rc < 2
            fail_msg: "ERR rc={{ out.rc }}"
    

    Create rc codes in your script. For example,

    shell> cat /tmp/testing2.sh 
    #!/usr/bin/sh
    
    # Partameter undefined or empty
    if [ -z "${1}" ]; then
        return 2
    fi
    
    # File does not exist
    if [ ! -e "${1}" ]; then
        return 3
    fi
    
    # File not executable
    if [ ! -x "${1}" ]; then
        return 4
    fi
    
    ${1}
    

    and use them in a play. For example,

    shell> cat pb.yml
    - hosts: localhost
    
      vars:
    
        err:
          2: Partameter undefined or empty.
          3: File does not exist.
          4: File not executable.
      
      tasks:
    
        - command: "/tmp/testing2.sh {{ param | d('') }}"
          register: out
          failed_when: out.rc > 4
    
        - debug:
            var: out.rc
    
        - assert:
            that: out.rc < 2
            fail_msg: "ERR {{ err[out.rc] }}"