Skip to content
This repository has been archived by the owner on Oct 30, 2018. It is now read-only.

unarchive does not work on initial unpacking remote archive #2936

Closed
Hubbitus opened this issue Feb 1, 2016 · 29 comments
Closed

unarchive does not work on initial unpacking remote archive #2936

Hubbitus opened this issue Feb 1, 2016 · 29 comments
Milestone

Comments

@Hubbitus
Copy link

Hubbitus commented Feb 1, 2016

Issue Type:
  • Bug Report
Ansible Version:
$ ansible --version
ansible 2.0.0.2
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
Ansible Configuration:

Fedora installation without much changes

Environment:

Fedora 23

Summary:

Remote download future which introduced in ansible 2.0 does not work when you run it for new archive.

Steps To Reproduce:

Playbook role:

- name: create jmeter directory
  file: path="{{jmeter_dir}}" state=directory

- name: Download and unpack activemq tarball
  unarchive:
    src: "http://apache-mirror.rbc.ru/pub/apache//jmeter/binaries/apache-jmeter-{{jmeter_version}}.tgz"
    copy: no
    dest: "{{jmeter_dir}}"
    creates: "{{jmeter_dir}}/bin/jmeter"

Default variables:

jmeter_version: 2.13
jmeter_dir: /opt/jmeter
Expected Results:

Tarball downloaded and content unpacked into /opt/jmeter

Actual Results:
PLAY ***************************************************************************

