bash to shell directive
ClosedPublic

Authored by lbrabec on Jun 30 2016, 1:14 PM.

Details

Summary

This is a preliminary diff to show code we came up with @jskladan, open for discussion. It changes bash directive to shell directive, the new format is

shell:
    - command with shell=True
    - [command, with, shell=False]
    - ignorereturn:
        - command with ignored returncode
    - command which output is exported

On top of that it also introduces filters for variables in yaml:

${variable.attr|escape}
${variable.attr|noescape}
Test Plan

pytest

Diff Detail

Repository
rLTRN libtaskotron
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.
lbrabec retitled this revision from to bash to shell directive.Jun 30 2016, 1:14 PM
lbrabec updated this object.
lbrabec edited the test plan for this revision. (Show Details)
lbrabec added a reviewer: kparal.
lbrabec updated this object.Jun 30 2016, 1:43 PM
lbrabec added a subscriber: jskladan.
lbrabec updated this revision to Diff 2397.Jul 21 2016, 8:50 AM
lbrabec updated this object.
  • reflect new format in docs
  • moar tests
  • moar docs

IIUIC this is no longer a proof-of-concept, so adding the full set of reviewers.

kparal requested changes to this revision.Jul 22 2016, 3:04 PM

I like how the new directive looks like overall. I tested it with modified task-example-gzip and it seems to work well.

I'm missing any documentation regarding ${variable.attr|escape} and ${variable.attr|noescape}, both in howto guides and in shell directive docs. I also see no unit tests for it (and this functionality seems to definitely need it).

For the same of simplicity and because I'd like to have the improved bash directive before Flock, would it make sense to split this diff into two parts? The first one would only change bash/shell directive, and not touch executor or taskformula. Which means the shell directive would not escape variables by default, and we would make it clear in the directive description. Later, a second diff would be published to implement that, and we could discuss the design a properly test it without being in rush. Can that be done without too much work on your part?

libtaskotron/directives/shell_directive.py
3
  1. One would think we could have a script for that :-)
20

What about:

execute one or more shell commands or direct processes
23–24

Let's be a bit more verbose. What about this:

Execute a command in shell or as a direct process. There are no parameters for this directive,
it uses a slightly different format:

- Provide a list of commands directly under the directive name, each item is one command.
- If you use a string, it will be executed in the default shell (including all shell expansions).
- If you use a list of strings, it will be executed as a direct process (no shell expansions
  apply, which also means you don't need to worry about escaping special characters).
- If start the string with ``ignorereturn:`` key and create a list sub-section under it, all
  those commands will ignore their exit code and not raise an error.

See the example section for a better idea.

This directive is intended for basic simple scripting or running commands. If you need anything
more advanced, it's recommended to create a standalone script (bash, perl, etc) and execute it
instead.
26–29

This is probably confusing, because we don't really require any parameter (as understood by reading all other directives). I'd keep the parameters section empty and describe the behavior in detail in the description.

But it seems we'll need to fix generate_directive_docs.py to allow parameters to be empty. If that's too much work, let's keep it as it is and create a ticket (please link it here) requesting changes in generate_directive_docs.py.

34–36

I'd probably remove this, I find it unnecessary/superfluous. It should be clear how to write a task formula from our formula howtos.

60

multiple

61
``ignorereturn``
69–73

Speaking about shell=False is a bit implementation specific, what about:

You can run a process directly, without shell processing, which means you don't need to worry 
about escaping special characters or e.g. spaces in file names::
  - name: execute command directly, without shell
    shell:
        - [echo, "don't worry about code injection"]
        - [rm, a file with # funny & characters]
        - [mkdir, ${variable_with_unknown_contents}]
82

file loader is unclear to me, what about:

Shell directive can be used to copy file contents into a variable. With this, you can later use ``${loaded_file}`` in other directives::
97

Let's log this instead of printing.

98

"a list", very nitpicky, sorry. Makes it easier to read.

Also, this error is not documented.

107–112

Would this be shorter, safer and easier to read?

if isinstance(cmd, dict) and 'ignorereturn' in cmd:
    self.process(cmd['ignorereturn'], arg_data, working_data, ignorereturn=True)
    continue
119

Could we print Executing shell for shell=True and Executing process for shell=False? That will help us debug potential issues in the future.

134

Could we please get a real-time output of the subprocess? This is how:

proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1,
                        close_fds=True)
output = []
for line in iter(proc.stdout.readline, ''):
    print line,
    output.append(line)

return ''.join(output)
libtaskotron/executor.py
148–154 ↗(On Diff #2397)

This would probably deserve an explanatory comment.

testing/test_shell_directive.py
45–53

Tests like these, where you actually seem to execute system shell, should probably go to a functest_ file. I guess it is similar to working with a filesystem - it's not immediate.

66

What if there is ocho on the system? Would it be better to mock the subprocess call to simply raise the same error it would raise for a non-existent command?

This revision now requires changes to proceed.Jul 22 2016, 3:04 PM
lbrabec updated this revision to Diff 2413.Jul 25 2016, 9:09 AM
  • polishing, escaping removed
kparal accepted this revision.Jul 25 2016, 9:50 AM

Looks good, seems to work.

Can you please apply https://phab.qadevel.cloud.fedoraproject.org/differential/diff/2415/ before pushing this?

libtaskotron/directives/shell_directive.py
125

Let's at least say what this is:

log.debug('Shell directive received invalid input: %r', params)
148

This line is probably not useful anymore.

testing/test_shell_directive.py
66

I don't even

This revision is now accepted and ready to land.Jul 25 2016, 9:50 AM
Closed by commit rLTRN222a68f14628: bash to shell directive (authored by Lukas Brabec <lbrabec@redhat.com>). · Explain WhyJul 25 2016, 11:25 AM
This revision was automatically updated to reflect the committed changes.
kparal added inline comments.
libtaskotron/directives/shell_directive.py
26–29

Created T821.