You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by kl...@apache.org on 2018/11/03 00:04:04 UTC

[mesos] branch master updated: Updated new CLI task attach/exec exit strategy.

This is an automated email from the ASF dual-hosted git repository.

klueska pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git


The following commit(s) were added to refs/heads/master by this push:
     new 25beea1  Updated new CLI task attach/exec exit strategy.
25beea1 is described below

commit 25beea12f9f12143e6df7b0ad2d272d4116c217c
Author: Kevin Klues <kl...@gmail.com>
AuthorDate: Fri Nov 2 19:55:44 2018 -0400

    Updated new CLI task attach/exec exit strategy.
    
    This code was pulled directly from:
    https://github.com/dcos/dcos-core-cli/blob/
        9d10e9d6fb2b16e46b58d67b7e9d79b2505f3451/
            python/lib/dcos/dcos/mesos.py
    
    Review: https://reviews.apache.org/r/69208/
---
 src/python/cli_new/lib/cli/mesos.py             | 48 ++++++++++++++++++++++++-
 src/python/cli_new/lib/cli/plugins/task/main.py | 15 +++-----
 src/python/cli_new/lib/cli/tests/task.py        | 32 +++++++++++++++++
 3 files changed, 84 insertions(+), 11 deletions(-)

diff --git a/src/python/cli_new/lib/cli/mesos.py b/src/python/cli_new/lib/cli/mesos.py
index 4a5777b..c4e5902 100644
--- a/src/python/cli_new/lib/cli/mesos.py
+++ b/src/python/cli_new/lib/cli/mesos.py
@@ -255,7 +255,7 @@ class TaskIO():
         # Allow an exit sequence to be used to break the CLIs attachment to
         # the remote task. Depending on the call, this may be disabled, or
         # the exit sequence to be used may be overwritten.
-        self.supports_exit_sequence = True
+        self.supports_exit_sequence = False
         self.exit_sequence = b'\x10\x11'  # Ctrl-p, Ctrl-q
         self.exit_sequence_detected = False
 
@@ -285,6 +285,18 @@ class TaskIO():
 
         self._run()
 
+        if not self.exit_sequence_detected:
+            # We are only able to get the 'exit_status' of tasks launched via
+            # the default executor (i.e. as pods rather than via the command
+            # executor). In the future, mesos will deprecate the command
+            # executor in favor of the default executor, so this check will
+            # be able to go away. In the meantime, we will always return '0'
+            # for tasks launched via the command executor.
+            if "parent" in self.container_id:
+                return self._wait()
+
+        return 0
+
     def exec(self, _cmd, _args=None, _interactive=False, _tty=False):
         """
         Execute a new process inside of a given task by redirecting
@@ -324,6 +336,8 @@ class TaskIO():
 
         self._run()
 
+        return self._wait()
+
     def _run(self):
         """
         Run the helper threads in this class which enable streaming
@@ -362,6 +376,7 @@ class TaskIO():
 
         try:
             if self.interactive:
+                self.supports_exit_sequence = True
                 tty.setraw(fd, when=termios.TCSANOW)
                 # To force a redraw of the remote terminal, we first resize it
                 # to 0 before setting it to the actual size of our local
@@ -386,6 +401,37 @@ class TaskIO():
             # pylint: disable=raising-bad-type
             raise self.exception
 
+    def _wait(self):
+        """
+        Wait for the container associated with this class (through
+        'container_id') to exit and return its exit status.
+        """
+        message = {
+            'type': 'WAIT_CONTAINER',
+            'wait_container': {
+                'container_id': self.container_id}}
+        req_extra_args = {
+            'additional_headers': {
+                'Content-Type': 'application/json',
+                'Accept': 'application/json'}}
+        try:
+            resource = mesos.http.Resource(self.agent_url)
+            response = resource.request(
+                mesos.http.METHOD_POST,
+                data=json.dumps(message),
+                retry=False,
+                timeout=None,
+                **req_extra_args)
+        except MesosException as exception:
+            raise CLIException(
+                "Error waiting for command to complete: {error}"
+                .format(error=exception))
+
+        exit_status = response.json()["wait_container"]["exit_status"]
+        if os.WIFSIGNALED(exit_status):
+            return os.WTERMSIG(exit_status) + 128
+        return os.WEXITSTATUS(exit_status)
+
     def _thread_wrapper(self, func):
         """
         A wrapper around all threads used in this class.
diff --git a/src/python/cli_new/lib/cli/plugins/task/main.py b/src/python/cli_new/lib/cli/plugins/task/main.py
index 5866a23..0536ccb 100644
--- a/src/python/cli_new/lib/cli/plugins/task/main.py
+++ b/src/python/cli_new/lib/cli/plugins/task/main.py
@@ -78,8 +78,7 @@ class Task(PluginBase):
                                .format(error=exception))
 
         task_io = TaskIO(master, argv["<task-id>"])
-        task_io.attach(argv["--no-stdin"])
-        return 0
+        return task_io.attach(argv["--no-stdin"])
 
 
     def exec(self, argv):
@@ -93,14 +92,10 @@ class Task(PluginBase):
                                .format(error=exception))
 
         task_io = TaskIO(master, argv["<task-id>"])
-        task_io.exec(argv["<command>"],
-                     argv["<args>"],
-                     argv["--interactive"],
-                     argv["--tty"])
-
-        # TODO(ArmandGrillet): We should not return 0 here but
-        # whatever the result of `<command> [<args>...]` was.
-        return 0
+        return task_io.exec(argv["<command>"],
+                            argv["<args>"],
+                            argv["--interactive"],
+                            argv["--tty"])
 
     def list(self, argv):
         """
diff --git a/src/python/cli_new/lib/cli/tests/task.py b/src/python/cli_new/lib/cli/tests/task.py
index ad11bd8..f033b4a 100644
--- a/src/python/cli_new/lib/cli/tests/task.py
+++ b/src/python/cli_new/lib/cli/tests/task.py
@@ -80,6 +80,38 @@ class TestTaskPlugin(CLITestCase):
         agent.kill()
         master.kill()
 
+    def test_exec_exit_status(self):
+        """
+        Basic test for the task `exec()` sub-command exit status.
+        """
+        # Launch a master, agent, and task.
+        master = Master()
+        master.launch()
+
+        agent = Agent()
+        agent.launch()
+
+        task = Task({"command": "sleep 1000"})
+        task.launch()
+
+        tasks = running_tasks(master)
+        if not tasks:
+            raise CLIException("Unable to find running tasks on master"
+                               " '{master}'".format(master=master.addr))
+
+        returncode, _, _ = exec_command(
+            ["mesos", "task", "exec", tasks[0]["id"], "true"])
+        self.assertEqual(returncode, 0)
+
+        returncode, _, _ = exec_command(
+            ["mesos", "task", "exec", tasks[0]["id"], "bash", "-c", "exit 10"])
+        self.assertEqual(returncode, 10)
+
+        # Kill the task, agent, and master.
+        task.kill()
+        agent.kill()
+        master.kill()
+
 
     def test_exec_interactive(self):
         """