TASK [install-jmeter : create jmeter directory] ********************************
ok: [unidata.perf.client] => {"changed": false, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/opt/jmeter", "secontext": "unconfined_u:object_r:usr_t:s0", "size": 6, "state": "directory", "uid": 0}

TASK [install-jmeter : Download and unpack activemq tarball] *******************
fatal: [unidata.perf.client]: FAILED! => {"changed": false, "check_results": {"cmd": "/usr/bin/gtar -C \"/opt/jmeter\" --diff -zf \"apache-jmeter-2.13.tgz\"", "err": "/usr/bin/gtar: apache-jmeter-2.13/docs/api/resources: Warning: Cannot stat: No such file or directory\n/usr/bin/gtar: apache-jmeter-2.13/docs/images: Warning: Cannot stat: No such file or directory\n/usr/bin/gtar: apache-jmeter-2.13/docs/images/screenshots: Warning: Cannot stat: No such file or directory\n/usr/bin/gtar: apache-jmeter-2.13/docs/images/screenshots/assertion: Warning: Cannot stat: No such file or directory\n/usr/bin/gtar: apache-jmeter-2.13/docs/images/screenshots/changes: Warning: Cannot stat: No such file or directory\n/usr/bin/gtar: apache-jmeter-2.13/docs/images/screenshots/changes/2.10: Warning: Cannot stat: No such file or directory
....
[snip]
...
/usr/bin/gtar: apache-jmeter-2.13/printable_docs/usermanual/remote-test.html: Warning: Cannot stat: No such file or directory\n/usr/bin/gtar: apache-jmeter-2.13/printable_docs/usermanual/test_plan.html: Warning: Cannot stat: No such file or directory\n", "out": "", "rc": 1, "unarchived": false}, "dest": "/opt/jmeter", "extract_results": {"cmd": "/usr/bin/gtar -xzf \"apache-jmeter-2.13.tgz\"", "err": "tar (child): apache-jmeter-2.13.tgz: Cannot open: No such file or directory\ntar (child): Error is not recoverable: exiting now\n/usr/bin/gtar: Child returned status 2\n/usr/bin/gtar: Error is not recoverable: exiting now\n", "out": "", "rc": 2}, "failed": true, "gid": 0, "group": "root", "handler": "TgzArchive", "mode": "0755", "msg": "failed to unpack apache-jmeter-2.13.tgz to /opt/jmeter", "owner": "root", "secontext": "unconfined_u:object_r:usr_t:s0", "size": 6, "src": "apache-jmeter-2.13.tgz", "stat": {"exists": false}, "state": "directory", "uid": 0}

PLAY RECAP *********************************************************************
unidata.perf.client        : ok=1    changed=0    unreachable=0    failed=1   

There MUCH of output, which is discussed in #74

So if I try run that command manual on server (CentOS 7) got similar result:

$ /usr/bin/tar -C "/opt/jmeter" --diff -zf apache-jmeter-2.13.tgz ; echo $?
[snip]
/usr/bin/tar: apache-jmeter-2.13/printable_docs/usermanual/regular_expressions.html: Warning: Cannot stat: No such file or directory
/usr/bin/tar: apache-jmeter-2.13/printable_docs/usermanual/remote-test.html: Warning: Cannot stat: No such file or directory
/usr/bin/tar: apache-jmeter-2.13/printable_docs/usermanual/test_plan.html: Warning: Cannot stat: No such file or directory
1

If I change --diff to --extract it will be run correct:

$ /usr/bin/tar -C "/opt/jmeter" --extract -zf apache-jmeter-2.13.tgz ; echo $?
0

And it creates /opt/jmeter/apache-jmeter-2.13 directory for some reason. It also is not desired as I want see its content directly in /opt/jmeter/.
But what more interesting, all subsequent --diff invocation will run fine:

$ /usr/bin/tar -C "/opt/jmeter" --diff -zf apache-jmeter-2.13.tgz ; echo $?
0
@jimi-c jimi-c added this to the stable-2.0 milestone Feb 9, 2016
@Inphi
Copy link

Inphi commented Feb 16, 2016

I'm having the exact same problem for 2.0.0.2. The problem doesn't occur when i unarchive within the same host.

@msteinhoff
Copy link

The problem is that the unarchive module first performs a idempotency check to see if the archive is already extracted. This is done using tar --diff. Unfortunately, when the archive is not yet extracted, the tar command prints a warning for each file in the archive (Warning: Cannot stat: No such file or directory).

It looks like there is no option to make tar ignore non-existing files. A possible solution might be to redirect stderr output from the tar --diff command to /dev/null, but that might also suppress errors ansible actually should display.

@vascop
Copy link

vascop commented Mar 2, 2016

Getting this error on 2.0.1.0 as well while unarchiving on a remote host.

@msteinhoff
Copy link

Btw @Hubbitus, this problem is not limited to remote downloads. I also get error messages with local archive files, so the issue title should reflect that.

@cjbottaro
Copy link

Not sure if this is one of those anti-plus-one repos, but uhh... +1.

@mklnz
Copy link

mklnz commented Mar 12, 2016

I have also just encountered this issue on Ubuntu 14.04. This is quite strange since I've successfully run this command on many prior hosts with the same environment. Does anyone know under which conditions this command could fail?

If I separate the downloading to a separate step using get_url, then extracting locally, it seems to be fine.

@angryzor
Copy link

I think I narrowed this issue down slightly. It suddenly popped up in a previously working playbook so I did some experiments and apparently it only happens when Ansible is running in pipelining mode (which I had just recently turned on).

@mklnz
Copy link

mklnz commented Mar 14, 2016

@angryzor Ah yes, you are correct! I have also just recently turned on pipelining, and it must be that change that started to cause this failure.

@dvianello
Copy link

+1, happens only when pipelining is on!

@0xABAB
Copy link

0xABAB commented Apr 21, 2016

I am just dying to know how a "core-module" can be shipped in such a state. Have you ever heard of this thing called "automated testing"? Or do you just enjoy attention (which you get when it breaks)?

If any prospective new users of Ansible are reading this; my recommendation is to not use Ansible.

@mlebee-genymobile
Copy link

+1, happens only when pipelining is on!

@FlorentCoppint
Copy link

I can see Ansible is very well tested...
@0xABAB +1

janrotter added a commit to softwaremill/ansible-bigbluebutton that referenced this issue May 1, 2016
@abadger abadger modified the milestones: 2.1.0, stable-2.0 May 25, 2016
@abadger
Copy link
Contributor

abadger commented May 25, 2016

This has been fixed for 2.1.0, I believe with this commit: 983cdd0

[pts/30@roan /srv/ansible/troubleshooting]$ ansible-playbook 2936.yml                                     (07:39:05)
 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] ***************************************************************

TASK [setup] *******************************************************************
ok: [localhost]

TASK [create jmeter directory] *************************************************
changed: [localhost]

TASK [Download and unpack activemq tarball] ************************************
changed: [localhost]

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

[pts/30@roan /srv/ansible/troubleshooting]$ ansible-playbook --version                                    (07:41:43)
ansible-playbook 2.1.0.0 (stable-2.1 09fa05373b) last updated 2016/05/25 07:01:51 (GMT -700)
  lib/ansible/modules/core: (stable-2.1 bb8c19ba35) last updated 2016/05/24 11:37:56 (GMT -700)
  lib/ansible/modules/extras: (detached HEAD 646880bf7a) last updated 2016/05/25 07:38:02 (GMT -700)
  config file = /srv/ansible/troubleshooting/ansible.cfg
  configured module search path = Default w/o overrides

