Friday, November 12, 2021

[SOLVED] Ansible - Unarchive specifics files from war archive

Issue

I would like to extract two files from directory from war archive.

The two files exists in /WEB-INF/classes/

I have tried:

- name: create application.properties in /tmp
  unarchive:
    src: "/application/webapps/application.war"
    dest: "/tmp"
    remote_src: yes
    extra_opts:
      - -j
      - WEB-INF/classes/application.properties
      - WEB-INF/classes/logback.xml

Error :

"err":"unzip: cannot find or open /WEB-INF/classes/application.properties"

But it doesn't work of course. Any idea?


Solution

Really it looks like Ansible unarchive module is not meant of this specific use case.
And there is a feature request to actually implement this exact feature.

Long story short: extra_opts is meant for options, but does not seems be be meant for extra parameters.

What you are looking to do seems like it was possible before this commit, indeed, before this commit, Ansible would do:

cmd = [ self.cmd_path, '-o', self.src ]
if self.opts:
    cmd.extend(self.opts)

And so you will end with the command:

unzip -o /application/webapps/application.war -j WEB-INF/classes/application.properties WEB-INF/classes/logback.xml

That should have worked in your use case.
But after this commit, Ansible's code ends up being

cmd = [ self.cmd_path, '-o' ]
if self.opts:
    cmd.extend(self.opts)
cmd.append(self.src)

And so, it generates this buggy unzip command:

unzip -o -j WEB-INF/classes/application.properties WEB-INF/classes/logback.xml /application/webapps/application.war

Now you could work around this bug, but this will make unzip error, and we will have to sweep this error under the rug, so this should not be the solution of your choice.

But here is the train of thought I went in and what I did to have a working playbook: I identified the issue with the command using ansible-playbook play.yml -vvvv, that got me the full unzip command issued by Ansible — there error below have been reduced for brevity:

fatal: [localhost]: FAILED! => {
    "changed": false,
    "dest": "/tmp/out",
    "extract_results": {
        "cmd": [
            "/usr/bin/unzip",
            "-o",
            "-j",
            "WEB-INF/classes/application.properties",
            "WEB-INF/classes/logback.xml",
            "test.war",
            "-d",
            "/tmp/out"
        ]
    }
}

Based on this, I tricked the extra_opts by reproducing the archive in the src parameter, so it fits right after the option -j in the command.

The drawback of this is that is is now generating this command:

unzip -o -j test.war WEB-INF/classes/application.properties WEB-INF/classes/logback.xml test.war

Which means that it is now trying to find the file test.war inside itself, and so it will error because of this.

Here is the errors this yields:

"err": "caution: filename not matched: test.war\n"

So once again, I used a trick with the help of failed_when to ignore this error, knowing the error it would yield me.

So given:

$ tree ./WEB-INF
./WEB-INF
└── classes
    ├── application.properties
    ├── ignore-me.txt
    └── logback.xml

1 directory, 3 files

$ tree /tmp
/tmp

0 directories, 0 files

And the playbook:

- hosts: all
  gather_facts: no
      
  tasks:
    - shell: /usr/lib/jvm/java-1.8-openjdk/bin/jar -cf test.war *

    - file:
        path: /tmp/out
        state: directory

    - unarchive: 
        src: test.war
        dest: /tmp/out
        remote_src: yes
        extra_opts:
          - -j
          - test.war
          - WEB-INF/classes/application.properties
          - WEB-INF/classes/logback.xml
      register: extract
      failed_when: "extract.extract_results.rc != 0 and extract.extract_results.err != 'caution: filename not matched:  ' ~ extract.src ~ '\n'"

This yields the recap:

PLAY [all] **********************************************************************************************************************************************

TASK [Create test.war file] *****************************************************************************************************************************
changed: [localhost]

TASK [file] *********************************************************************************************************************************************
changed: [localhost]

TASK [Create test.war file] *****************************************************************************************************************************
ok: [localhost]

PLAY RECAP **********************************************************************************************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

And those files:


/ansible # tree /tmp/out
/tmp/out
├── application.properties
└── logback.xml

0 directories, 2 files


Answered By - β.εηοιτ.βε