WhatAmIDoing
WhatAmIDoing

Reputation: 220

Ansible compare zip listing to directory

EDIT: I resolved this a different way by getting a checksum of the 7z contents and checking

a) if the directory existed b) if it did - did it's contents checksum match


I have an ansible playbook which uses a 7zip shell command, but I want to check if the 7z has been inflated already so I have the following

- name: Get zip listing
  shell: '7z l {{ sz_file }} | tail -n +21 | head -n -2 | cut -c 54-'
  register: sz_contents

- name: Compare zip listing to file contents
  stat:
    path: '{{ extract_dir }}/{{ item }}'
    register: result
  with_items: '{{ sz_contents.stdout_lines }}'

- name: Inflate 7z file if needed
  shell: 7z x {{ sz_file }}
  when: ???

I want the following to happen:

  1. Stop the Compare task the first time results.stat.exists == False (the 7z has many files and continuing the comparison after that is pointless)
  2. Register if the file needs inflating and do so as needed

Upvotes: 0

Views: 1173

Answers (1)

larsks
larsks

Reputation: 311750

It sounds like you want to make the extract task conditional on whether the compare tasks succeeds or fails, and you want the compare task to fail as soon as it finds a file that doesn't exist.

We can get most of the way there.

Normally, the stat module doesn't trigger a failure when you point it at a path that doesn't exist. For example, the following playbook:

- hosts: localhost
  gather_facts: false
  tasks:
    - stat:
        path: /does-not-exist
      register: result

    - debug:
        var: result

Yields:

TASK [stat] ***********************************************************************************
ok: [localhost]

TASK [debug] **********************************************************************************
ok: [localhost] => {
    "result": {
        "changed": false,
        "failed": false,
        "stat": {
            "exists": false
        }
    }
}

Ansible provides us with the failed_when directive to control when a task fails. This means we can rewrite your compare task to fail on a missing file like this:

- name: Compare zip listing to file contents
  stat:
    path: '{{ extract_dir }}/{{ item }}'
  register: result
  failed_when: not result.stat.exists
  ignore_errors: true
  with_items: '{{ sz_contents.stdout_lines }}'

The failed_when directive tells Ansible to consider the task "failed" if the file passed to stat doesn't exist, and the ignore_errors directive tells Ansible to continue executing the playbook rather than aborting when the task fails.

We can make the extract task condition on this one with a simple when directive:

- name: Inflate 7z file if needed
  shell: 7z x {{ sz_file }}
  when: result is failed

The only problem with this solution is that Ansible won't exit a loop when an individual item causes a failure, so it's going to check through all of sz_contents.stdout_lines regardless.

Update

I was discussing this issue on irc and @bcoca pointed out the when is evaluated before register, so we can actually get the behavior you want by writing the compare task like this:

- name: Compare zip listing to file contents
  stat:
    path: '{{ extract_dir }}/{{ item }}'
  register: result
  when: result is defined or result is success
  failed_when: not result.stat.exists
  ignore_errors: true
  with_items: '{{ sz_contents.stdout_lines }}'

The when statement will cause all loop iterations after the first failure to be skipped.

Upvotes: 1

Related Questions