@Hubbitus I also saw your separate remark: "And it creates /opt/jmeter/apache-jmeter-2.13 directory for some reason. It also is not desired as I want see its content directly in /opt/jmeter/." Unfortunately, that's out of scope for the unarchive module. apache-jmeter-2.13 is part of the directory structure inside of the tarball. tar does not have a way to rewrite the directory structure from inside the tarball on the fly so unarchive doesn't either.

Closing as fixed in 2.1.0.

@abadger abadger closed this as completed May 25, 2016
@dagwieers
Copy link
Contributor

@Hubbitus If we implement a native/pure-python unarchive, we could easily add an option to strip paths from the archive which would work for all archive types. Stay tuned...

@Hubbitus
Copy link
Author

Hubbitus commented Jun 6, 2016

@abadger thank you for the fixing.

@dagwieers could you please post links to documentation on that new plugin and that particular option?

@dagwieers
Copy link
Contributor

@Hubbitus Cannot post links to something that does not exist yet.

@ShortyFR
Copy link

I checked the commit, and it adds a regex to check for missing files :

MISSING_FILE_RE = re.compile(r': Warning: Cannot stat: No such file or directory$')

Which means it only works if the remote server is running in English... No such luck here, my remote server is in French, so the message is : "Aucun fichier ou dossier de ce type".

You shouldn't rely on English messages in modules.

@dagwieers
Copy link
Contributor

@ShortyFR What commit did you check ? It helps to be explicit in your communication.

We have no other option than to parse the output from gtar (unless we reimplement using tarfile). The module forces LANG=C nowadays, so that should not be a concern.

@ShortyFR
Copy link

