Help understanding shell modules

120 views
Skip to first unread message

Stuart Reynolds

unread,
Jul 3, 2014, 6:44:37 PM7/3/14
to ansibl...@googlegroups.com
[Moved from ansible-project]


I'm trying to understand what I can expect when writing my own modules. My hope would be to more quickly move and existing Makefile and shell script configuration to ansible.

However, just testing a simple shell module:

 #!/bin/bash
set -e  # Not so useful -- return code is ignored by ansible.
echo x
=123
echo a
=789 1>&2 # Output stderr
echo y
=456
exit 1  # Ignored by ansible :-( Succeeds if we get here.

there's a lot I find confusing.

(1) Here, only x and y are returned, not a, which seems to disagree with the documentation. From: http://docs.ansible.com/developing_modules.html#common-pitfalls
Modules must not output anything on standard error, because the system will merge standard out with standard error and prevent the JSON from parsing. Capturing standard error and returning it as a variable in the JSON on standard out is fine, and is, in fact, how the command module is implemented.
If a module returns stderr or otherwise fails to produce valid JSON, the actual output will still be shown in Ansible, but the command will not succeed.

(i.e. stdout and stderr are NOT merged). In fact, if I comment out the lines to print x and y, ansible notes that there's no valid JSONy like output on stdout, reports a failure, which includes the values output to stderr. In fact if it looks like there's valid variables in stdout, it stderr is never examined (ansible succeeds even if there is garbage output on stderr, and the return code is ignored). Did I misread the docs? Or or the docs out of date?
 
(2) The following...
#!/bin/bash
set -# NB. RETURN CODE IGNORED BY ANSIBLE
echo x
=123
exit 1  # Simulate an error part way through my script
echo y
=123
succeeds! (as far as Ansible is concerned). Ansible always ignores the return codes (really?!)

So, it seems that the only safe pattern when developing shell modules is:
#!/bin/bash
set -# NB. RETURN CODE IGNORED BY ANSIBLE

{do all your work here. Exit on failure. Return code doesn't matter}

# Print result variables here on success.
# Success must be guaranteed as soon as the first variable is output
echo y=123
# failures here while printing other variables as treated as a success by Ansible!
./outputVars # Danger, danger! 
# Return code doesn'
t matter

In other words... a  lot of care is required when migrating shell modules in Ansible.
A lot would be simpler if return code failures = ansible failures.

(3) Debugging. How? The docs suggest that I'm not generally allowed to output stdout or stderr from my commands (only variables to be reported to ansible may be reported). So... I need to add  >& /dev/null to every command?
What's the best practice here for getting command failures reported to ansible?

(4)
#!/bin/bash
set -e
echo 
"x=123" 1>&2
gives:
failed: [localhost] => {"failed": true, "parsed": false}
invalid output was
: x=123

Which looks to me to be valid output (its just on stderr). Is there any way for a module to return variables in the event of a failure?


- Stu

Michael DeHaan

unread,
Jul 4, 2014, 12:55:45 PM7/4/14
to Stuart Reynolds, ansibl...@googlegroups.com
I'm not sure about all your "danger danger" stuff and "the only safe way" comments above, that seems a bit hyperbolic to me.

Basically you can output key=value pairs, one per line, or JSON.    

No, you should not output other things.   

Ansible detects failure by using "failed=True/False" in the output, whether JSON or key=value.   Yes, other attributes are also returned at the same time, as will show if you run with "-v" or use register, just like modules written in Python (or other languages) that return JSON.   

Ultimately though, bash is a terrible programming language.   Rather than writing modules in bash, an alternative if you are having problems is  just to use the 'script' module to push a remote script.   It will return return codes and everything, no modification required.









--
You received this message because you are subscribed to the Google Groups "Ansible Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-deve...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Henry Finucane

unread,
Jul 12, 2014, 3:41:26 PM7/12/14
to ansibl...@googlegroups.com
On Thursday, July 3, 2014 3:44:37 PM UTC-7, Stuart Reynolds wrote:
(4)
#!/bin/bash
set -e
echo 
"x=123" 1>&2
gives:
failed: [localhost] => {"failed": true, "parsed": false}
invalid output was
: x=123

Which looks to me to be valid output (its just on stderr). Is there any way for a module to return variables in the event of a failure?

I suspect you could do something with exit traps to consolidate failure handling: http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html

I *also* suspect that Michael is right, and you should consider giving up and writing Python modules. It's not so hard, and if you get into the habit, you can wind up with remarkably cleaner playbooks.
 
Reply all
Reply to author
Forward
0 new messages