Skip to content
This repository was archived by the owner on Feb 27, 2020. It is now read-only.

Conversation

@rkday-pro
Copy link
Contributor

@rkday-pro rkday-pro requested a review from RobDover January 18, 2018 19:14
Copy link
Contributor

@RobDover RobDover left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks solid, but I noticed a few small things to fix up.

error_returncodes = []
for command_args in list_of_command_args:
if namespace:
command_args[0:0] = ['ip', 'netns', 'exec', namespace]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like this syntax (command_args = ['ip', 'netns', 'exec', namespace] + command_args seems more readable to me) but I guess this is the existing code so I'm not super-bothered.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#maketheworldabetterplace

stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True)
processes.append(p)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about:

processes = [subprocess.Popen(command_args, 
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE,
                              close_fds=True)
             for command_args in list_of_command_args]

Then you don't need line 27 and you don't have to keep this inside the for loop.

if error_returncodes:
return error_returncodes[0]
else:
return 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much more neatly written as:

return next(error_returncodes, 0)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this doesn't work:

  File "/usr/share/clearwater/clearwater-queue-manager/env/local/lib/python2.7/site-packages/metaswitch/clearwater/etcd_shared/plugin_utils.py", line 65, in run_commands
    return next(error_returncodes, 0)
TypeError: list object is not an iterator

return next(iter(error_returncodes), 0) is a bit of a mouthful, so I'm going back to the original.

"stdout {!r}, and stderr {!r}".format(' '.join(command_args),
p.returncode,
stdout,
stderr))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to look ugly if there are multiple lines of stdout/stderr. Is it feasible to make this a bit more multi-line friendly? eg:

_log_error("Command {} failed with return code {}.\n"
          "stdout:\n{!r}"
          "stderr:\n{!r}")

I can see that this may not work if we're logging in a manner that doesn't interact well with multi-line logs though. If that's the case, how about:

_log_error("Command {} failed with return code {}."
if stdout:
    _log_error("stdout: {!r}")
if stderr:
    _log_error("stderr: {!r}")

error_returncodes.append(p.returncode)
else:
_log.debug("Command {} succeeded".format(' '.join(command_args)))
# it succeeded, log out stderr of the command run if present
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lower case here sets my teeth on edge. Can you fix it while you're here (or even just get rid of it - I don't think it's adding very much).


def run_command(command_args, namespace=None, log_error=True):
def run_commands(list_of_command_args, namespace=None, log_error=True):
"""Runs the given shell command, logging the output and return code.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring needs updating.

_log.warning("Command {} succeeded, with stderr output {!r}".
format(' '.join(command_args), stderr))
processes = []
error_returncodes = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer this to live next to the stanza of code that uses it.

return 0


def run_command(command_args, namespace=None, log_error=True):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No docstring. You're using an interesting design pattern here (the function to run a single command being a call to the one that runs multiple), so it's worth explaining why you've chosen to use it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it actually that interesting or worth commenting on? It feels like the obvious thing to me - it's just the special case of run_commands where N=1, and anything else would mean duplicated code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have thought the obvious way round to do something like this is:

  • run_command() contains the code for running a single command
  • run_commands() contains the code for iterating run_command() as necessary

There are good reasons why you chose not to do it that way, but if I were coming to the code fresh I might expect to find how a command is being run under run_command and that's not obvious here.

for p in processes:
stdout, stderr = p.communicate()
if p.returncode != 0:
# it failed, log the return code and output
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, can we tidy this up while we're here?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants