I want to download a file from a set of URLs in a loop, stopping after the first URL that actually has the file. Here’s a sample playbook illustrating the problem:
---
- hosts: localhost
gather_facts: false
tasks:
- name: try to get a file
uri:
url: "{{ item }}"
dest: "/tmp/data.dat"
mode: "0644"
register: results
ignore_errors: true
loop:
- "http://localhost/readme2.txt"
- "http://localhost/readme1.txt"
- "http://localhost/readme.txt"
loop_control:
extended: true
until:
- results is defined
- results.results[ansible_loop.index0].status == 200 # <--- This does not work!
- name: print the results
debug:
var: results
Of course, the real URLs are different, and the file to be downloaded is pretty big, which is why I would like to avoid doing it N times, but do it only once. With the URLs in the example above, the resulting status codes should be 200, 404, and 200, respectively, so the loop should stop after one iteration. But there’s no guarantee that the first URL will yield a 200, it just so happens to be the case in this test scenario.
I’m struggling with getting the conditions right:
If I leave off the ignore_errors, the playbook stops after the loop, and I can’t do any further processing. Also, it still insists on iterating through the list of URLs. If I leave the second condition off, the result looks like this:
ok: [localhost] => {
"results": {
"changed": true,
"failed": true,
"msg": "One or more items failed",
"results": [
{
"accept_ranges": "bytes",
"ansible_loop": {
"allitems": [
"http://localhost/readme2.txt",
"http://localhost/readme1.txt",
"http://localhost/readme.txt"
],
"first": true,
"index": 1,
"index0": 0,
"last": false,
"length": 3,
"nextitem": "http://localhost/readme1.txt",
"revindex": 3,
"revindex0": 2
},
"ansible_loop_var": "item",
...
"invocation": {
"module_args": {
"dest": "/tmp/data.dat",
"method": "GET",
"path": "/tmp/data.dat",
"status_code": [
200
],
"url": "http://localhost/readme2.txt",
}
},
"item": "http://localhost/readme2.txt",
"last_modified": "Mon, 12 Mar 2018 13:58:09 GMT",
"mode": "0644",
"msg": "OK (105 bytes)",
"owner": "toni",
"path": "/tmp/data.dat",
"redirected": false,
"server": "nginx",
"size": 105,
"state": "file",
"status": 200,
"uid": 1000,
"url": "http://localhost/readme2.txt"
},
{
"ansible_loop": {
"allitems": [
"http://localhost/readme2.txt",
"http://localhost/readme1.txt",
"http://localhost/readme.txt"
],
"first": false,
"index": 2,
"index0": 1,
"last": false,
"length": 3,
"nextitem": "http://localhost/readme.txt",
"previtem": "http://localhost/readme2.txt",
"revindex": 2,
"revindex0": 1
},
"ansible_loop_var": "item",
"invocation": {
"module_args": {
"method": "GET",
"status_code": [
200
],
"url": "http://localhost/readme1.txt",
}
},
"item": "http://localhost/readme1.txt",
"msg": "Status code was 404 and not [200]: HTTP Error 404: Not Found",
"status": 404,
},
{
"accept_ranges": "bytes",
"ansible_loop": {
"allitems": [
"http://localhost/readme2.txt",
"http://localhost/readme1.txt",
"http://localhost/readme.txt"
],
"first": false,
"index": 3,
"index0": 2,
"last": true,
"length": 3,
"previtem": "http://localhost/readme1.txt",
"revindex": 1,
"revindex0": 0
},
"ansible_loop_var": "item",
"changed": true,
"invocation": {
"module_args": {
"status_code": [
200
],
"url": "http://localhost/readme.txt",
...
}
},
"status": 200,
"url": "http://localhost/readme.txt"
}
],
"skipped": false
}
}
If I leave the second condition in (results.results[ansible_loop.index0].status == 200), I get an error message that I don’t understand:
"msg": "Task failed: Error while evaluating conditional: object of type 'dict' has no attribute 'results'"
Is there a way to actually print the state of the variable while in the loop? Because when I redirect the output to file and then process that with jq, I get (eg.):
$ jq '.results.results[1].status' < ansible.json
404
I’m running Ansible 2.20.1 from a virtualenv on a Debian 13 machine.
What am I doing wrong, please?