You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by vi...@apache.org on 2016/10/14 19:00:05 UTC

[1/5] mesos git commit: Added utils method to check if `ExecutorInfo` different.

Repository: mesos
Updated Branches:
  refs/heads/master b0d4439d6 -> 9afaaffd8


Added utils method to check if `ExecutorInfo` different.

Review: https://reviews.apache.org/r/52817/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/ebe7ea56
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/ebe7ea56
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/ebe7ea56

Branch: refs/heads/master
Commit: ebe7ea56fa0f00e63a3fd274eaad75b58985b075
Parents: b0d4439
Author: haosdent huang <ha...@gmail.com>
Authored: Fri Oct 14 11:59:26 2016 -0700
Committer: Vinod Kone <vi...@gmail.com>
Committed: Fri Oct 14 11:59:26 2016 -0700

----------------------------------------------------------------------
 include/mesos/type_utils.hpp |  1 +
 src/common/type_utils.cpp    | 15 ++++++++++++++-
 2 files changed, 15 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/ebe7ea56/include/mesos/type_utils.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/type_utils.hpp b/include/mesos/type_utils.hpp
index 1d09b4f..88c7bcf 100644
--- a/include/mesos/type_utils.hpp
+++ b/include/mesos/type_utils.hpp
@@ -68,6 +68,7 @@ bool operator==(const TaskStatus& left, const TaskStatus& right);
 bool operator==(const URL& left, const URL& right);
 bool operator==(const Volume& left, const Volume& right);
 
+bool operator!=(const ExecutorInfo& left, const ExecutorInfo& right);
 bool operator!=(const Labels& left, const Labels& right);
 bool operator!=(const TaskStatus& left, const TaskStatus& right);
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/ebe7ea56/src/common/type_utils.cpp
----------------------------------------------------------------------
diff --git a/src/common/type_utils.cpp b/src/common/type_utils.cpp
index cad243f..c6cf4f1 100644
--- a/src/common/type_utils.cpp
+++ b/src/common/type_utils.cpp
@@ -318,7 +318,14 @@ bool operator==(const DiscoveryInfo& left, const DiscoveryInfo& right)
 
 bool operator==(const ExecutorInfo& left, const ExecutorInfo& right)
 {
-  return left.executor_id() == right.executor_id() &&
+  if (left.has_type() && right.has_type()) {
+    if (left.type() != right.type()) {
+      return false;
+    }
+  }
+
+  return left.has_type() == right.has_type() &&
+    left.executor_id() == right.executor_id() &&
     left.data() == right.data() &&
     Resources(left.resources()) == Resources(right.resources()) &&
     left.command() == right.command() &&
@@ -330,6 +337,12 @@ bool operator==(const ExecutorInfo& left, const ExecutorInfo& right)
 }
 
 
+bool operator!=(const ExecutorInfo& left, const ExecutorInfo& right)
+{
+  return !(left == right);
+}
+
+
 bool operator==(const MasterInfo& left, const MasterInfo& right)
 {
   return left.id() == right.id() &&


[3/5] mesos git commit: Exposed the executor's type in the endpoints.

Posted by vi...@apache.org.
Exposed the executor's type in the endpoints.

Review: https://reviews.apache.org/r/52520/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/88cd1df7
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/88cd1df7
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/88cd1df7

Branch: refs/heads/master
Commit: 88cd1df7af6904e2a34ece1f10bee59915d68f67
Parents: d2da824
Author: haosdent huang <ha...@gmail.com>
Authored: Fri Oct 14 11:59:39 2016 -0700
Committer: Vinod Kone <vi...@gmail.com>
Committed: Fri Oct 14 11:59:39 2016 -0700

----------------------------------------------------------------------
 src/common/http.cpp                  |  4 ++++
 src/slave/http.cpp                   |  4 ++++
 src/tests/default_executor_tests.cpp | 23 +++++++++++++++++++++++
 3 files changed, 31 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/88cd1df7/src/common/http.cpp
----------------------------------------------------------------------
diff --git a/src/common/http.cpp b/src/common/http.cpp
index 538330a..fb8454a 100644
--- a/src/common/http.cpp
+++ b/src/common/http.cpp
@@ -530,6 +530,10 @@ void json(JSON::ObjectWriter* writer, const ExecutorInfo& executorInfo)
   if (executorInfo.has_labels()) {
     writer->field("labels", executorInfo.labels());
   }
+
+  if (executorInfo.has_type()) {
+    writer->field("type", ExecutorInfo::Type_Name(executorInfo.type()));
+  }
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/88cd1df7/src/slave/http.cpp
----------------------------------------------------------------------
diff --git a/src/slave/http.cpp b/src/slave/http.cpp
index a1fbb7a..a32aca4 100644
--- a/src/slave/http.cpp
+++ b/src/slave/http.cpp
@@ -153,6 +153,10 @@ struct ExecutorWriter
       writer->field("labels", executor_->info.labels());
     }
 
