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 2014/05/12 21:51:12 UTC
[1/2] git commit: Moved ReconcileTaskTest test to its own file.
Repository: mesos
Updated Branches:
refs/heads/master c4188341d -> cceb071a0
Moved ReconcileTaskTest test to its own file.
Review: https://reviews.apache.org/r/21175
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/63a51849
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/63a51849
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/63a51849
Branch: refs/heads/master
Commit: 63a51849217a563b58a6a271ac1d69e147562cc8
Parents: c418834
Author: Vinod Kone <vi...@twitter.com>
Authored: Thu May 8 13:54:23 2014 -0700
Committer: Vinod Kone <vi...@twitter.com>
Committed: Mon May 12 12:50:52 2014 -0700
----------------------------------------------------------------------
src/Makefile.am | 1 +
src/tests/master_tests.cpp | 79 ------------------
src/tests/reconciliation_tests.cpp | 144 ++++++++++++++++++++++++++++++++
3 files changed, 145 insertions(+), 79 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/63a51849/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 812ad2c..12374c4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -909,6 +909,7 @@ mesos_tests_SOURCES = \
tests/monitor_tests.cpp \
tests/paths_tests.cpp \
tests/protobuf_io_tests.cpp \
+ tests/reconciliation_tests.cpp \
tests/registrar_tests.cpp \
tests/repair_tests.cpp \
tests/resource_offers_tests.cpp \
http://git-wip-us.apache.org/repos/asf/mesos/blob/63a51849/src/tests/master_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_tests.cpp b/src/tests/master_tests.cpp
index 30ea7ff..639e760 100644
--- a/src/tests/master_tests.cpp
+++ b/src/tests/master_tests.cpp
@@ -958,85 +958,6 @@ TEST_F(MasterTest, MasterLost)
}
-// Test sends different state than current and expects an update with
-// the current state of task.
-//
-// TODO(nnielsen): Stubs have been left for future test, where test sends
-// expected state of non-existing task and an update with TASK_LOST should
-// be received. Also (not currently covered) if statuses are up to date,
-// nothing should happen.
-TEST_F(MasterTest, ReconcileTaskTest)
-{
- Try<PID<Master> > master = StartMaster();
- ASSERT_SOME(master);
-
- MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
- TestContainerizer containerizer(&exec);
-
- Try<PID<Slave> > slave = StartSlave(&containerizer);
- ASSERT_SOME(slave);
-
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
- Future<FrameworkID> frameworkId;
- EXPECT_CALL(sched, registered(&driver, _, _))
- .WillOnce(FutureArg<1>(&frameworkId));
-
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 512, "*"))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- EXPECT_CALL(exec, registered(_, _, _, _));
-
- EXPECT_CALL(exec, launchTask(_, _))
- .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
-
- Future<TaskStatus> status;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
-
- driver.start();
-
- AWAIT_READY(status);
- EXPECT_EQ(TASK_RUNNING, status.get().state());
-
- EXPECT_EQ(true, status.get().has_slave_id());
-
- const TaskID taskId = status.get().task_id();
- const SlaveID slaveId = status.get().slave_id();
-
- // If framwework has different state, current state should be reported.
- Future<TaskStatus> status2;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status2));
-
- vector<TaskStatus> statuses;
-
- TaskStatus differentStatus;
- differentStatus.mutable_task_id()->CopyFrom(taskId);
- differentStatus.mutable_slave_id()->CopyFrom(slaveId);
- differentStatus.set_state(TASK_KILLED);
-
- statuses.push_back(differentStatus);
-
- driver.reconcileTasks(statuses);
-
- AWAIT_READY(status2);
- EXPECT_EQ(TASK_RUNNING, status2.get().state());
-
- EXPECT_CALL(exec, shutdown(_))
- .Times(AtMost(1));
-
- driver.stop();
- driver.join();
-
- Shutdown(); // Must shutdown before 'containerizer' gets deallocated.
-}
-
-
// Test ensures two offers from same slave can be used for single task.
// This is done by first launching single task which utilize half of the
// available resources. A subsequent offer for the rest of the available
http://git-wip-us.apache.org/repos/asf/mesos/blob/63a51849/src/tests/reconciliation_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/reconciliation_tests.cpp b/src/tests/reconciliation_tests.cpp
new file mode 100644
index 0000000..e1c8a49
--- /dev/null
+++ b/src/tests/reconciliation_tests.cpp
@@ -0,0 +1,144 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include <gmock/gmock.h>
+
+#include <vector>
+
+#include <mesos/mesos.hpp>
+#include <mesos/scheduler.hpp>
+
+#include <process/future.hpp>
+#include <process/pid.hpp>
+#include <process/process.hpp>
+
+#include <stout/uuid.hpp>
+
+#include "common/protobuf_utils.hpp"
+
+#include "master/master.hpp"
+
+#include "slave/slave.hpp"
+
+#include "tests/containerizer.hpp"
+#include "tests/mesos.hpp"
+
+using namespace mesos;
+using namespace mesos::internal;
+using namespace mesos::internal::tests;
+
+using mesos::internal::master::Master;
+
+using mesos::internal::slave::Slave;
+
+using process::Future;
+using process::PID;
+
+using std::vector;
+
+using testing::_;
+using testing::AtMost;
+using testing::Return;
+
+
+class ReconciliationTest : public MesosTest {};
+
+
+// Test sends different state than current and expects an update with
+// the current state of task.
+//
+// TODO(nnielsen): Stubs have been left for future test, where test sends
+// expected state of non-existing task and an update with TASK_LOST should
+// be received. Also (not currently covered) if statuses are up to date,
+// nothing should happen.
+TEST_F(ReconciliationTest, TaskStateMismatch)
+{
+ Try<PID<Master> > master = StartMaster();
+ ASSERT_SOME(master);
+
+ MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+ TestContainerizer containerizer(&exec);
+
+ Try<PID<Slave> > slave = StartSlave(&containerizer);
+ ASSERT_SOME(slave);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
+
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 512, "*"))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+ EXPECT_CALL(exec, registered(_, _, _, _));
+
+ EXPECT_CALL(exec, launchTask(_, _))
+ .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
+
+ Future<TaskStatus> status;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status));
+
+ driver.start();
+
+ // Wait until the framework is registered.
+ AWAIT_READY(frameworkId);
+
+ AWAIT_READY(status);
+ EXPECT_EQ(TASK_RUNNING, status.get().state());
+
+ EXPECT_EQ(true, status.get().has_slave_id());
+
+ const TaskID taskId = status.get().task_id();
+ const SlaveID slaveId = status.get().slave_id();
+
+ // If framework has different state, current state should be reported.
+ Future<TaskStatus> status2;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status2));
+
+ vector<TaskStatus> statuses;
+
+ TaskStatus differentStatus;
+ differentStatus.mutable_task_id()->CopyFrom(taskId);
+ differentStatus.mutable_slave_id()->CopyFrom(slaveId);
+ differentStatus.set_state(TASK_KILLED);
+
+ statuses.push_back(differentStatus);
+
+ driver.reconcileTasks(statuses);
+
+ AWAIT_READY(status2);
+ EXPECT_EQ(TASK_RUNNING, status2.get().state());
+
+ EXPECT_CALL(exec, shutdown(_))
+ .Times(AtMost(1));
+
+ driver.stop();
+ driver.join();
+
+ Shutdown(); // Must shutdown before 'containerizer' gets deallocated.
+}
[2/2] git commit: Fixed tasks reconciliation and added tests.
Posted by vi...@apache.org.
Fixed tasks reconciliation and added tests.
Review: https://reviews.apache.org/r/21236
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/cceb071a
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/cceb071a
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/cceb071a
Branch: refs/heads/master
Commit: cceb071a0af8ea4ea2597cb5519c8c98fa43b7bc
Parents: 63a5184
Author: Vinod Kone <vi...@twitter.com>
Authored: Thu May 8 13:55:25 2014 -0700
Committer: Vinod Kone <vi...@twitter.com>
Committed: Mon May 12 12:50:53 2014 -0700
----------------------------------------------------------------------
src/master/master.cpp | 13 +-
src/master/master.hpp | 20 +-
src/tests/reconciliation_tests.cpp | 392 ++++++++++++++++++++++++++++++--
3 files changed, 389 insertions(+), 36 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/cceb071a/src/master/master.cpp
----------------------------------------------------------------------
diff --git a/src/master/master.cpp b/src/master/master.cpp
index d851a72..0499877 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -2584,8 +2584,8 @@ void Master::reconcileTasks(
return;
}
- LOG(INFO) << "Performing best-effort task state reconciliation for framework "
- << frameworkId;
+ LOG(INFO) << "Performing task state reconciliation for " << statuses.size()
+ << " task statuses of framework " << frameworkId;
// Reconciliation occurs for the following cases:
// (1) If the slave is unknown, we send TASK_LOST.
@@ -2609,13 +2609,13 @@ void Master::reconcileTasks(
!slaves.reregistering.contains(status.slave_id()) &&
!slaves.activated.contains(status.slave_id()) &&
!slaves.removing.contains(status.slave_id())) {
- // Slave is removed!
+ // Slave is unknown or removed!
update = protobuf::createStatusUpdate(
frameworkId,
status.slave_id(),
status.task_id(),
TASK_LOST,
- "Reconciliation: Slave is removed");
+ "Reconciliation: Slave is unknown/removed");
}
// Check for a known slave / task (cases (2) and (3)).
@@ -2645,7 +2645,10 @@ void Master::reconcileTasks(
}
if (update.isSome()) {
- statusUpdate(update.get(), UPID());
+ CHECK_NOTNULL(framework);
+ StatusUpdateMessage message;
+ message.mutable_update()->CopyFrom(update.get());
+ send(framework->pid, message);
}
}
}
http://git-wip-us.apache.org/repos/asf/mesos/blob/cceb071a/src/master/master.hpp
----------------------------------------------------------------------
diff --git a/src/master/master.hpp b/src/master/master.hpp
index 0a350b0..de2ae62 100644
--- a/src/master/master.hpp
+++ b/src/master/master.hpp
@@ -199,6 +199,18 @@ public:
// Made public for testing purposes.
process::Future<Nothing> _recover(const Registry& registry);
+ // Continuation of reregisterSlave().
+ // Made public for testing purposes.
+ // TODO(vinod): Instead of doing this create and use a
+ // MockRegistrar.
+ void _reregisterSlave(
+ const SlaveInfo& slaveInfo,
+ const process::UPID& pid,
+ const std::vector<ExecutorInfo>& executorInfos,
+ const std::vector<Task>& tasks,
+ const std::vector<Archive::Framework>& completedFrameworks,
+ const process::Future<bool>& readmit);
+
MasterInfo info() const
{
return info_;
@@ -219,14 +231,6 @@ protected:
const process::UPID& pid,
const process::Future<bool>& admit);
- void _reregisterSlave(
- const SlaveInfo& slaveInfo,
- const process::UPID& pid,
- const std::vector<ExecutorInfo>& executorInfos,
- const std::vector<Task>& tasks,
- const std::vector<Archive::Framework>& completedFrameworks,
- const process::Future<bool>& readmit);
-
void __reregisterSlave(
Slave* slave,
const std::vector<Task>& tasks);
http://git-wip-us.apache.org/repos/asf/mesos/blob/cceb071a/src/tests/reconciliation_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/reconciliation_tests.cpp b/src/tests/reconciliation_tests.cpp
index e1c8a49..677d18e 100644
--- a/src/tests/reconciliation_tests.cpp
+++ b/src/tests/reconciliation_tests.cpp
@@ -26,6 +26,7 @@
#include <mesos/mesos.hpp>
#include <mesos/scheduler.hpp>
+#include <process/clock.hpp>
#include <process/future.hpp>
#include <process/pid.hpp>
#include <process/process.hpp>
@@ -34,6 +35,7 @@
#include "common/protobuf_utils.hpp"
+#include "master/flags.hpp"
#include "master/master.hpp"
#include "slave/slave.hpp"
@@ -49,6 +51,7 @@ using mesos::internal::master::Master;
using mesos::internal::slave::Slave;
+using process::Clock;
using process::Future;
using process::PID;
@@ -62,13 +65,9 @@ using testing::Return;
class ReconciliationTest : public MesosTest {};
-// Test sends different state than current and expects an update with
-// the current state of task.
-//
-// TODO(nnielsen): Stubs have been left for future test, where test sends
-// expected state of non-existing task and an update with TASK_LOST should
-// be received. Also (not currently covered) if statuses are up to date,
-// nothing should happen.
+// This test verifies that task state reconciliation for a task
+// whose state differs between framework and master results in a
+// status update.
TEST_F(ReconciliationTest, TaskStateMismatch)
{
Try<PID<Master> > master = StartMaster();
@@ -98,41 +97,41 @@ TEST_F(ReconciliationTest, TaskStateMismatch)
EXPECT_CALL(exec, launchTask(_, _))
.WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
- Future<TaskStatus> status;
+ Future<TaskStatus> update;
EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
+ .WillOnce(FutureArg<1>(&update));
driver.start();
// Wait until the framework is registered.
AWAIT_READY(frameworkId);
- AWAIT_READY(status);
- EXPECT_EQ(TASK_RUNNING, status.get().state());
+ AWAIT_READY(update);
+ EXPECT_EQ(TASK_RUNNING, update.get().state());
- EXPECT_EQ(true, status.get().has_slave_id());
+ EXPECT_EQ(true, update.get().has_slave_id());
- const TaskID taskId = status.get().task_id();
- const SlaveID slaveId = status.get().slave_id();
+ const TaskID taskId = update.get().task_id();
+ const SlaveID slaveId = update.get().slave_id();
// If framework has different state, current state should be reported.
- Future<TaskStatus> status2;
+ Future<TaskStatus> update2;
EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status2));
+ .WillOnce(FutureArg<1>(&update2));
vector<TaskStatus> statuses;
- TaskStatus differentStatus;
- differentStatus.mutable_task_id()->CopyFrom(taskId);
- differentStatus.mutable_slave_id()->CopyFrom(slaveId);
- differentStatus.set_state(TASK_KILLED);
+ TaskStatus status;
+ status.mutable_task_id()->CopyFrom(taskId);
+ status.mutable_slave_id()->CopyFrom(slaveId);
+ status.set_state(TASK_KILLED);
- statuses.push_back(differentStatus);
+ statuses.push_back(status);
driver.reconcileTasks(statuses);
- AWAIT_READY(status2);
- EXPECT_EQ(TASK_RUNNING, status2.get().state());
+ AWAIT_READY(update2);
+ EXPECT_EQ(TASK_RUNNING, update2.get().state());
EXPECT_CALL(exec, shutdown(_))
.Times(AtMost(1));
@@ -142,3 +141,350 @@ TEST_F(ReconciliationTest, TaskStateMismatch)
Shutdown(); // Must shutdown before 'containerizer' gets deallocated.
}
+
+
+// This test verifies that task state reconciliation for a task
+// whose state does not differ between framework and master does not
+// result in a status update.
+TEST_F(ReconciliationTest, TaskStateMatch)
+{
+ Try<PID<Master> > master = StartMaster();
+ ASSERT_SOME(master);
+
+ MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+ TestContainerizer containerizer(&exec);
+
+ Try<PID<Slave> > slave = StartSlave(&containerizer);
+ ASSERT_SOME(slave);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
+
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 1, 512, "*"))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+ EXPECT_CALL(exec, registered(_, _, _, _));
+
+ EXPECT_CALL(exec, launchTask(_, _))
+ .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
+
+ Future<TaskStatus> update;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&update));
+
+ driver.start();
+
+ // Wait until the framework is registered.
+ AWAIT_READY(frameworkId);
+
+ AWAIT_READY(update);
+ EXPECT_EQ(TASK_RUNNING, update.get().state());
+
+ EXPECT_EQ(true, update.get().has_slave_id());
+
+ const TaskID taskId = update.get().task_id();
+ const SlaveID slaveId = update.get().slave_id();
+
+ // Framework should not receive a status udpate.
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .Times(0);
+
+ vector<TaskStatus> statuses;
+
+ TaskStatus status;
+ status.mutable_task_id()->CopyFrom(taskId);
+ status.mutable_slave_id()->CopyFrom(slaveId);
+ status.set_state(TASK_RUNNING);
+
+ statuses.push_back(status);
+
+ Future<ReconcileTasksMessage> reconcileTasksMessage =
+ FUTURE_PROTOBUF(ReconcileTasksMessage(), _ , _);
+
+ Clock::pause();
+
+ driver.reconcileTasks(statuses);
+
+ // Make sure the master received the reconcile tasks message.
+ AWAIT_READY(reconcileTasksMessage);
+
+ // The Clock::settle() will ensure that framework would receive
+ // a status update if it is sent by the master. In this test it
+ // shouldn't receive any.
+ Clock::settle();
+
+ EXPECT_CALL(exec, shutdown(_))
+ .Times(AtMost(1));
+
+ driver.stop();
+ driver.join();
+
+ Shutdown(); // Must shutdown before 'containerizer' gets deallocated.
+}
+
+
+// This test verifies that reconciliation of a task that belongs to an
+// unknown slave results in TASK_LOST.
+TEST_F(ReconciliationTest, UnknownSlave)
+{
+ Try<PID<Master> > master = StartMaster();
+ ASSERT_SOME(master);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
+
+ driver.start();
+
+ // Wait until the framework is registered.
+ AWAIT_READY(frameworkId);
+
+ Future<TaskStatus> update;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&update));
+
+ vector<TaskStatus> statuses;
+
+ // Create a task status with a random slave id (and task id).
+ TaskStatus status;
+ status.mutable_task_id()->set_value(UUID::random().toString());
+ status.mutable_slave_id()->set_value(UUID::random().toString());
+ status.set_state(TASK_RUNNING);
+
+ statuses.push_back(status);
+
+ driver.reconcileTasks(statuses);
+
+ // Framework should receive TASK_LOST because the slave is unknown.
+ AWAIT_READY(update);
+ EXPECT_EQ(TASK_LOST, update.get().state());
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}
+
+
+// This test verifies that reconciliation of a task that belongs to an
+// unknown slave but with non-strict registry doesn't result in a
+// status update.
+TEST_F(ReconciliationTest, UnknownSlaveNonStrictRegistry)
+{
+ master::Flags flags = CreateMasterFlags();
+ flags.registry_strict = false; // Non-strict registry.
+ Try<PID<Master> > master = StartMaster(flags);
+ ASSERT_SOME(master);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
+
+ driver.start();
+
+ // Wait until the framework is registered.
+ AWAIT_READY(frameworkId);
+
+ // Framework should not receive any update.
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .Times(0);
+
+ vector<TaskStatus> statuses;
+
+ // Create a task status with a random slave id (and task id).
+ TaskStatus status;
+ status.mutable_task_id()->set_value(UUID::random().toString());
+ status.mutable_slave_id()->set_value(UUID::random().toString());
+ status.set_state(TASK_RUNNING);
+
+ statuses.push_back(status);
+
+ Future<ReconcileTasksMessage> reconcileTasksMessage =
+ FUTURE_PROTOBUF(ReconcileTasksMessage(), _ , _);
+
+ Clock::pause();
+
+ driver.reconcileTasks(statuses);
+
+ // Make sure the master received the reconcile tasks message.
+ AWAIT_READY(reconcileTasksMessage);
+
+ // The Clock::settle() will ensure that framework would receive
+ // a status update if it is sent by the master. In this test it
+ // shouldn't receive any.
+ Clock::settle();
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}
+
+
+// This test verifies that reconciliation of an unknown task that
+// belongs to a known slave results in TASK_LOST.
+TEST_F(ReconciliationTest, UnknownTask)
+{
+ Try<PID<Master> > master = StartMaster();
+ ASSERT_SOME(master);
+
+ Future<SlaveRegisteredMessage> slaveRegisteredMessage =
+ FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
+
+ Try<PID<Slave> > slave = StartSlave();
+ ASSERT_SOME(slave);
+
+ // Wait for the slave to register and get the slave id.
+ AWAIT_READY(slaveRegisteredMessage);
+ const SlaveID slaveId = slaveRegisteredMessage.get().slave_id();
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
+
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillRepeatedly(Return()); // Ignore offers.
+
+ driver.start();
+
+ // Wait until the framework is registered.
+ AWAIT_READY(frameworkId);
+
+ Future<TaskStatus> update;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&update));
+
+ vector<TaskStatus> statuses;
+
+ // Create a task status with a random task id.
+ TaskStatus status;
+ status.mutable_task_id()->set_value(UUID::random().toString());
+ status.mutable_slave_id()->CopyFrom(slaveId);
+ status.set_state(TASK_RUNNING);
+
+ statuses.push_back(status);
+
+ driver.reconcileTasks(statuses);
+
+ // Framework should receive TASK_LOST for unknown task.
+ AWAIT_READY(update);
+ EXPECT_EQ(TASK_LOST, update.get().state());
+
+ driver.stop();
+ driver.join();
+
+ Shutdown(); // Must shutdown before 'containerizer' gets deallocated.
+}
+
+
+// This test verifies that reconciliation of a task that belongs to a
+// slave that is a transitional state doesn't result in an update.
+TEST_F(ReconciliationTest, SlaveInTransition)
+{
+ master::Flags masterFlags = CreateMasterFlags();
+ Try<PID<Master> > master = StartMaster();
+ ASSERT_SOME(master);
+
+ // Start a checkpointing slave.
+ slave::Flags slaveFlags = CreateSlaveFlags();
+ slaveFlags.checkpoint = true;
+
+ Future<SlaveRegisteredMessage> slaveRegisteredMessage =
+ FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
+
+ Try<PID<Slave> > slave = StartSlave(slaveFlags);
+ ASSERT_SOME(slave);
+
+ // Wait for the slave to register and get the slave id.
+ AWAIT_READY(slaveRegisteredMessage);
+ const SlaveID slaveId = slaveRegisteredMessage.get().slave_id();
+
+ // Stop the master and slave.
+ Stop(master.get());
+ Stop(slave.get());
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ Future<FrameworkID> frameworkId;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureArg<1>(&frameworkId));
+
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillRepeatedly(Return()); // Ignore offers.
+
+ // Framework should not receive any update.
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .Times(0);
+
+ // Drop '&Master::_reregisterSlave' dispatch so that the slave is
+ // in 'reregistering' state.
+ Future<Nothing> _reregisterSlave =
+ DROP_DISPATCH(_, &Master::_reregisterSlave);
+
+ // Restart the master.
+ master = StartMaster(masterFlags);
+ ASSERT_SOME(master);
+
+ driver.start();
+
+ // Wait for the framework to register.
+ AWAIT_READY(frameworkId);
+
+ // Restart the slave.
+ slave = StartSlave(slaveFlags);
+ ASSERT_SOME(slave);
+
+ // Slave will be in 'reregistering' state here.
+ AWAIT_READY(_reregisterSlave);
+
+ vector<TaskStatus> statuses;
+
+ // Create a task status with a random task id.
+ TaskStatus status;
+ status.mutable_task_id()->set_value(UUID::random().toString());
+ status.mutable_slave_id()->CopyFrom(slaveId);
+ status.set_state(TASK_RUNNING);
+
+ statuses.push_back(status);
+
+ Future<ReconcileTasksMessage> reconcileTasksMessage =
+ FUTURE_PROTOBUF(ReconcileTasksMessage(), _ , _);
+
+ Clock::pause();
+
+ driver.reconcileTasks(statuses);
+
+ // Make sure the master received the reconcile tasks message.
+ AWAIT_READY(reconcileTasksMessage);
+
+ // The Clock::settle() will ensure that framework would receive
+ // a status update if it is sent by the master. In this test it
+ // shouldn't receive any.
+ Clock::settle();
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}