- name: capture timestamp before fire and forget set_fact: start_timestamp: "{{ lookup('pipe', 'date +%s') }}" - name: async fire and forget async_test: sleep_delay_sec: 8 async: 20 poll: 0 register: asyncresult - name: validate response assert: that: - asyncresult.ansible_job_id is match('\d+\.\d+') - asyncresult.started == 1 - asyncresult.finished == 0 - asyncresult.results_file is search('\.ansible_async.+\d+\.\d+') # ensure that async is actually async- this test will fail if # hosts > forks or if the target host is VERY slow - (lookup('pipe', 'date +%s') | int) - (start_timestamp | int) < 8 - name: async poll immediate success async_test: sleep_delay_sec: 0 async: 10 poll: 1 register: asyncresult - name: validate response assert: that: - asyncresult.ansible_job_id is match('\d+\.\d+') - asyncresult.finished == 1 - asyncresult.changed == true - asyncresult.ansible_async_watchdog_pid is number # - asyncresult.module_tempdir is search('ansible-tmp-') - asyncresult.module_pid is number # this part of the test is flaky- Windows PIDs are reused aggressively, so this occasionally fails due to a new process with the same ID # FUTURE: consider having the test module hook to a kernel object we can poke at that gets signaled/released on exit #- name: ensure that watchdog and module procs have exited # raw: Get-Process | Where { $_.Id -in ({{ asyncresult.ansible_async_watchdog_pid }}, {{ asyncresult.module_pid }}) } # register: proclist # #- name: validate no running watchdog/module processes were returned # assert: # that: # - proclist.stdout.strip() == '' #- name: ensure that module_tempdir was deleted # raw: Test-Path {{ asyncresult.module_tempdir }} # register: tempdircheck # #- name: validate tempdir response # assert: # that: # - tempdircheck.stdout | search('False') - name: async poll retry async_test: sleep_delay_sec: 5 async: 10 poll: 1 register: asyncresult - name: validate response assert: that: - asyncresult.ansible_job_id is match('\d+\.\d+') - asyncresult.finished == 1 - asyncresult.changed == true # - asyncresult.module_tempdir is search('ansible-tmp-') - asyncresult.module_pid is number # this part of the test is flaky- Windows PIDs are reused aggressively, so this occasionally fails due to a new process with the same ID # FUTURE: consider having the test module hook to a kernel object we can poke at that gets signaled/released on exit #- name: ensure that watchdog and module procs have exited # raw: Get-Process | Where { $_.Id -in ({{ asyncresult.ansible_async_watchdog_pid }}, {{ asyncresult.module_pid }}) } # register: proclist # #- name: validate no running watchdog/module processes were returned # assert: # that: # - proclist.stdout.strip() == '' #- name: ensure that module_tempdir was deleted # raw: Test-Path {{ asyncresult.module_tempdir }} # register: tempdircheck # #- name: validate tempdir response # assert: # that: # - tempdircheck.stdout | search('False') - name: async poll timeout async_test: sleep_delay_sec: 5 async: 3 poll: 1 register: asyncresult ignore_errors: true - name: validate response assert: that: - asyncresult.ansible_job_id is match('\d+\.\d+') - asyncresult.finished == 1 - asyncresult.changed == false - asyncresult | failed == true - asyncresult.msg is search('timed out') - name: async poll graceful module failure async_test: fail_mode: graceful async: 5 poll: 1 register: asyncresult ignore_errors: true - name: validate response assert: that: - asyncresult.ansible_job_id is match('\d+\.\d+') - asyncresult.finished == 1 - asyncresult.changed == true - asyncresult | failed == true - asyncresult.msg == 'failed gracefully' - name: async poll exception module failure async_test: fail_mode: exception async: 5 poll: 1 register: asyncresult ignore_errors: true - name: validate response assert: that: - asyncresult.ansible_job_id is match('\d+\.\d+') - asyncresult.finished == 1 - asyncresult.changed == false - asyncresult | failed == true # TODO: re-enable after catastrophic failure behavior is cleaned up # - asyncresult.msg is search('failing via exception') # FUTURE: figure out why the last iteration of this test often fails on shippable #- name: loop async success # async_test: # sleep_delay_sec: 3 # async: 10 # poll: 0 # with_sequence: start=1 end=4 # register: async_many # #- name: wait for completion # async_status: # jid: "{{ item }}" # register: asyncout # until: asyncout.finished == 1 # retries: 10 # delay: 1 # with_items: "{{ async_many.results | map(attribute='ansible_job_id') | list }}" # #- name: validate results # assert: # that: # - item.finished == 1 # - item.slept_sec == 3 # - item.changed == true # - item.ansible_job_id is match('\d+\.\d+') # with_items: "{{ asyncout.results }}" # this part of the test is flaky- Windows PIDs are reused aggressively, so this occasionally fails due to a new process with the same ID # FUTURE: consider having the test module hook to a kernel object we can poke at that gets signaled/released on exit #- name: ensure that all watchdog and module procs have exited # raw: Get-Process | Where { $_.Id -in ({{ asyncout.results | join(',', attribute='ansible_async_watchdog_pid') }}, {{ asyncout.results | join(',', attribute='module_pid') }}) } # register: proclist # #- name: validate no processes were returned # assert: # that: # - proclist.stdout.strip() == "" # FUTURE: test junk before/after JSON # FUTURE: verify tempdir stays through module exec # FUTURE: verify tempdir is deleted after module exec # FUTURE: verify tempdir is permanent with ANSIBLE_KEEP_REMOTE_FILES=1 (how?) # FUTURE: verify binary modules work # FUTURE: test status/return # FUTURE: test status/cleanup # FUTURE: test reboot/connection failure # FUTURE: figure out how to ensure that processes and tempdirs are cleaned up in all exceptional cases