+    if (executor_->info.has_type()) {
+      writer->field("type", ExecutorInfo::Type_Name(executor_->info.type()));
+    }
+
     writer->field("tasks", [this](JSON::ArrayWriter* writer) {
       foreach (Task* task, executor_->launchedTasks.values()) {
         if (!approveViewTask(taskApprover_, *task, framework_->info)) {

http://git-wip-us.apache.org/repos/asf/mesos/blob/88cd1df7/src/tests/default_executor_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/default_executor_tests.cpp b/src/tests/default_executor_tests.cpp
index dc002c6..92e6b9f 100644
--- a/src/tests/default_executor_tests.cpp
+++ b/src/tests/default_executor_tests.cpp
@@ -25,6 +25,7 @@
 #include <mesos/v1/executor.hpp>
 #include <mesos/v1/scheduler.hpp>
 
+#include <process/http.hpp>
 #include <process/owned.hpp>
 
 #include <stout/hashset.hpp>
@@ -40,6 +41,9 @@ using mesos::v1::scheduler::Mesos;
 using process::Future;
 using process::Owned;
 
+using process::http::OK;
+using process::http::Response;
+
 using std::pair;
 using std::set;
 using std::string;
@@ -181,6 +185,25 @@ TEST_P(DefaultExecutorTest, ROOT_TaskRunning)
           executorInfo.executor_id()),
       "tasks",
       taskInfo.task_id().value())));
+
+  // Verify that the executor's type is exposed in the agent's state
+  // endpoint.
+  Future<Response> response = process::http::get(
+      slave.get()->pid,
+      "state",
+      None(),
+      createBasicAuthHeaders(DEFAULT_CREDENTIAL));
+
+  AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response);
+  AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response);
+
+  Try<JSON::Object> parse = JSON::parse<JSON::Object>(response.get().body);
+  ASSERT_SOME(parse);
+  JSON::Object state = parse.get();
+
+  EXPECT_SOME_EQ(
+      JSON::String(ExecutorInfo::Type_Name(executorInfo.type())),
+      state.find<JSON::String>("frameworks[0].executors[0].type"));
 }
 
 


[4/5] mesos git commit: Fixed the wrong sandbox directory of the tasks in Web UI.

Posted by vi...@apache.org.
Fixed the wrong sandbox directory of the tasks in Web UI.

For the task launched by default-executor, its sandbox directory is
'executor_directory/tasks/task_id/'. This patch generates the
corresponding sandbox directory in Web UI for the task according to its
executor type.

Review: https://reviews.apache.org/r/52471/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/6762fc8f
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/6762fc8f
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/6762fc8f

Branch: refs/heads/master
Commit: 6762fc8fb26470e3e24a915d4c19ceb2005fcce7
Parents: 88cd1df
Author: haosdent huang <ha...@gmail.com>
Authored: Fri Oct 14 11:59:44 2016 -0700
Committer: Vinod Kone <vi...@gmail.com>
Committed: Fri Oct 14 11:59:44 2016 -0700

----------------------------------------------------------------------
 src/webui/master/static/agent_executor.html |  4 +-
 src/webui/master/static/framework.html      |  4 +-
 src/webui/master/static/home.html           |  6 +--
 src/webui/master/static/js/app.js           |  4 +-
 src/webui/master/static/js/controllers.js   | 60 +++++++++++++++++++++---
 5 files changed, 63 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/6762fc8f/src/webui/master/static/agent_executor.html
----------------------------------------------------------------------
diff --git a/src/webui/master/static/agent_executor.html b/src/webui/master/static/agent_executor.html
index 8b83ed5..d2ab85d 100644
--- a/src/webui/master/static/agent_executor.html
+++ b/src/webui/master/static/agent_executor.html
@@ -147,7 +147,7 @@
           <td>{{task.resources.disk * (1024 * 1024) | dataSize}}</td>
           <td>
             <a href="{{'#/agents/' + agent_id + '/browse?path=' +
-                       encodeURIComponent(executor.directory)}}">
+                       encodeURIComponent(task.directory)}}">
               Sandbox
             </a>
           </td>
@@ -180,7 +180,7 @@
           <td>{{completed_task.resources.disk * (1024 * 1024) | dataSize}}</td>
           <td>
             <a href="{{'#/agents/' + agent_id + '/browse?path=' +
-                       encodeURIComponent(executor.directory)}}">
+                       encodeURIComponent(completed_task.directory)}}">
               Sandbox
             </a>
           </td>

http://git-wip-us.apache.org/repos/asf/mesos/blob/6762fc8f/src/webui/master/static/framework.html
----------------------------------------------------------------------
diff --git a/src/webui/master/static/framework.html b/src/webui/master/static/framework.html
index bc3c56a..6297cf9 100644
--- a/src/webui/master/static/framework.html
+++ b/src/webui/master/static/framework.html
@@ -84,7 +84,7 @@
             </span>
           </td>
           <td>
-            <a data-ng-show="agents[task.slave_id]" href="#/agents/{{task.slave_id}}/frameworks/{{task.framework_id}}/executors/{{task.executor_id}}/browse">
+            <a data-ng-show="agents[task.slave_id]" href="#/agents/{{task.slave_id}}/frameworks/{{task.framework_id}}/executors/{{task.executor_id}}/tasks/{{task.id}}/browse">
               Sandbox
             </a>
             <span class="text-muted" data-ng-show="!agents[task.slave_id]">
@@ -129,7 +129,7 @@
             </span>
           </td>
           <td>
-            <a data-ng-show="agents[task.slave_id]" href="#/agents/{{task.slave_id}}/frameworks/{{task.framework_id}}/executors/{{task.executor_id}}/browse">
+            <a data-ng-show="agents[task.slave_id]" href="#/agents/{{task.slave_id}}/frameworks/{{task.framework_id}}/executors/{{task.executor_id}}/tasks/{{task.id}}/browse">
               Sandbox
             </a>
             <span class="text-muted" data-ng-show="!agents[task.slave_id]">

http://git-wip-us.apache.org/repos/asf/mesos/blob/6762fc8f/src/webui/master/static/home.html
----------------------------------------------------------------------
diff --git a/src/webui/master/static/home.html b/src/webui/master/static/home.html
index 179cb15..07f862f 100644
--- a/src/webui/master/static/home.html
+++ b/src/webui/master/static/home.html
@@ -181,7 +181,7 @@
             </span>
           </td>
           <td>
-            <a data-ng-show="agents[task.slave_id]" href="#/agents/{{task.slave_id}}/frameworks/{{task.framework_id}}/executors/{{task.executor_id}}/browse">
+            <a data-ng-show="agents[task.slave_id]" href="#/agents/{{task.slave_id}}/frameworks/{{task.framework_id}}/executors/{{task.executor_id}}/tasks/{{task.id}}/browse">
               Sandbox
             </a>
             <span class="text-muted" data-ng-show="!agents[task.slave_id]">
@@ -229,7 +229,7 @@
             </span>
           </td>
           <td>
-            <a data-ng-show="agents[task.slave_id]" href="#/agents/{{task.slave_id}}/frameworks/{{task.framework_id}}/executors/{{task.executor_id}}/browse">
+            <a data-ng-show="agents[task.slave_id]" href="#/agents/{{task.slave_id}}/frameworks/{{task.framework_id}}/executors/{{task.executor_id}}/tasks/{{task.id}}/browse">
               Sandbox
             </a>
             <span class="text-muted" data-ng-show="!agents[task.slave_id]">
@@ -276,7 +276,7 @@
               </span>
             </td>
             <td>
-              <a data-ng-show="agents[task.slave_id]" href="#/agents/{{task.slave_id}}/frameworks/{{task.framework_id}}/executors/{{task.executor_id}}/browse">
+              <a data-ng-show="agents[task.slave_id]" href="#/agents/{{task.slave_id}}/frameworks/{{task.framework_id}}/executors/{{task.executor_id}}/tasks/{{task.id}}/browse">
                 Sandbox
               </a>
               <span class="text-muted" data-ng-show="!agents[task.slave_id]">

