diff --git a/CHANGELOG.md b/CHANGELOG.md index bdfff161..af331fc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The client here will eventually be released as "spython" (and eventually to singularity on pypi), and the versions here will coincide with these releases. ## [master](https://github.com/singularityhub/singularity-cli/tree/master) + - add sudo_options option to spython.main.Client.build (0.0.73) - list of options and writable added to shell, execute, and run (0.0.72) - client is not honoring quiet for pull (0.0.71) - removing debugging line in pull (0.0.70) diff --git a/spython/main/base/command.py b/spython/main/base/command.py index b367b6b3..451c4136 100644 --- a/spython/main/base/command.py +++ b/spython/main/base/command.py @@ -106,7 +106,8 @@ def run_command(self, cmd, sudo=False, capture=True, quiet=None, - return_result=False): + return_result=False, + sudo_options=None): '''run_command is a wrapper for the global run_command, checking first for sudo and exiting on error if needed. The message is returned as @@ -119,6 +120,7 @@ def run_command(self, cmd, sudo: does the command require sudo? quiet: if quiet set by function, overrides client setting. return_result: return the result, if not successful (default False). + sudo_options: string or list of strings that will be passed as options to sudo On success, returns result. ''' @@ -126,7 +128,7 @@ def run_command(self, cmd, if quiet is None: quiet = self.quiet - result = run_cmd(cmd, sudo=sudo, capture=capture, quiet=quiet) + result = run_cmd(cmd, sudo=sudo, capture=capture, quiet=quiet, sudo_options=sudo_options) # If one line is returned, squash dimension if len(result['message']) == 1: diff --git a/spython/main/build.py b/spython/main/build.py index 877ea96b..b3fd0a71 100644 --- a/spython/main/build.py +++ b/spython/main/build.py @@ -24,7 +24,8 @@ def build(self, recipe=None, force=False, options=None, quiet=False, - return_result=False): + return_result=False, + sudo_options=None): '''build a singularity image, optionally for an isolated build (requires sudo). If you specify to stream, expect the image name @@ -47,6 +48,7 @@ def build(self, recipe=None, name (with "image") then a fun robot name will be generated instead. Highly recommended :) sudo: give sudo to the command (or not) default is True for build + sudo_options: options to pass to sudo (e.g. --preserve-env=SINGULARITY_CACHEDIR,SINGULARITY_TMPDIR) options: for all other options, specify them in this list. quiet: quiet verbose printing from the client. return_result: if True, return complete error code / message dictionary @@ -105,6 +107,7 @@ def build(self, recipe=None, if not stream: self._run_command(cmd, sudo=sudo, + sudo_options=sudo_options, quiet=quiet, return_result=return_result, capture=False) @@ -112,7 +115,7 @@ def build(self, recipe=None, else: # Here we return the expected image, and an iterator! # The caller must iterate over - return image, stream_command(cmd, sudo=sudo) + return image, stream_command(cmd, sudo=sudo, sudo_options=sudo_options) if os.path.exists(image): return image diff --git a/spython/utils/terminal.py b/spython/utils/terminal.py index 40a4b19f..c76a960e 100644 --- a/spython/utils/terminal.py +++ b/spython/utils/terminal.py @@ -15,11 +15,21 @@ from spython.logger import decodeUtf8String import subprocess import sys +import shlex ################################################################################ # Local commands and requests ################################################################################ +def _process_sudo_cmd(cmd, sudo, sudo_options): + if sudo and sudo_options is not None: + if isinstance(sudo_options, str): + sudo_options = shlex.split(sudo_options) + cmd = ['sudo'] + sudo_options + cmd + elif sudo: + cmd = ['sudo'] + cmd + return cmd + def check_install(software='singularity', quiet=True): '''check_install will attempt to run the singularity command, and @@ -89,7 +99,7 @@ def get_installdir(): return os.path.abspath(os.path.dirname(os.path.dirname(__file__))) -def stream_command(cmd, no_newline_regexp="Progess", sudo=False): +def stream_command(cmd, no_newline_regexp="Progess", sudo=False, sudo_options=None): '''stream a command (yield) back to the user, as each line is available. # Example usage: @@ -103,10 +113,10 @@ def stream_command(cmd, no_newline_regexp="Progess", sudo=False): cmd: the command to send, should be a list for subprocess no_newline_regexp: the regular expression to determine skipping a newline. Defaults to finding Progress + sudo_options: string or list of strings that will be passed as options to sudo ''' - if sudo: - cmd = ['sudo'] + cmd + cmd = _process_sudo_cmd(cmd, sudo, sudo_options) process = subprocess.Popen(cmd, stdout=subprocess.PIPE, @@ -124,7 +134,8 @@ def run_command(cmd, sudo=False, capture=True, no_newline_regexp="Progess", - quiet=False): + quiet=False, + sudo_options=None): '''run_command uses subprocess to send a command to the terminal. If capture is True, we use the parent stdout, so the progress bar (and @@ -140,10 +151,9 @@ def run_command(cmd, capture: if True, don't set stdout and have it go to console. This option can print a progress bar, but won't return the lines as output. + sudo_options: string or list of strings that will be passed as options to sudo ''' - - if sudo: - cmd = ['sudo'] + cmd + cmd = _process_sudo_cmd(cmd, sudo, sudo_options) stdout = None if capture: diff --git a/spython/version.py b/spython/version.py index 5e0fb736..59db0d50 100644 --- a/spython/version.py +++ b/spython/version.py @@ -6,7 +6,7 @@ # with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -__version__ = "0.0.72" +__version__ = "0.0.73" AUTHOR = 'Vanessa Sochat' AUTHOR_EMAIL = 'vsochat@stanford.edu' NAME = 'spython'