From 57cfdaab966ee06e8517b18ea887805c5570613d Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Tue, 5 Mar 2019 16:54:26 -0500 Subject: [PATCH 1/5] adding return code option to run and execute --- spython/main/base/command.py | 5 +++++ spython/main/execute.py | 9 +++++++-- spython/main/run.py | 15 +++++++++++++-- spython/tests/test_client.py | 6 ++++++ spython/tests/test_instances.py | 8 +++++++- 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/spython/main/base/command.py b/spython/main/base/command.py index fb11a172..43a671ef 100644 --- a/spython/main/base/command.py +++ b/spython/main/base/command.py @@ -135,6 +135,11 @@ def run_command(self, cmd, result = run_cmd(cmd, sudo=sudo, capture=capture, quiet=quiet) message = result['message'] + if return_result == True: + if len(result['message']) == 1: + result['message'] = result['message'][0] + return result + # On success, return result if result['return_code'] == 0: if len(result['message']) == 1: diff --git a/spython/main/execute.py b/spython/main/execute.py index 80c61083..4df805a6 100644 --- a/spython/main/execute.py +++ b/spython/main/execute.py @@ -20,7 +20,8 @@ def execute(self, contain = False, bind = None, stream = False, - nv = False): + nv = False, + return_result=False): ''' execute: send a command to a container @@ -37,6 +38,8 @@ def execute(self, This option allows you to map directories on your host system to directories within your container using bind mounts nv: if True, load Nvidia Drivers in runtime (default False) + return_result: if True, return entire json object with return code + and message result (default is False) ''' from spython.utils import check_install check_install() @@ -81,7 +84,9 @@ def execute(self, cmd = cmd + [image] + command if stream is False: - return self._run_command(cmd,sudo=sudo) + return self._run_command(cmd, + sudo=sudo, + return_result=return_result) return stream_command(cmd, sudo=sudo) bot.error('Please include a command (list) to execute.') diff --git a/spython/main/run.py b/spython/main/run.py index 07ba2d41..3ecf0404 100644 --- a/spython/main/run.py +++ b/spython/main/run.py @@ -19,7 +19,8 @@ def run(self, contain = False, bind = None, stream = False, - nv = False): + nv = False, + return_result=False): ''' run will run the container, with or withour arguments (which @@ -38,6 +39,9 @@ def run(self, directories within your container using bind mounts stream: if True, return for the user to run nv: if True, load Nvidia Drivers in runtime (default False) + return_result: if True, return entire json object with return code + and message result (default is False) + ''' from spython.utils import check_install check_install() @@ -76,10 +80,17 @@ def run(self, cmd = cmd + args if stream is False: - result = self._run_command(cmd, sudo=sudo) + result = self._run_command(cmd, + sudo=sudo, + return_result=return_result) else: return stream_command(cmd, sudo=sudo) + # If the user wants the raw result object + if return_result: + return result + + # Otherwise, we parse the result if it was successful if result: result = result.strip('\n') diff --git a/spython/tests/test_client.py b/spython/tests/test_client.py index 4f7bd9e2..4dbf8ae9 100644 --- a/spython/tests/test_client.py +++ b/spython/tests/test_client.py @@ -68,6 +68,12 @@ def test_commands(self): print(result) self.assertTrue('tmp\nusr\nvar' in result) + print('Testing client.execute command with return code') + result = self.cli.execute(container,'ls /', return_result=True) + print(result) + self.assertTrue('tmp\nusr\nvar' in result['message']) + self.assertEqual(result['return_code'], 0) + print("Testing client.inspect command") labels = self.cli.inspect(container) diff --git a/spython/tests/test_instances.py b/spython/tests/test_instances.py index d36c246c..590d9acb 100644 --- a/spython/tests/test_instances.py +++ b/spython/tests/test_instances.py @@ -55,7 +55,13 @@ def test_instances(self): result = self.cli.execute(myinstance, ['echo', 'hello']) self.assertTrue('hello\n' == result) - print("...Case 4: Stop instances") + print('...Case 4: Return value from instance') + result = self.cli.execute(myinstance,'ls /', return_result=True) + print(result) + self.assertTrue('tmp\nusr\nvar' in result['message']) + self.assertEqual(result['return_code'], 0) + + print("...Case 5: Stop instances") myinstance.stop() instances = self.cli.instances() self.assertEqual(instances, []) From 4181f2fb48568184eaba15155fc93ee0cee12562 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Tue, 5 Mar 2019 17:01:32 -0500 Subject: [PATCH 2/5] adding return code option to run and execute --- .circleci/config.yml | 3 ++- spython/main/base/command.py | 16 +++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bc923a26..0e1ada2b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,11 +28,12 @@ workflows: waitforapt: &waitforapt name: Remove cloud init lock command: | - sudo rm -rf /var/lib/apt/lists/lock while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo 'Waiting for cloud-init...'; sleep 10; done while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do echo 'Waiting for autoupdates to complete...'; sleep 10; done + while [ -f /var/lib/apt/lists/lock ]; do echo 'Waiting for apt updates...'; sleep 10; done echo 'Waiting for instance to really be ready...' sleep 30 + sudo rm -rf /var/lib/apt/lists/lock sudo rm /var/lib/dpkg/lock && sudo dpkg --configure -a diff --git a/spython/main/base/command.py b/spython/main/base/command.py index 43a671ef..103cf408 100644 --- a/spython/main/base/command.py +++ b/spython/main/base/command.py @@ -133,19 +133,17 @@ def run_command(self, cmd, quiet = self.quiet result = run_cmd(cmd, sudo=sudo, capture=capture, quiet=quiet) - message = result['message'] - if return_result == True: - if len(result['message']) == 1: - result['message'] = result['message'][0] + # If one line is returned, squash dimension + if len(result['message']) == 1: + result['message'] = result['message'][0] + + # If the user wants to return the result, just return it + if return_result is True: return result # On success, return result if result['return_code'] == 0: - if len(result['message']) == 1: - result['message'] = result['message'][0] return result['message'] - # For client (internal) calls, we want the return code - if return_result is True: - return result + return result From a972029790f0b7c37c7e12c8ffa49835bee0de18 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Tue, 5 Mar 2019 17:32:52 -0500 Subject: [PATCH 3/5] adding return code option to run and execute --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0e1ada2b..4e010b90 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,7 +30,6 @@ waitforapt: &waitforapt command: | while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo 'Waiting for cloud-init...'; sleep 10; done while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1; do echo 'Waiting for autoupdates to complete...'; sleep 10; done - while [ -f /var/lib/apt/lists/lock ]; do echo 'Waiting for apt updates...'; sleep 10; done echo 'Waiting for instance to really be ready...' sleep 30 sudo rm -rf /var/lib/apt/lists/lock From 1772dc1a38db7165c0976be81fa811effd9a4b54 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Tue, 5 Mar 2019 17:49:01 -0500 Subject: [PATCH 4/5] update testing --- spython/oci/__init__.py | 2 +- spython/oci/cmd/states.py | 10 ++++++---- spython/tests/test_oci.py | 24 +++++++++++++++--------- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/spython/oci/__init__.py b/spython/oci/__init__.py index 66939da1..6fdb7048 100644 --- a/spython/oci/__init__.py +++ b/spython/oci/__init__.py @@ -121,7 +121,7 @@ def _run_and_return(self, cmd, sudo=None): # Show the response to the user, only if not quiet. elif not self.quiet: - bot.print(result['message'][0]) + bot.print(result['message']) # Return the state object to the user return result['return_code'] diff --git a/spython/oci/cmd/states.py b/spython/oci/cmd/states.py index 378ee264..dfcd36c6 100644 --- a/spython/oci/cmd/states.py +++ b/spython/oci/cmd/states.py @@ -43,12 +43,14 @@ def state(self, container_id=None, sudo=None, sync_socket=None): cmd.append(container_id) # Get the instance state - result = self._run_command(cmd, sudo=sudo, quiet=True, return_result=True) + result = self._run_command(cmd, sudo=sudo, quiet=True) - # If successful, a string is returned to parse - if isinstance(result, str): - return json.loads(result) + if result != None: + # If successful, a string is returned to parse + if isinstance(result, str): + return json.loads(result) + def _state_command(self, container_id=None, command='start', sudo=None): diff --git a/spython/tests/test_oci.py b/spython/tests/test_oci.py index 3f06527e..0cb8844e 100644 --- a/spython/tests/test_oci.py +++ b/spython/tests/test_oci.py @@ -71,25 +71,31 @@ def test_oci(self): state = self.cli.oci.state(self.name, sudo=True) self.assertEqual(state['status'], 'created') - print('...Case 5. Start container.') + print('...Case 5. Start container return value 0.') state = self.cli.oci.start(self.name, sudo=True) - self.assertEqual(state, None) + self.assertEqual(state, 0) + + print('...Case 6. Testing that state is now running.') + state = self.cli.oci.state(self.name, sudo=True) + self.assertEqual(state['status'], 'running') - print('...Case 6. Pause running container.') + print('...Case 7. Pause running container return value 0.') state = self.cli.oci.pause(self.name, sudo=True) - self.assertEqual(state, None) + self.assertEqual(state, 0) - print('...Case 7. Resume paused container.') + print('...Case 8. Resume paused container return value 0.') state = self.cli.oci.resume(self.name, sudo=True) - self.assertEqual(state, None) + self.assertEqual(state, 0) - print('...Case 8. Kill should work with running container.') + print('...Case 9. Kill container.') state = self.cli.oci.kill(self.name, sudo=True) - self.assertTrue(state in [None, 255]) + self.assertEqual(state, 0) # Clean up the image (should still use sudo) + # Bug in singularity that kill doesn't kill completely - this returns + # 255. When testsupdated to 3.1.* add signal=K to run result = self.cli.oci.delete(self.name, sudo=True) - self.assertTrue(result in [None, 255]) + self.assertTrue(result in [0,255]) if __name__ == '__main__': From 6f81f734d7bf3194ea7aec5c0d832f16182c51f8 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Tue, 5 Mar 2019 18:00:36 -0500 Subject: [PATCH 5/5] bumping version --- CHANGELOG.md | 2 ++ spython/version.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6a317d2..17f6c1eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ 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) + - Added ability for exec and run to return the full output and message (0.0.55) + - By default, oci commands (pause, resume, kill) return the return value - Added support and tests for OCI image command group (0.0.54) - client now has version() function to call get_singularity_version - added return_result (boolean) to client run_command function. diff --git a/spython/version.py b/spython/version.py index ea618a61..38472069 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.54" +__version__ = "0.0.55" AUTHOR = 'Vanessa Sochat' AUTHOR_EMAIL = 'vsochat@stanford.edu' NAME = 'spython'