http://git-wip-us.apache.org/repos/asf/mesos/blob/6762fc8f/src/webui/master/static/js/app.js
----------------------------------------------------------------------
diff --git a/src/webui/master/static/js/app.js b/src/webui/master/static/js/app.js
index 400a428..c764430 100644
--- a/src/webui/master/static/js/app.js
+++ b/src/webui/master/static/js/app.js
@@ -40,7 +40,9 @@
         //
         //     https://github.com/angular/angular.js/issues/1838
         .when('/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/browse',
-          {template: ' ', controller: 'AgentExecutorRerouterCtrl'})
+          {template: ' ', controller: 'AgentTaskAndExecutorRerouterCtrl'})
+        .when('/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/tasks/:task_id/browse',
+          {template: ' ', controller: 'AgentTaskAndExecutorRerouterCtrl'})
         .when('/agents/:agent_id/browse',
           {templateUrl: 'static/browse.html', controller: 'BrowseCtrl'})
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/6762fc8f/src/webui/master/static/js/controllers.js
----------------------------------------------------------------------
diff --git a/src/webui/master/static/js/controllers.js b/src/webui/master/static/js/controllers.js
index 29a5a1c..8aac466 100644
--- a/src/webui/master/static/js/controllers.js
+++ b/src/webui/master/static/js/controllers.js
@@ -49,6 +49,21 @@
     }
   }
 
+  // Set the task sandbox directory for use by the WebUI.
+  function setTaskSandbox(executor) {
+    _.each(
+        [executor.tasks, executor.queued_tasks, executor.completed_tasks],
+        function(tasks) {
+      _.each(tasks, function(task) {
+        if (executor.type === 'DEFAULT') {
+          task.directory = executor.directory + '/tasks/' + task.id;
+        } else {
+          task.directory = executor.directory;
+        };
+      });
+    });
+  }
+
 
   // Update the outermost scope with the new state.
   function updateState($scope, $timeout, state) {
@@ -705,6 +720,8 @@
             return;
           }
 
+          setTaskSandbox($scope.executor);
+
           $('#agent').show();
         })
         .error(function (reason) {
@@ -722,15 +739,17 @@
   }]);
 
 
-  // Reroutes a request like
-  // '/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/browse'
-  // to the executor's sandbox. This requires a second request because the
-  // directory to browse is known by the agent but not by the master. Request
-  // the directory from the agent, and then redirect to it.
+  // Reroutes requests like:
+  //   * '/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/browse'
+  //   * '/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/tasks/:task_id/browse'
+  // to the sandbox directory of the executor or the task respectively. This
+  // requires a second request because the directory to browse is known by the
+  // agent but not by the master. Request the directory from the agent, and then
+  // redirect to it.
   //
   // TODO(ssorallen): Add `executor.directory` to the master's state endpoint
   // output so this controller of rerouting is no longer necessary.
-  mesosApp.controller('AgentExecutorRerouterCtrl',
+  mesosApp.controller('AgentTaskAndExecutorRerouterCtrl',
       function($alert, $http, $location, $routeParams, $scope, $window) {
 
     function goBack(flashMessageOrOptions) {
@@ -809,10 +828,37 @@
           );
         }
 
+        var sandboxDirectory = executor.directory;
+
+        // Continue to navigate to the task's sandbox if the task id is
+        // specified in route parameters.
+        if ($routeParams.task_id) {
+          setTaskSandbox(executor);
+
+          function matchTask(task) {
+            return $routeParams.task_id === task.id;
+          }
+
+          var task =
+            _.find(executor.tasks, matchTask) ||
+            _.find(executor.queued_tasks, matchTask) ||
+            _.find(executor.completed_tasks, matchTask);
+
+          if (!task) {
+            return goBack(
+              "Task with ID '" + $routeParams.task_id +
+                "' does not exist on agent with ID '" + $routeParams.agent_id +
+                "'."
+            );
+          }
+
+          sandboxDirectory = task.directory;
+        }
+
         // Navigate to a path like '/agents/:id/browse?path=%2Ftmp%2F', the
         // recognized "browse" endpoint for an agent.
         $location.path('/agents/' + $routeParams.agent_id + '/browse')
-          .search({path: executor.directory})
+          .search({path: sandboxDirectory})
           .replace();
       })
       .error(function(response) {


[5/5] mesos git commit: Moved the `decimalFloat` filter to app.js for consistency.

Posted by vi...@apache.org.
Moved the `decimalFloat` filter to app.js for consistency.

Review: https://reviews.apache.org/r/52664/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/9afaaffd
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/9afaaffd
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/9afaaffd

Branch: refs/heads/master
Commit: 9afaaffd8badbe4e28013e25918cd62afe1e0edd
Parents: 6762fc8
Author: haosdent huang <ha...@gmail.com>
Authored: Fri Oct 14 11:59:48 2016 -0700
Committer: Vinod Kone <vi...@gmail.com>
Committed: Fri Oct 14 11:59:48 2016 -0700

----------------------------------------------------------------------
 src/webui/master/static/js/app.js         | 6 ++++++
 src/webui/master/static/js/controllers.js | 7 -------
 2 files changed, 6 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/9afaaffd/src/webui/master/static/js/app.js
----------------------------------------------------------------------
diff --git a/src/webui/master/static/js/app.js b/src/webui/master/static/js/app.js
index c764430..c32177a 100644
--- a/src/webui/master/static/js/app.js
+++ b/src/webui/master/static/js/app.js
@@ -118,6 +118,12 @@
         }
       };
     })