I checked the commit mentioned in the fix message : 983cdd0 (AFAIK it's the only commit mentioned here).

The locale does indeed seem to be a concern since I actually have errors like this on Ansible 2.1 :

fatal: [myserver]: FAILED! => {"changed": false, "failed": true, "msg": "Unexpected error when accessing exploded file: [Errno 2] Aucun fichier ou dossier de ce type: '/usr/local/java/jdk1.8.0_60'", "stat": {"exists": false}}

@dagwieers
Copy link
Contributor

If you look in the devel branch, you will see that it is already fixed. We are forcing LANG=C.

@kundeng
Copy link

kundeng commented Jun 14, 2016

I can't update to 2.2 because ansible comes with SLEP 11 and I have no way to install from source due to a number of errors.

My question is how to patch 2.0.2.0 with the bug-fixed unarchive.

@dagwieers
Copy link
Contributor

It's not impossible, but you have to start from the unarchive module from the devel branch. Then remove all the newer functionality in the module API. E.g. the parameter typing, diff-mode, etc...

Unfortunately, because of the various changes in the interface, we cannot take newer modules and use them as-is on older Ansible releases. (This used to work, but not lately)

@n0ts
Copy link

n0ts commented Jun 23, 2016

I had a same issue. I think that .tgz extension is not supported 2.1.0.0?

@gustavomcarmo
Copy link

The task is:

unarchive: src={{pkgrepo}}/apache/maven/apache-maven-{{mvnver}}-bin.tar.gz dest=/usr/local creates=/usr/local/apache-maven-{{mvnver}} copy=no

And the error I was facing is:

path /usr/local/apache-maven-3.3.9/boot/plexus-classworlds-2.5.2.jar does not exist

But it worked when the LANG=C was defined in the command:

LANG=C ansible-playbook playbook.yml

On Ansible 2.1.0.0.

@gustavomcarmo
Copy link

It also works when were defined the following environment variables, inside the playbook:

unarchive: src={{pkgrepo}}/apache/maven/apache-maven-{{mvnver}}-bin.tar.gz dest=/usr/local creates=/usr/local/apache-maven-{{mvnver}} copy=no
environment:
  LANG: "C"
  LC_ALL: "C"
  LC_MESSAGES: "C"

@daan944
Copy link

daan944 commented Jul 13, 2016

This fix is the dirtiest of them all.

As ShortyFR said: "You shouldn't rely on English messages in modules."

@dagwieers
Copy link
Contributor

Guys, read the various reports instead of stating the obvious (over and
again). We need a pure-python module, no discussion about that. But what
would you prefer in the meantime ? That we stop working on this module
altogether, or that we remove this module until someone contributes a
pure-python drop-in replacement ?

Using gtar is a real pain, but it's what we currently have. I wish it was
different.
On 13 Jul 2016 14:55, "daan944" notifications@github.com wrote:

This fix is the dirtiest of them all.

As ShortyFR said: "You shouldn't rely on English messages in modules."


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#2936 (comment),
or mute the thread
https://github.com/notifications/unsubscribe/AAXsZj-YXzcd_TMShQFrsKnQGI7W98kVks5qVOBAgaJpZM4HQtIo
.

@sean-abbott
Copy link

sean-abbott commented Nov 22, 2016

This is still an issue for me with 2.2.0.0.

- name: "unpack solr tarball"
  become: yes
  unarchive:
    remote_src: no
    src: "{{ solr_repo_path }}/tmp/{{ solr_tarball }}"
    dest: "{{ release_path }}"

yields:
fatal: []: FAILED! => {"changed": false, "dest": "/srv/releases/production/20161122150414", "extract_results": {"cmd": ["/bin/tar", "--extract", "-C", "/srv/releases/production/20161122150414", "-z", "-f", "/home/sabbott/.ansible/tmp/ansible-tmp-1479845067.86-172919774756549/source"], "err": "/bin/tar: ./assets/conf/admin-extra.menu-bottom.html: Cannot utime: No such file or directory\n/bin/tar: ./assets/conf/admin-extra.menu-bottom.html: Cannot change ownership to uid 1000, gid 1000: No such file or directory\n/bin/tar: ./assets/conf/admin-extra.menu-bottom.html: Cannot change mode to rw-rw-r--: No such file or directory\n/bin/tar: Exiting with failure status due to previous errors\n", "out": "", "rc": 2}, "failed": true, "gid": 5001, "group": "release", "handler": "TgzArchive", "mode": "0755", "msg": "failed to unpack /home/sabbott/.ansible/tmp/ansible-tmp-1479845067.86-172919774756549/source to /srv/releases/production/20161122150414", "owner": "root", "size": 7, "src": "/home/sabbott/.ansible/tmp/ansible-tmp-1479845067.86-172919774756549/source", "state": "directory", "uid": 0}

Tried the lang and pipeline workaround above to no avail. Re-running on failed hosts until they all worked worked. (50% of hosts, each run, would work)

@stevekuznetsov
Copy link

stevekuznetsov commented Feb 1, 2017

Still broken on 2.2.1.0

$ ansible --version
ansible 2.2.1.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides

no extra options -- works

---
- hosts: localhost
  connection: local
  become: no
  gather_facts: no

  tasks:
    - name: install protoc to allow for protobuf generation
      unarchive:
        src: https://github.com/google/protobuf/releases/download/v3.0.2/protoc-3.0.2-linux-x86_64.zip
        copy: no
        dest: /tmp/test
$ ansible-playbook test.yml -vvv
Using /etc/ansible/ansible.cfg as config file
 [WARNING]: provided hosts list is empty, only localhost is available


PLAYBOOK: test.yml *************************************************************
1 plays in test.yml

PLAY [localhost] ***************************************************************

TASK [install protoc to allow for protobuf generation] *************************
changed: [localhost] => {
    "changed": true, 
    "dest": "/tmp/test", 
    "diff": {
        "prepared": ".f..t....?? include/google/protobuf/struct.proto\n.f..t....?? include/google/protobuf/type.proto\n.f..t....?? include/google/protobuf/descriptor.proto\n.f..t....?? include/google/protobuf/api.proto\n.f..t....?? include/google/protobuf/empty.proto\n.f..t....?? include/google/protobuf/compiler/plugin.proto\n.f..t....?? include/google/protobuf/any.proto\n.f..t....?? include/google/protobuf/field_mask.proto\n.f..t....?? include/google/protobuf/wrappers.proto\n.f..t....?? include/google/protobuf/timestamp.proto\n.f..t....?? include/google/protobuf/duration.proto\n.f..t....?? include/google/protobuf/source_context.proto\n.f..t....?? bin/protoc\n.f..t....?? readme.txt\n"
    }, 
    "extract_results": {
        "cmd": [
            "/usr/bin/unzip", 
            "-o", 
            "/tmp/ansible_OGOTrH/protoc-3.0.2-linux-x86_64.zip", 
            "-d", 
            "/tmp/test"
        ], 
        "err": "", 
        "out": "Archive:  /tmp/ansible_OGOTrH/protoc-3.0.2-linux-x86_64.zip\n  inflating: /tmp/test/include/google/protobuf/struct.proto  \n  inflating: /tmp/test/include/google/protobuf/type.proto  \n  inflating: /tmp/test/include/google/protobuf/descriptor.proto  \n  inflating: /tmp/test/include/google/protobuf/api.proto  \n  inflating: /tmp/test/include/google/protobuf/empty.proto  \n  inflating: /tmp/test/include/google/protobuf/compiler/plugin.proto  \n  inflating: /tmp/test/include/google/protobuf/any.proto  \n  inflating: /tmp/test/include/google/protobuf/field_mask.proto  \n  inflating: /tmp/test/include/google/protobuf/wrappers.proto  \n  inflating: /tmp/test/include/google/protobuf/timestamp.proto  \n  inflating: /tmp/test/include/google/protobuf/duration.proto  \n  inflating: /tmp/test/include/google/protobuf/source_context.proto  \n  inflating: /tmp/test/bin/protoc    \n  inflating: /tmp/test/readme.txt    \n", 
        "rc": 0
    }, 
    "gid": 1001, 
    "group": "skuznets", 
    "handler": "ZipArchive", 
    "invocation": {
        "module_args": {
            "backup": null, 
            "content": null, 
            "copy": false, 
            "creates": "/tmp/test/protoc", 
            "delimiter": null, 
            "dest": "/tmp/test", 
            "directory_mode": null, 
            "exclude": [], 
            "extra_opts": [], 
            "follow": false, 
            "force": null, 
            "group": null, 
            "keep_newer": false, 
            "list_files": false, 
            "mode": null, 
            "original_basename": "protoc-3.0.2-linux-x86_64.zip", 
            "owner": null, 
            "regexp": null, 
            "remote_src": false, 
            "selevel": null, 
            "serole": null, 
            "setype": null, 
            "seuser": null, 
            "src": "https://github.com/google/protobuf/releases/download/v3.0.2/protoc-3.0.2-linux-x86_64.zip", 
            "unsafe_writes": null, 
            "validate_certs": true
        }
    }, 
    "mode": "0775", 
    "owner": "skuznets", 
    "secontext": "unconfined_u:object_r:user_tmp_t:s0", 
    "size": 100, 
    "src": "/tmp/ansible_OGOTrH/protoc-3.0.2-linux-x86_64.zip", 
    "state": "directory", 
    "uid": 1000
}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0  

$ tree /tmp/test
test
├── bin
│   └── protoc
├── include
│   └── google
│       └── protobuf
│           ├── any.proto
│           ├── api.proto
│           ├── compiler
│           │   └── plugin.proto
│           ├── descriptor.proto
│           ├── duration.proto
│           ├── empty.proto
│           ├── field_mask.proto
│           ├── source_context.proto
│           ├── struct.proto
│           ├── timestamp.proto
│           ├── type.proto
│           └── wrappers.proto
└── readme.txt

5 directories, 14 files

extra options -- fails and also writes to the filesystem!

---
- hosts: localhost
  connection: local
  become: no
  gather_facts: no

  tasks:
    - name: install protoc to allow for protobuf generation
      unarchive:
        src: https://github.com/google/protobuf/releases/download/v3.0.2/protoc-3.0.2-linux-x86_64.zip
        copy: no
        extra_opts: ['bin/protoc']
        dest: /tmp/test
$ ansible-playbook test.yml -vvv
Using /etc/ansible/ansible.cfg as config file
 [WARNING]: provided hosts list is empty, only localhost is available


PLAYBOOK: test.yml *************************************************************
1 plays in test.yml

PLAY [localhost] ***************************************************************

TASK [install protoc to allow for protobuf generation] *************************
fatal: [localhost]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "invocation": {
        "module_args": {
            "backup": null, 
            "content": null, 
            "copy": false, 
            "creates": "/tmp/test/protoc", 
            "delimiter": null, 
            "dest": "/tmp/test", 
            "directory_mode": null, 
            "exclude": [], 
            "extra_opts": [
                "bin/protoc"
            ], 
            "follow": false, 
            "force": null, 
            "group": null, 
            "keep_newer": false, 
            "list_files": false, 
            "mode": null, 
            "original_basename": "protoc-3.0.2-linux-x86_64.zip", 
            "owner": null, 
            "regexp": null, 
            "remote_src": false, 
            "selevel": null, 
            "serole": null, 
            "setype": null, 
            "seuser": null, 
            "src": "https://github.com/google/protobuf/releases/download/v3.0.2/protoc-3.0.2-linux-x86_64.zip", 
            "unsafe_writes": null, 
            "validate_certs": true
        }
    }, 
    "msg": "path /tmp/test/include/ does not exist", 
    "path": "/tmp/test/include/", 
    "state": "absent"
}
	to retry, use: --limit @/tmp/test.retry

PLAY RECAP *********************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1   

$ tree /tmp/test
/tmp/test
└── bin
    └── protoc

1 directory, 1 file

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests