5

Earlier today, we experienced a nasty issue that was caused by the following shell pipe:

- name: get remote branches
  shell: git ls-remote -h git@bitbucket.org:orga/repo.git | sed 's_.*refs/heads/__g'
  register: branches_remote

The git command fails, but the return code of the entire pipe is 0. This is default bash/sh behavior.

To fix this, in sh/bash, you can set -o pipefail or set -e. Is it possible to do that in ansible, preferably globally for all my shell commands?

Community
  • 1
  • 1
Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249
  • does ansible's [git module](http://docs.ansible.com/ansible/git_module.html) not work for you? – chicks Oct 23 '15 at 16:57
  • We need to extract a remote branch name, manipulate it a little bit, and store it in a variable. If this can be done without `shell`, that'd be great of course. – Nico Schlömer Oct 23 '15 at 17:22
  • 5
    `set -e` is not a substitute for `set -o pipefail` (contrary to what the "or" in the question implies); if you want your script to pass through the first failed exit status from any failed pipeline component without proceeding to later commands, you need *both*. – Charles Duffy Oct 23 '15 at 21:44
  • 5
    ...that said, `shell:` uses `/bin/sh` by default, for which `set -o pipefail` isn't guaranteed to be available at all. – Charles Duffy Oct 23 '15 at 21:45
  • You *could* implement this as a local patch to `library/commands/command`, but... eww. – Charles Duffy Oct 23 '15 at 21:54

3 Answers3

4

In general you should try to use the shell commands as a last resort as they tend to be a bit brittle. If you need to use the shell module with any shell options, simply submit it as part of your command pipeline as shown below. The executable parameter forces the use of bash shell.

[user@ansible ~]$ ansible myhost -m shell -a "executable=/bin/bash set -o pipefail && false | echo hello there"
myhost | FAILED | rc=1 >>
hello there

[user@ansible ~]$ ansible myhost -m shell -a "executable=/bin/bash set -o pipefail && true | echo hello there"
myhost | success | rc=0 >>
hello there
Dave Snigier
  • 2,574
  • 3
  • 21
  • 29
2

Bash accepts set -o pipefail, but the default executable is /bin/sh which, on platforms such as Debian, is not guaranteed to support this, probably for a good reason (dash is a pure-posix shell).

You can configure /bin/bash as the executable in ansible.cfg:

[defaults]
executable = /bin/bash

The same can be done in molecule.yml


provisioner:
  name: ansible
  config_options:
    defaults:
      executable: /bin/bash

There is no configuration executable_flags available, so you should adjust the actions yourself:

- name: pipes that fail should fail the action
  shell: |
    set -e -o pipefail
    git ls-remote -h git@bitbucket.org:orga/repo.git | sed 's_.*refs/heads/__g'
   register: branches_remote
bbaassssiiee
  • 6,013
  • 2
  • 42
  • 55
1

You can set the executable for the shell module, for example:

- name: get remote branches
  shell: |
    set -e -o pipefail
    git ls-remote -h git@bitbucket.org:orga/repo.git | sed 's_.*refs/heads/__g'
   args:
     executable: /usr/bin/bash
   register: branches_remote