+    // A filter that uses to convert small float number to decimal string.
+    .filter('decimalFloat', function() {
+      return function(num) {
+        return num ? parseFloat(num.toFixed(4)).toString() : num;
+      }
+    })
     .filter('dataSize', function() {
       var BYTES_PER_KB = Math.pow(2, 10);
       var BYTES_PER_MB = Math.pow(2, 20);

http://git-wip-us.apache.org/repos/asf/mesos/blob/9afaaffd/src/webui/master/static/js/controllers.js
----------------------------------------------------------------------
diff --git a/src/webui/master/static/js/controllers.js b/src/webui/master/static/js/controllers.js
index 8aac466..0a7ea3f 100644
--- a/src/webui/master/static/js/controllers.js
+++ b/src/webui/master/static/js/controllers.js
@@ -240,13 +240,6 @@
     return true; // Continue polling.
   }
 
-  // Add a filter to convert small float number to decimal string
-  mesosApp.filter('decimalFloat', function() {
-    return function(num) {
-      return num ? parseFloat(num.toFixed(4)).toString() : num;
-    }
-  });
-
   // Update the outermost scope with the metrics/snapshot endpoint.
   function updateMetrics($scope, $timeout, data) {
     var metrics = JSON.parse(data);


[2/5] mesos git commit: Filled missing executor info in tasks when `LAUNCH_GROUP`.

Posted by vi...@apache.org.
Filled missing executor info in tasks when `LAUNCH_GROUP`.

This fixed the navigate error in Web UI because Web UI uses the
executor id of the task to search the corresponding sandbox directory.
Web UI uses the task id as the executor id if the executor id of the
task is empty when searching the sandbox directory. It works fine when
tasks are launched by `CommandExecutor` because the executor id of the
task is equal to the task id in this case. However, when tasks are
launched by `DefaultExecutor`, the executor id of the task is defined
in the framework side and may different with the task id. So we need to
fill the `ExecutorInfo` of the `TaskInfo` when `LAUNCH_GROUP` to avoid
the Web UI uses incorrect executor id to search sandbox directory.

Review: https://reviews.apache.org/r/52470/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/d2da824c
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/d2da824c
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/d2da824c

Branch: refs/heads/master
Commit: d2da824cfbfc4242ea4962d763da9726faf7aaca
Parents: ebe7ea5
Author: haosdent huang <ha...@gmail.com>
Authored: Fri Oct 14 11:59:32 2016 -0700
Committer: Vinod Kone <vi...@gmail.com>
Committed: Fri Oct 14 11:59:32 2016 -0700

----------------------------------------------------------------------
 include/mesos/mesos.proto             |   5 +-
 include/mesos/v1/mesos.proto          |   5 +-
 src/master/master.cpp                 |  25 +++++--
 src/master/master.hpp                 |   2 +-
 src/master/validation.cpp             |  15 +++-
 src/tests/default_executor_tests.cpp  | 112 +++++++++++++++++++++++++++++
 src/tests/master_validation_tests.cpp |  20 ++++--
 7 files changed, 166 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/d2da824c/include/mesos/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/mesos.proto b/include/mesos/mesos.proto
index 05988d4..d071431 100644
--- a/include/mesos/mesos.proto
+++ b/include/mesos/mesos.proto
@@ -1436,8 +1436,9 @@ message TaskInfo {
  * allow the group to be launched "atomically".
  *
  * NOTES:
- * 1) `TaskInfo.executor` must not be set.
- * 2) `NetworkInfo` must not be set inside task's `ContainerInfo`.
+ * 1) `NetworkInfo` must not be set inside task's `ContainerInfo`.
+ * 2) `TaskInfo.executor` doesn't need to set. If set, it should match
+ *    `LaunchGroup.executor`.
  */
 message TaskGroupInfo {
   repeated TaskInfo tasks = 1;

http://git-wip-us.apache.org/repos/asf/mesos/blob/d2da824c/include/mesos/v1/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/v1/mesos.proto b/include/mesos/v1/mesos.proto
index 08a536c..74761a0 100644
--- a/include/mesos/v1/mesos.proto
+++ b/include/mesos/v1/mesos.proto
@@ -1435,8 +1435,9 @@ message TaskInfo {
  * allow the group to be launched "atomically".
  *
  * NOTES:
- * 1) `TaskInfo.executor` must not be set.
- * 2) `NetworkInfo` must not be set inside task's `ContainerInfo`.
+ * 1) `NetworkInfo` must not be set inside task's `ContainerInfo`.
+ * 2) `TaskInfo.executor` doesn't need to set. If set, it should match
+ *    `LaunchGroup.executor`.
  */
 message TaskGroupInfo {
   repeated TaskInfo tasks = 1;

http://git-wip-us.apache.org/repos/asf/mesos/blob/d2da824c/src/master/master.cpp
----------------------------------------------------------------------
diff --git a/src/master/master.cpp b/src/master/master.cpp
index 7ef8987..3c6b18e 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -3405,13 +3405,15 @@ Resources Master::addTask(
 
 void Master::accept(
     Framework* framework,
-    const scheduler::Call::Accept& accept)
+    scheduler::Call::Accept accept)
 {
   CHECK_NOTNULL(framework);
 
-  foreach (const Offer::Operation& operation, accept.operations()) {
-    if (operation.type() == Offer::Operation::LAUNCH) {
-      if (operation.launch().task_infos().size() > 0) {
+  for (int i = 0; i < accept.operations_size(); ++i) {
+    Offer::Operation* operation = accept.mutable_operations(i);
+
+    if (operation->type() == Offer::Operation::LAUNCH) {
+      if (operation->launch().task_infos().size() > 0) {
         ++metrics->messages_launch_tasks;
       } else {
         ++metrics->messages_decline_offers;
@@ -3419,6 +3421,21 @@ void Master::accept(
                      << " in ACCEPT call for framework " << framework->id()
                      << " as the launch operation specified no tasks";
       }
+    } else if (operation->type() == Offer::Operation::LAUNCH_GROUP) {
+      const ExecutorInfo& executor = operation->launch_group().executor();
+
+      TaskGroupInfo* taskGroup =
+        operation->mutable_launch_group()->mutable_task_group();
+
+      // Mutate `TaskInfo` to include `ExecutorInfo` to make it easy
+      // for operator API and WebUI to get access to the corresponding
+      // executor for tasks in the task group.
+      for (int j = 0; j < taskGroup->tasks().size(); ++j) {
+        TaskInfo* task = taskGroup->mutable_tasks(j);
+        if (!task->has_executor()) {
+          task->mutable_executor()->CopyFrom(executor);
+        }
+      }
     }
 
     // TODO(jieyu): Add metrics for non launch operations.

http://git-wip-us.apache.org/repos/asf/mesos/blob/d2da824c/src/master/master.hpp
----------------------------------------------------------------------
diff --git a/src/master/master.hpp b/src/master/master.hpp
index 43518b9..881f0d6 100644
--- a/src/master/master.hpp
+++ b/src/master/master.hpp
@@ -911,7 +911,7 @@ private:
 
   void accept(
       Framework* framework,
-      const scheduler::Call::Accept& accept);
+      scheduler::Call::Accept accept);
 
   void _accept(
       const FrameworkID& frameworkId,

http://git-wip-us.apache.org/repos/asf/mesos/blob/d2da824c/src/master/validation.cpp
----------------------------------------------------------------------
diff --git a/src/master/validation.cpp b/src/master/validation.cpp
index 480a94b..f690a9e 100644
--- a/src/master/validation.cpp
+++ b/src/master/validation.cpp
@@ -1011,8 +1011,8 @@ Option<Error> validateTask(
 
   // Now do `TaskGroup` specific validation.
 
-  if (task.has_executor()) {
-    return Error("'TaskInfo.executor' must not be set");
+  if (!task.has_executor()) {
+    return Error("'TaskInfo.executor' must be set");
   }
 
   if (task.has_container()) {
@@ -1087,6 +1087,17 @@ Option<Error> validateExecutor(
     return Error("Docker ContainerInfo is not supported on the executor");
   }
 
+  // Validate the `ExecutorInfo` in all tasks are same.
+
+  foreach (const TaskInfo& task, taskGroup.tasks()) {
+    if (task.has_executor() && task.executor() != executor) {
+      return Error(
+          "The `ExecutorInfo` of "
+          "task '" + stringify(task.task_id()) + "' is different from "
+          "executor '" + stringify(executor.executor_id()) + "'");
+    }
+  }
+
   const Resources& executorResources = executor.resources();
 
   // Validate minimal cpus and memory resources of executor.

http://git-wip-us.apache.org/repos/asf/mesos/blob/d2da824c/src/tests/default_executor_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/default_executor_tests.cpp b/src/tests/default_executor_tests.cpp
index 9e0fd67..dc002c6 100644
--- a/src/tests/default_executor_tests.cpp
+++ b/src/tests/default_executor_tests.cpp
@@ -541,6 +541,118 @@ TEST_F(DefaultExecutorTest, KillTaskGroupOnTaskFailure)
   ASSERT_EQ(expectedTaskStates, taskStates);
 }
 
+
+// Verifies that a task in a task group with an executor is accepted
+// during `TaskGroupInfo` validation.
+TEST_P(DefaultExecutorTest, ROOT_TaskUsesExecutor)
+{
+  Try<Owned<cluster::Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  auto scheduler = std::make_shared<MockV1HTTPScheduler>();
+
+  Resources resources =
+    Resources::parse("cpus:0.1;mem:32;disk:32").get();
+
+  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+
+  ExecutorInfo executorInfo;
+  executorInfo.set_type(ExecutorInfo::DEFAULT);
+
+  executorInfo.mutable_executor_id()->CopyFrom(DEFAULT_EXECUTOR_ID);
+  executorInfo.mutable_resources()->CopyFrom(resources);
+
+  // Disable AuthN on the agent.
+  slave::Flags flags = CreateSlaveFlags();
+  flags.authenticate_http_readwrite = false;
+  flags.containerizers = GetParam();
+
+  Owned<MasterDetector> detector = master.get()->createDetector();
+  Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), flags);
+  ASSERT_SOME(slave);
+
+  Future<Nothing> connected;
+  EXPECT_CALL(*scheduler, connected(_))
+    .WillOnce(FutureSatisfy(&connected));
+
+  scheduler::TestV1Mesos mesos(
+      master.get()->pid, ContentType::PROTOBUF, scheduler);
+
+  AWAIT_READY(connected);
+
+  Future<v1::scheduler::Event::Subscribed> subscribed;
+  EXPECT_CALL(*scheduler, subscribed(_, _))
+    .WillOnce(FutureArg<1>(&subscribed));
+
+  Future<v1::scheduler::Event::Offers> offers;
+  EXPECT_CALL(*scheduler, offers(_, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return());
+
+  EXPECT_CALL(*scheduler, heartbeat(_))
+    .WillRepeatedly(Return()); // Ignore heartbeats.
+
+  {
+    Call call;
+    call.set_type(Call::SUBSCRIBE);
+    Call::Subscribe* subscribe = call.mutable_subscribe();
+    subscribe->mutable_framework_info()->CopyFrom(evolve(frameworkInfo));
+
+    mesos.send(call);
+  }
+
+  AWAIT_READY(subscribed);
+
+  v1::FrameworkID frameworkId(subscribed->framework_id());
+
+  // Update `executorInfo` with the subscribed `frameworkId`.
+  executorInfo.mutable_framework_id()->CopyFrom(devolve(frameworkId));
+
+  AWAIT_READY(offers);
+  EXPECT_NE(0, offers->offers().size());
+
+  Future<v1::scheduler::Event::Update> update;
+  EXPECT_CALL(*scheduler, update(_, _))
+    .WillOnce(FutureArg<1>(&update));
+
+  const v1::Offer& offer = offers->offers(0);
+  const SlaveID slaveId = devolve(offer.agent_id());
+
+  v1::TaskInfo taskInfo =
+    evolve(createTask(slaveId, resources, "sleep 1000"));
+
+  taskInfo.mutable_executor()->CopyFrom(evolve(executorInfo));
+
+  v1::TaskGroupInfo taskGroup;
+  taskGroup.add_tasks()->CopyFrom(taskInfo);
+
+  {
+    Call call;
+    call.mutable_framework_id()->CopyFrom(frameworkId);
+    call.set_type(Call::ACCEPT);
+
+    Call::Accept* accept = call.mutable_accept();
+    accept->add_offer_ids()->CopyFrom(offer.id());
+
+    v1::Offer::Operation* operation = accept->add_operations();
+    operation->set_type(v1::Offer::Operation::LAUNCH_GROUP);
+
+    v1::Offer::Operation::LaunchGroup* launchGroup =
+      operation->mutable_launch_group();
+
+    launchGroup->mutable_executor()->CopyFrom(evolve(executorInfo));
+    launchGroup->mutable_task_group()->CopyFrom(taskGroup);
+
+    mesos.send(call);
+  }
+
+  AWAIT_READY(update);
+
+  ASSERT_EQ(TASK_RUNNING, update->status().state());
+  EXPECT_EQ(taskInfo.task_id(), update->status().task_id());
+  EXPECT_TRUE(update->status().has_timestamp());
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/d2da824c/src/tests/master_validation_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_validation_tests.cpp b/src/tests/master_validation_tests.cpp
index 0f8d33b..da43f99 100644
--- a/src/tests/master_validation_tests.cpp
+++ b/src/tests/master_validation_tests.cpp
@@ -2105,9 +2105,9 @@ TEST_F(TaskGroupValidationTest, TaskUsesNetworkInfo)
 }
 
 
-// Ensures that a task in a task group with an executor
+// Ensures that a task in a task group with a different executor
 // is rejected during `TaskGroupInfo` validation.
-TEST_F(TaskGroupValidationTest, TaskUsesExecutor)
+TEST_F(TaskGroupValidationTest, TaskUsesDifferentExecutor)
 {
   Try<Owned<cluster::Master>> master = StartMaster();
   ASSERT_SOME(master);
@@ -2116,9 +2116,12 @@ TEST_F(TaskGroupValidationTest, TaskUsesExecutor)
   Try<Owned<cluster::Slave>> slave = StartSlave(detector.get());
   ASSERT_SOME(slave);
 
+  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo.mutable_id()->set_value("Test_Framework");
+
   MockScheduler sched;
   MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
+      &sched, frameworkInfo, master.get()->pid, DEFAULT_CREDENTIAL);
 
   EXPECT_CALL(sched, registered(&driver, _, _))
     .Times(1);
@@ -2137,23 +2140,25 @@ TEST_F(TaskGroupValidationTest, TaskUsesExecutor)
   Resources resources = Resources::parse("cpus:1;mem:512;disk:32").get();
 
   ExecutorInfo executor(DEFAULT_EXECUTOR_INFO);
+  executor.mutable_framework_id()->CopyFrom(frameworkInfo.id());
   executor.set_type(ExecutorInfo::CUSTOM);
   executor.mutable_resources()->CopyFrom(resources);
 
-  // Create an invalid task that has executor.
+  // Create an invalid task that has a different executor.
   TaskInfo task1;
   task1.set_name("1");
   task1.mutable_task_id()->set_value("1");
   task1.mutable_slave_id()->MergeFrom(offer.slave_id());
   task1.mutable_resources()->MergeFrom(resources);
   task1.mutable_executor()->MergeFrom(executor);
+  task1.mutable_executor()->set_type(ExecutorInfo::DEFAULT);
 
   // Create a valid task.
   TaskInfo task2;
   task2.set_name("2");
   task2.mutable_task_id()->set_value("2");
   task2.mutable_slave_id()->MergeFrom(offer.slave_id());
-  task1.mutable_resources()->MergeFrom(resources);
+  task2.mutable_resources()->MergeFrom(resources);
 
   TaskGroupInfo taskGroup;
   taskGroup.add_tasks()->CopyFrom(task1);
@@ -2180,8 +2185,9 @@ TEST_F(TaskGroupValidationTest, TaskUsesExecutor)
   EXPECT_EQ(task1.task_id(), task1Status->task_id());
   EXPECT_EQ(TASK_ERROR, task1Status->state());
   EXPECT_EQ(TaskStatus::REASON_TASK_GROUP_INVALID, task1Status->reason());
-  EXPECT_EQ("Task '1' is invalid: 'TaskInfo.executor' must not be set",
-            task1Status->message());
+  EXPECT_EQ(
+      "The `ExecutorInfo` of task '1' is different from executor 'default'",
+      task1Status->message());
 
   AWAIT_READY(task2Status);
   EXPECT_EQ(task2.task_id(), task2Status->task_id());