You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2015/03/05 01:18:38 UTC
[2/2] mesos git commit: Moved task validation tests to
master_validation_tests.cpp.
Moved task validation tests to master_validation_tests.cpp.
Review: https://reviews.apache.org/r/31701
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/79d21398
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/79d21398
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/79d21398
Branch: refs/heads/master
Commit: 79d213986cbcd898bb38e26ed9cf84773b818b6b
Parents: d8b36f1
Author: Jie Yu <yu...@gmail.com>
Authored: Tue Mar 3 11:42:49 2015 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Mar 4 16:18:21 2015 -0800
----------------------------------------------------------------------
src/tests/master_validation_tests.cpp | 623 ++++++++++++++++++++++++++++-
src/tests/resource_offers_tests.cpp | 606 +---------------------------
2 files changed, 624 insertions(+), 605 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/79d21398/src/tests/master_validation_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_validation_tests.cpp b/src/tests/master_validation_tests.cpp
index 38ddb1d..70ec6db 100644
--- a/src/tests/master_validation_tests.cpp
+++ b/src/tests/master_validation_tests.cpp
@@ -16,23 +16,45 @@
* limitations under the License.
*/
+#include <gmock/gmock.h>
+
#include <google/protobuf/repeated_field.h>
-#include <gtest/gtest.h>
+#include <vector>
+#include <mesos/executor.hpp>
#include <mesos/mesos.hpp>
#include <mesos/resources.hpp>
+#include <mesos/scheduler.hpp>
#include <stout/gtest.hpp>
+#include <stout/strings.hpp>
+#include <stout/uuid.hpp>
+#include "master/master.hpp"
#include "master/validation.hpp"
+#include "slave/slave.hpp"
+
#include "tests/mesos.hpp"
using namespace mesos::internal::master::validation;
using google::protobuf::RepeatedPtrField;
+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;
+
namespace mesos {
namespace internal {
namespace tests {
@@ -200,6 +222,605 @@ TEST_F(DestroyOperationValidationTest, UnknownPersistentVolume)
EXPECT_SOME(operation::validate(destroy, Resources()));
}
+
+// TODO(jieyu): All of the task validation tests have the same flow:
+// launch a task, expect an update of a particular format (invalid w/
+// message). Consider providing common functionalities in the test
+// fixture to avoid code bloat. Ultimately, we should make task or
+// offer validation unit testable.
+class TaskValidationTest : public MesosTest {};
+
+
+TEST_F(TaskValidationTest, TaskUsesInvalidFrameworkID)
+{
+ Try<PID<Master>> master = StartMaster();
+ ASSERT_SOME(master);
+
+ Try<PID<Slave>> slave = StartSlave();
+ ASSERT_SOME(slave);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched, registered(&driver, _, _));
+
+ // Create an executor with a random framework id.
+ ExecutorInfo executor;
+ executor = DEFAULT_EXECUTOR_INFO;
+ executor.mutable_framework_id()->set_value(UUID::random().toString());
+
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(LaunchTasks(executor, 1, 1, 16, "*"))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+ Future<TaskStatus> status;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status));
+
+ driver.start();
+
+ AWAIT_READY(status);
+ EXPECT_EQ(TASK_ERROR, status.get().state());
+ EXPECT_TRUE(strings::startsWith(
+ status.get().message(), "ExecutorInfo has an invalid FrameworkID"));
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}
+
+
+TEST_F(TaskValidationTest, TaskUsesCommandInfoAndExecutorInfo)
+{
+ Try<PID<Master>> master = StartMaster();
+ ASSERT_SOME(master);
+
+ Try<PID<Slave>> slave = StartSlave();
+ ASSERT_SOME(slave);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched, registered(&driver, _, _));
+
+ Future<vector<Offer>> offers;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+ driver.start();
+
+ AWAIT_READY(offers);
+ EXPECT_NE(0u, offers.get().size());
+
+ Future<TaskStatus> status;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status));
+
+ // Create a task that uses both command info and task info.
+ TaskInfo task = createTask(offers.get()[0], ""); // Command task.
+ task.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO); // Executor task.
+
+ vector<TaskInfo> tasks;
+ tasks.push_back(task);
+
+ driver.launchTasks(offers.get()[0].id(), tasks);
+
+ AWAIT_READY(status);
+ EXPECT_EQ(TASK_ERROR, status.get().state());
+ EXPECT_TRUE(strings::contains(
+ status.get().message(), "CommandInfo or ExecutorInfo present"));
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}
+
+
+TEST_F(TaskValidationTest, TaskUsesNoResources)
+{
+ Try<PID<Master>> master = StartMaster();
+ ASSERT_SOME(master);
+
+ Try<PID<Slave>> slave = StartSlave();
+ ASSERT_SOME(slave);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .Times(1);
+
+ Future<vector<Offer>> offers;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+ driver.start();
+
+ AWAIT_READY(offers);
+ EXPECT_NE(0u, offers.get().size());
+
+ TaskInfo task;
+ task.set_name("");
+ task.mutable_task_id()->set_value("1");
+ task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
+ task.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
+
+ vector<TaskInfo> tasks;
+ tasks.push_back(task);
+
+ Future<TaskStatus> status;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status));
+
+ driver.launchTasks(offers.get()[0].id(), tasks);
+
+ AWAIT_READY(status);
+ EXPECT_EQ(task.task_id(), status.get().task_id());
+ EXPECT_EQ(TASK_ERROR, status.get().state());
+ EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status.get().reason());
+ EXPECT_TRUE(status.get().has_message());
+ EXPECT_EQ("Task uses no resources", status.get().message());
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}
+
+
+TEST_F(TaskValidationTest, TaskUsesMoreResourcesThanOffered)
+{
+ Try<PID<Master>> master = StartMaster();
+ ASSERT_SOME(master);
+
+ Try<PID<Slave>> slave = StartSlave();
+ ASSERT_SOME(slave);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .Times(1);
+
+ Future<vector<Offer>> offers;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+ driver.start();
+
+ AWAIT_READY(offers);
+ EXPECT_NE(0u, offers.get().size());
+
+ TaskInfo task;
+ task.set_name("");
+ task.mutable_task_id()->set_value("1");
+ task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
+ task.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
+
+ Resource* cpus = task.add_resources();
+ cpus->set_name("cpus");
+ cpus->set_type(Value::SCALAR);
+ cpus->mutable_scalar()->set_value(2.01);
+
+ vector<TaskInfo> tasks;
+ tasks.push_back(task);
+
+ Future<TaskStatus> status;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status));
+
+ driver.launchTasks(offers.get()[0].id(), tasks);
+
+ AWAIT_READY(status);
+
+ EXPECT_EQ(task.task_id(), status.get().task_id());
+ EXPECT_EQ(TASK_ERROR, status.get().state());
+ EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status.get().reason());
+ EXPECT_TRUE(status.get().has_message());
+ EXPECT_TRUE(strings::contains(
+ status.get().message(), "Task uses more resources"));
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}
+
+
+// This test verifies that if two tasks are launched with the same
+// task ID, the second task will get rejected.
+TEST_F(TaskValidationTest, DuplicatedTaskID)
+{
+ Try<PID<Master>> master = StartMaster();
+ ASSERT_SOME(master);
+
+ MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+ Try<PID<Slave>> slave = StartSlave(&exec);
+ ASSERT_SOME(slave);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched, registered(&driver, _, _));
+
+ Future<vector<Offer>> offers;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+ driver.start();
+
+ AWAIT_READY(offers);
+ EXPECT_NE(0u, offers.get().size());
+
+ ExecutorInfo executor;
+ executor.mutable_executor_id()->set_value("default");
+ executor.mutable_command()->set_value("exit 1");
+
+ // Create two tasks with the same id.
+ TaskInfo task1;
+ task1.set_name("");
+ task1.mutable_task_id()->set_value("1");
+ task1.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
+ task1.mutable_resources()->MergeFrom(Resources::parse("cpus:1;mem:32").get());
+ task1.mutable_executor()->MergeFrom(executor);
+
+ TaskInfo task2;
+ task2.set_name("");
+ task2.mutable_task_id()->set_value("1");
+ task2.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
+ task2.mutable_resources()->MergeFrom(Resources::parse("cpus:1;mem:32").get());
+ task2.mutable_executor()->MergeFrom(executor);
+
+ vector<TaskInfo> tasks;
+ tasks.push_back(task1);
+ tasks.push_back(task2);
+
+ EXPECT_CALL(exec, registered(_, _, _, _));
+
+ // Grab the first task but don't send a status update.
+ Future<TaskInfo> task;
+ EXPECT_CALL(exec, launchTask(_, _))
+ .WillOnce(FutureArg<1>(&task));
+
+ Future<TaskStatus> status;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status));
+
+ driver.launchTasks(offers.get()[0].id(), tasks);
+
+ AWAIT_READY(task);
+ EXPECT_EQ(task1.task_id(), task.get().task_id());
+
+ AWAIT_READY(status);
+ EXPECT_EQ(TASK_ERROR, status.get().state());
+ EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status.get().reason());
+
+ EXPECT_TRUE(strings::startsWith(
+ status.get().message(), "Task has duplicate ID"));
+
+ EXPECT_CALL(exec, shutdown(_))
+ .Times(AtMost(1));
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}
+
+
+// This test verifies that two tasks launched on the same slave with
+// the same executor id but different executor info are rejected.
+TEST_F(TaskValidationTest, ExecutorInfoDiffersOnSameSlave)
+{
+ Try<PID<Master>> master = StartMaster();
+ ASSERT_SOME(master);
+
+ MockExecutor exec(DEFAULT_EXECUTOR_ID);
+
+ Try<PID<Slave>> slave = StartSlave(&exec);
+ ASSERT_SOME(slave);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .Times(1);
+
+ Future<vector<Offer>> offers;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+ driver.start();
+
+ AWAIT_READY(offers);
+ EXPECT_NE(0u, offers.get().size());
+
+ ExecutorInfo executor;
+ executor.mutable_executor_id()->set_value("default");
+ executor.mutable_command()->set_value("exit 1");
+
+ TaskInfo task1;
+ task1.set_name("");
+ task1.mutable_task_id()->set_value("1");
+ task1.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
+ task1.mutable_resources()->MergeFrom(
+ Resources::parse("cpus:1;mem:512").get());
+ task1.mutable_executor()->MergeFrom(executor);
+
+ executor.mutable_command()->set_value("exit 2");
+
+ TaskInfo task2;
+ task2.set_name("");
+ task2.mutable_task_id()->set_value("2");
+ task2.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
+ task2.mutable_resources()->MergeFrom(
+ Resources::parse("cpus:1;mem:512").get());
+ task2.mutable_executor()->MergeFrom(executor);
+
+ vector<TaskInfo> tasks;
+ tasks.push_back(task1);
+ tasks.push_back(task2);
+
+ EXPECT_CALL(exec, registered(_, _, _, _))
+ .Times(1);
+
+ // Grab the "good" task but don't send a status update.
+ Future<TaskInfo> task;
+ EXPECT_CALL(exec, launchTask(_, _))
+ .WillOnce(FutureArg<1>(&task));
+
+ Future<TaskStatus> status;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status));
+
+ driver.launchTasks(offers.get()[0].id(), tasks);
+
+ AWAIT_READY(task);
+ EXPECT_EQ(task1.task_id(), task.get().task_id());
+
+ AWAIT_READY(status);
+ EXPECT_EQ(task2.task_id(), status.get().task_id());
+ EXPECT_EQ(TASK_ERROR, status.get().state());
+ EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status.get().reason());
+ EXPECT_TRUE(status.get().has_message());
+ EXPECT_TRUE(strings::contains(
+ status.get().message(), "Task has invalid ExecutorInfo"));
+
+ EXPECT_CALL(exec, shutdown(_))
+ .Times(AtMost(1));
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}
+
+
+// This test verifies that two tasks each launched on a different
+// slave with same executor id but different executor info are
+// allowed.
+TEST_F(TaskValidationTest, ExecutorInfoDiffersOnDifferentSlaves)
+{
+ Try<PID<Master>> master = StartMaster();
+ ASSERT_SOME(master);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+ Future<Nothing> registered;
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .WillOnce(FutureSatisfy(®istered));
+
+ driver.start();
+
+ AWAIT_READY(registered);
+
+ Future<vector<Offer>> offers1;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers1));
+
+ // Start the first slave.
+ MockExecutor exec1(DEFAULT_EXECUTOR_ID);
+
+ Try<PID<Slave>> slave1 = StartSlave(&exec1);
+ ASSERT_SOME(slave1);
+
+ AWAIT_READY(offers1);
+ EXPECT_NE(0u, offers1.get().size());
+
+ // Launch the first task with the default executor id.
+ ExecutorInfo executor1;
+ executor1 = DEFAULT_EXECUTOR_INFO;
+ executor1.mutable_command()->set_value("exit 1");
+
+ TaskInfo task1 = createTask(
+ offers1.get()[0], executor1.command().value(), executor1.executor_id());
+
+ vector<TaskInfo> tasks1;
+ tasks1.push_back(task1);
+
+ EXPECT_CALL(exec1, registered(_, _, _, _))
+ .Times(1);
+
+ EXPECT_CALL(exec1, launchTask(_, _))
+ .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
+
+ Future<TaskStatus> status1;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status1));
+
+ driver.launchTasks(offers1.get()[0].id(), tasks1);
+
+ AWAIT_READY(status1);
+ ASSERT_EQ(TASK_RUNNING, status1.get().state());
+
+ Future<vector<Offer>> offers2;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers2))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+ // Now start the second slave.
+ MockExecutor exec2(DEFAULT_EXECUTOR_ID);
+
+ Try<PID<Slave>> slave2 = StartSlave(&exec2);
+ ASSERT_SOME(slave2);
+
+ AWAIT_READY(offers2);
+ EXPECT_NE(0u, offers2.get().size());
+
+ // Now launch the second task with the same executor id but
+ // a different executor command.
+ ExecutorInfo executor2;
+ executor2 = executor1;
+ executor2.mutable_command()->set_value("exit 2");
+
+ TaskInfo task2 = createTask(
+ offers2.get()[0], executor2.command().value(), executor2.executor_id());
+
+ vector<TaskInfo> tasks2;
+ tasks2.push_back(task2);
+
+ EXPECT_CALL(exec2, registered(_, _, _, _))
+ .Times(1);
+
+ EXPECT_CALL(exec2, launchTask(_, _))
+ .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
+
+ Future<TaskStatus> status2;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status2));
+
+ driver.launchTasks(offers2.get()[0].id(), tasks2);
+
+ AWAIT_READY(status2);
+ ASSERT_EQ(TASK_RUNNING, status2.get().state());
+
+ EXPECT_CALL(exec1, shutdown(_))
+ .Times(AtMost(1));
+
+ EXPECT_CALL(exec2, shutdown(_))
+ .Times(AtMost(1));
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}
+
+
+// This test ensures that a persistent volume that is larger than the
+// offered disk resources results in a failed task.
+TEST_F(TaskValidationTest, DISABLED_AcquirePersistentDiskTooBig)
+{
+ // Create a framework with role "role1";
+ FrameworkInfo frameworkInfo;
+ frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+ frameworkInfo.set_role("role1");
+
+ // Setup ACLs in order to receive offers for "role1".
+ ACLs acls;
+ mesos::ACL::RegisterFramework* acl = acls.add_register_frameworks();
+ acl->mutable_principals()->add_values(frameworkInfo.principal());
+ acl->mutable_roles()->add_values(frameworkInfo.role());
+
+ // Start master with ACLs.
+ master::Flags masterFlags = CreateMasterFlags();
+ masterFlags.roles = frameworkInfo.role();
+ masterFlags.acls = acls;
+
+ Try<PID<Master>> master = StartMaster(masterFlags);
+ ASSERT_SOME(master);
+
+ slave::Flags slaveFlags = CreateSlaveFlags();
+ slaveFlags.resources = "cpus(*):4;mem(*):2048;disk(role1):1024";
+
+ Try<PID<Slave>> slave = StartSlave(slaveFlags);
+ ASSERT_SOME(slave);
+
+ MockScheduler sched;
+ MesosSchedulerDriver driver(
+ &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
+
+ EXPECT_CALL(sched, registered(&driver, _, _))
+ .Times(1);
+
+ Future<vector<Offer>> offers;
+ EXPECT_CALL(sched, resourceOffers(&driver, _))
+ .WillOnce(FutureArg<1>(&offers))
+ .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+ driver.start();
+
+ AWAIT_READY(offers);
+ EXPECT_NE(0u, offers.get().size());
+
+ // Create a persistent volume whose size is larger than the size of
+ // the offered disk.
+ Resource diskResource = Resources::parse("disk", "2048", "role1").get();
+ diskResource.mutable_disk()->CopyFrom(createDiskInfo("1", "1"));
+
+ // Include other resources in task resources.
+ Resources taskResources =
+ Resources::parse("cpus:1;mem:128").get() + diskResource;
+
+ Offer offer = offers.get()[0];
+ TaskInfo task =
+ createTask(offer.slave_id(), taskResources, "", DEFAULT_EXECUTOR_ID);
+
+ vector<TaskInfo> tasks;
+ tasks.push_back(task);
+
+ Future<TaskStatus> status;
+ EXPECT_CALL(sched, statusUpdate(&driver, _))
+ .WillOnce(FutureArg<1>(&status));
+
+ driver.launchTasks(offer.id(), tasks);
+
+ AWAIT_READY(status);
+ EXPECT_EQ(task.task_id(), status.get().task_id());
+ EXPECT_EQ(TASK_ERROR, status.get().state());
+ EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status.get().reason());
+ EXPECT_TRUE(status.get().has_message());
+ EXPECT_TRUE(strings::contains(
+ status.get().message(), "Failed to create persistent volumes"));
+
+ driver.stop();
+ driver.join();
+
+ Shutdown();
+}
+
+// TODO(jieyu): Add tests for checking duplicated persistence ID
+// against offered resources.
+
+// TODO(jieyu): Add tests for checking duplicated persistence ID
+// across task and executors.
+
+// TODO(jieyu): Add tests for checking duplicated persistence ID
+// within an executor.
+
+// TODO(benh): Add tests for checking correct slave IDs.
+
+// TODO(benh): Add tests for checking executor resource usage.
+
+// TODO(benh): Add tests which launch multiple tasks and check for
+// aggregate resource usage.
+
} // namespace tests {
} // namespace internal {
} // namespace mesos {
http://git-wip-us.apache.org/repos/asf/mesos/blob/79d21398/src/tests/resource_offers_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/resource_offers_tests.cpp b/src/tests/resource_offers_tests.cpp
index d1eb2fc..882a9ff 100644
--- a/src/tests/resource_offers_tests.cpp
+++ b/src/tests/resource_offers_tests.cpp
@@ -16,15 +16,14 @@
* limitations under the License.
*/
-#include <gmock/gmock.h>
-
#include <vector>
+#include <gmock/gmock.h>
+
#include <mesos/executor.hpp>
#include <mesos/scheduler.hpp>
#include <stout/strings.hpp>
-#include <stout/uuid.hpp>
#include "master/master.hpp"
@@ -44,613 +43,12 @@ using process::PID;
using std::vector;
using testing::_;
-using testing::AtMost;
-using testing::Return;
namespace mesos {
namespace internal {
namespace tests {
-// TODO(jieyu): All of the task validation tests have the same flow:
-// launch a task, expect an update of a particular format (invalid w/
-// message). Consider providing common functionalities in the test
-// fixture to avoid code bloat. Ultimately, we should make task or
-// offer validation unit testable.
-class TaskValidationTest : public MesosTest {};
-
-
-TEST_F(TaskValidationTest, TaskUsesInvalidFrameworkID)
-{
- Try<PID<Master>> master = StartMaster();
- ASSERT_SOME(master);
-
- Try<PID<Slave>> slave = StartSlave();
- ASSERT_SOME(slave);
-
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
- EXPECT_CALL(sched, registered(&driver, _, _));
-
- // Create an executor with a random framework id.
- ExecutorInfo executor;
- executor = DEFAULT_EXECUTOR_INFO;
- executor.mutable_framework_id()->set_value(UUID::random().toString());
-
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(LaunchTasks(executor, 1, 1, 16, "*"))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- Future<TaskStatus> status;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
-
- driver.start();
-
- AWAIT_READY(status);
- EXPECT_EQ(TASK_ERROR, status.get().state());
- EXPECT_TRUE(strings::startsWith(
- status.get().message(), "ExecutorInfo has an invalid FrameworkID"));
-
- driver.stop();
- driver.join();
-
- Shutdown();
-}
-
-
-TEST_F(TaskValidationTest, TaskUsesCommandInfoAndExecutorInfo)
-{
- Try<PID<Master>> master = StartMaster();
- ASSERT_SOME(master);
-
- Try<PID<Slave>> slave = StartSlave();
- ASSERT_SOME(slave);
-
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
- EXPECT_CALL(sched, registered(&driver, _, _));
-
- Future<vector<Offer>> offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- driver.start();
-
- AWAIT_READY(offers);
- EXPECT_NE(0u, offers.get().size());
-
- Future<TaskStatus> status;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
-
- // Create a task that uses both command info and task info.
- TaskInfo task = createTask(offers.get()[0], ""); // Command task.
- task.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO); // Executor task.
-
- vector<TaskInfo> tasks;
- tasks.push_back(task);
-
- driver.launchTasks(offers.get()[0].id(), tasks);
-
- AWAIT_READY(status);
- EXPECT_EQ(TASK_ERROR, status.get().state());
- EXPECT_TRUE(strings::contains(
- status.get().message(), "CommandInfo or ExecutorInfo present"));
-
- driver.stop();
- driver.join();
-
- Shutdown();
-}
-
-
-TEST_F(TaskValidationTest, TaskUsesNoResources)
-{
- Try<PID<Master>> master = StartMaster();
- ASSERT_SOME(master);
-
- Try<PID<Slave>> slave = StartSlave();
- ASSERT_SOME(slave);
-
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
- EXPECT_CALL(sched, registered(&driver, _, _))
- .Times(1);
-
- Future<vector<Offer>> offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- driver.start();
-
- AWAIT_READY(offers);
- EXPECT_NE(0u, offers.get().size());
-
- TaskInfo task;
- task.set_name("");
- task.mutable_task_id()->set_value("1");
- task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
- task.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
-
- vector<TaskInfo> tasks;
- tasks.push_back(task);
-
- Future<TaskStatus> status;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
-
- driver.launchTasks(offers.get()[0].id(), tasks);
-
- AWAIT_READY(status);
- EXPECT_EQ(task.task_id(), status.get().task_id());
- EXPECT_EQ(TASK_ERROR, status.get().state());
- EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status.get().reason());
- EXPECT_TRUE(status.get().has_message());
- EXPECT_EQ("Task uses no resources", status.get().message());
-
- driver.stop();
- driver.join();
-
- Shutdown();
-}
-
-
-TEST_F(TaskValidationTest, TaskUsesMoreResourcesThanOffered)
-{
- Try<PID<Master>> master = StartMaster();
- ASSERT_SOME(master);
-
- Try<PID<Slave>> slave = StartSlave();
- ASSERT_SOME(slave);
-
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
- EXPECT_CALL(sched, registered(&driver, _, _))
- .Times(1);
-
- Future<vector<Offer>> offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- driver.start();
-
- AWAIT_READY(offers);
- EXPECT_NE(0u, offers.get().size());
-
- TaskInfo task;
- task.set_name("");
- task.mutable_task_id()->set_value("1");
- task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
- task.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
-
- Resource* cpus = task.add_resources();
- cpus->set_name("cpus");
- cpus->set_type(Value::SCALAR);
- cpus->mutable_scalar()->set_value(2.01);
-
- vector<TaskInfo> tasks;
- tasks.push_back(task);
-
- Future<TaskStatus> status;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
-
- driver.launchTasks(offers.get()[0].id(), tasks);
-
- AWAIT_READY(status);
-
- EXPECT_EQ(task.task_id(), status.get().task_id());
- EXPECT_EQ(TASK_ERROR, status.get().state());
- EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status.get().reason());
- EXPECT_TRUE(status.get().has_message());
- EXPECT_TRUE(strings::contains(
- status.get().message(), "Task uses more resources"));
-
- driver.stop();
- driver.join();
-
- Shutdown();
-}
-
-
-// This test verifies that if two tasks are launched with the same
-// task ID, the second task will get rejected.
-TEST_F(TaskValidationTest, DuplicatedTaskID)
-{
- Try<PID<Master>> master = StartMaster();
- ASSERT_SOME(master);
-
- MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
- Try<PID<Slave>> slave = StartSlave(&exec);
- ASSERT_SOME(slave);
-
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
- EXPECT_CALL(sched, registered(&driver, _, _));
-
- Future<vector<Offer>> offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- driver.start();
-
- AWAIT_READY(offers);
- EXPECT_NE(0u, offers.get().size());
-
- ExecutorInfo executor;
- executor.mutable_executor_id()->set_value("default");
- executor.mutable_command()->set_value("exit 1");
-
- // Create two tasks with the same id.
- TaskInfo task1;
- task1.set_name("");
- task1.mutable_task_id()->set_value("1");
- task1.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
- task1.mutable_resources()->MergeFrom(Resources::parse("cpus:1;mem:32").get());
- task1.mutable_executor()->MergeFrom(executor);
-
- TaskInfo task2;
- task2.set_name("");
- task2.mutable_task_id()->set_value("1");
- task2.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
- task2.mutable_resources()->MergeFrom(Resources::parse("cpus:1;mem:32").get());
- task2.mutable_executor()->MergeFrom(executor);
-
- vector<TaskInfo> tasks;
- tasks.push_back(task1);
- tasks.push_back(task2);
-
- EXPECT_CALL(exec, registered(_, _, _, _));
-
- // Grab the first task but don't send a status update.
- Future<TaskInfo> task;
- EXPECT_CALL(exec, launchTask(_, _))
- .WillOnce(FutureArg<1>(&task));
-
- Future<TaskStatus> status;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
-
- driver.launchTasks(offers.get()[0].id(), tasks);
-
- AWAIT_READY(task);
- EXPECT_EQ(task1.task_id(), task.get().task_id());
-
- AWAIT_READY(status);
- EXPECT_EQ(TASK_ERROR, status.get().state());
- EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status.get().reason());
-
- EXPECT_TRUE(strings::startsWith(
- status.get().message(), "Task has duplicate ID"));
-
- EXPECT_CALL(exec, shutdown(_))
- .Times(AtMost(1));
-
- driver.stop();
- driver.join();
-
- Shutdown();
-}
-
-
-// This test verifies that two tasks launched on the same slave with
-// the same executor id but different executor info are rejected.
-TEST_F(TaskValidationTest, ExecutorInfoDiffersOnSameSlave)
-{
- Try<PID<Master>> master = StartMaster();
- ASSERT_SOME(master);
-
- MockExecutor exec(DEFAULT_EXECUTOR_ID);
-
- Try<PID<Slave>> slave = StartSlave(&exec);
- ASSERT_SOME(slave);
-
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
- EXPECT_CALL(sched, registered(&driver, _, _))
- .Times(1);
-
- Future<vector<Offer>> offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- driver.start();
-
- AWAIT_READY(offers);
- EXPECT_NE(0u, offers.get().size());
-
- ExecutorInfo executor;
- executor.mutable_executor_id()->set_value("default");
- executor.mutable_command()->set_value("exit 1");
-
- TaskInfo task1;
- task1.set_name("");
- task1.mutable_task_id()->set_value("1");
- task1.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
- task1.mutable_resources()->MergeFrom(
- Resources::parse("cpus:1;mem:512").get());
- task1.mutable_executor()->MergeFrom(executor);
-
- executor.mutable_command()->set_value("exit 2");
-
- TaskInfo task2;
- task2.set_name("");
- task2.mutable_task_id()->set_value("2");
- task2.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id());
- task2.mutable_resources()->MergeFrom(
- Resources::parse("cpus:1;mem:512").get());
- task2.mutable_executor()->MergeFrom(executor);
-
- vector<TaskInfo> tasks;
- tasks.push_back(task1);
- tasks.push_back(task2);
-
- EXPECT_CALL(exec, registered(_, _, _, _))
- .Times(1);
-
- // Grab the "good" task but don't send a status update.
- Future<TaskInfo> task;
- EXPECT_CALL(exec, launchTask(_, _))
- .WillOnce(FutureArg<1>(&task));
-
- Future<TaskStatus> status;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
-
- driver.launchTasks(offers.get()[0].id(), tasks);
-
- AWAIT_READY(task);
- EXPECT_EQ(task1.task_id(), task.get().task_id());
-
- AWAIT_READY(status);
- EXPECT_EQ(task2.task_id(), status.get().task_id());
- EXPECT_EQ(TASK_ERROR, status.get().state());
- EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status.get().reason());
- EXPECT_TRUE(status.get().has_message());
- EXPECT_TRUE(strings::contains(
- status.get().message(), "Task has invalid ExecutorInfo"));
-
- EXPECT_CALL(exec, shutdown(_))
- .Times(AtMost(1));
-
- driver.stop();
- driver.join();
-
- Shutdown();
-}
-
-
-// This test verifies that two tasks each launched on a different
-// slave with same executor id but different executor info are
-// allowed.
-TEST_F(TaskValidationTest, ExecutorInfoDiffersOnDifferentSlaves)
-{
- Try<PID<Master>> master = StartMaster();
- ASSERT_SOME(master);
-
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
- Future<Nothing> registered;
- EXPECT_CALL(sched, registered(&driver, _, _))
- .WillOnce(FutureSatisfy(®istered));
-
- driver.start();
-
- AWAIT_READY(registered);
-
- Future<vector<Offer>> offers1;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers1));
-
- // Start the first slave.
- MockExecutor exec1(DEFAULT_EXECUTOR_ID);
-
- Try<PID<Slave>> slave1 = StartSlave(&exec1);
- ASSERT_SOME(slave1);
-
- AWAIT_READY(offers1);
- EXPECT_NE(0u, offers1.get().size());
-
- // Launch the first task with the default executor id.
- ExecutorInfo executor1;
- executor1 = DEFAULT_EXECUTOR_INFO;
- executor1.mutable_command()->set_value("exit 1");
-
- TaskInfo task1 = createTask(
- offers1.get()[0], executor1.command().value(), executor1.executor_id());
-
- vector<TaskInfo> tasks1;
- tasks1.push_back(task1);
-
- EXPECT_CALL(exec1, registered(_, _, _, _))
- .Times(1);
-
- EXPECT_CALL(exec1, launchTask(_, _))
- .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
-
- Future<TaskStatus> status1;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status1));
-
- driver.launchTasks(offers1.get()[0].id(), tasks1);
-
- AWAIT_READY(status1);
- ASSERT_EQ(TASK_RUNNING, status1.get().state());
-
- Future<vector<Offer>> offers2;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers2))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- // Now start the second slave.
- MockExecutor exec2(DEFAULT_EXECUTOR_ID);
-
- Try<PID<Slave>> slave2 = StartSlave(&exec2);
- ASSERT_SOME(slave2);
-
- AWAIT_READY(offers2);
- EXPECT_NE(0u, offers2.get().size());
-
- // Now launch the second task with the same executor id but
- // a different executor command.
- ExecutorInfo executor2;
- executor2 = executor1;
- executor2.mutable_command()->set_value("exit 2");
-
- TaskInfo task2 = createTask(
- offers2.get()[0], executor2.command().value(), executor2.executor_id());
-
- vector<TaskInfo> tasks2;
- tasks2.push_back(task2);
-
- EXPECT_CALL(exec2, registered(_, _, _, _))
- .Times(1);
-
- EXPECT_CALL(exec2, launchTask(_, _))
- .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING));
-
- Future<TaskStatus> status2;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status2));
-
- driver.launchTasks(offers2.get()[0].id(), tasks2);
-
- AWAIT_READY(status2);
- ASSERT_EQ(TASK_RUNNING, status2.get().state());
-
- EXPECT_CALL(exec1, shutdown(_))
- .Times(AtMost(1));
-
- EXPECT_CALL(exec2, shutdown(_))
- .Times(AtMost(1));
-
- driver.stop();
- driver.join();
-
- Shutdown();
-}
-
-
-// This test ensures that a persistent volume that is larger than the
-// offered disk resources results in a failed task.
-TEST_F(TaskValidationTest, DISABLED_AcquirePersistentDiskTooBig)
-{
- // Create a framework with role "role1";
- FrameworkInfo frameworkInfo;
- frameworkInfo = DEFAULT_FRAMEWORK_INFO;
- frameworkInfo.set_role("role1");
-
- // Setup ACLs in order to receive offers for "role1".
- ACLs acls;
- mesos::ACL::RegisterFramework* acl = acls.add_register_frameworks();
- acl->mutable_principals()->add_values(frameworkInfo.principal());
- acl->mutable_roles()->add_values(frameworkInfo.role());
-
- // Start master with ACLs.
- master::Flags masterFlags = CreateMasterFlags();
- masterFlags.roles = frameworkInfo.role();
- masterFlags.acls = acls;
-
- Try<PID<Master>> master = StartMaster(masterFlags);
- ASSERT_SOME(master);
-
- slave::Flags slaveFlags = CreateSlaveFlags();
- slaveFlags.resources = "cpus(*):4;mem(*):2048;disk(role1):1024";
-
- Try<PID<Slave>> slave = StartSlave(slaveFlags);
- ASSERT_SOME(slave);
-
- MockScheduler sched;
- MesosSchedulerDriver driver(
- &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
-
- EXPECT_CALL(sched, registered(&driver, _, _))
- .Times(1);
-
- Future<vector<Offer>> offers;
- EXPECT_CALL(sched, resourceOffers(&driver, _))
- .WillOnce(FutureArg<1>(&offers))
- .WillRepeatedly(Return()); // Ignore subsequent offers.
-
- driver.start();
-
- AWAIT_READY(offers);
- EXPECT_NE(0u, offers.get().size());
-
- // Create a persistent volume whose size is larger than the size of
- // the offered disk.
- Resource diskResource = Resources::parse("disk", "2048", "role1").get();
- diskResource.mutable_disk()->CopyFrom(createDiskInfo("1", "1"));
-
- // Include other resources in task resources.
- Resources taskResources =
- Resources::parse("cpus:1;mem:128").get() + diskResource;
-
- Offer offer = offers.get()[0];
- TaskInfo task =
- createTask(offer.slave_id(), taskResources, "", DEFAULT_EXECUTOR_ID);
-
- vector<TaskInfo> tasks;
- tasks.push_back(task);
-
- Future<TaskStatus> status;
- EXPECT_CALL(sched, statusUpdate(&driver, _))
- .WillOnce(FutureArg<1>(&status));
-
- driver.launchTasks(offer.id(), tasks);
-
- AWAIT_READY(status);
- EXPECT_EQ(task.task_id(), status.get().task_id());
- EXPECT_EQ(TASK_ERROR, status.get().state());
- EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status.get().reason());
- EXPECT_TRUE(status.get().has_message());
- EXPECT_TRUE(strings::contains(
- status.get().message(), "Failed to create persistent volumes"));
-
- driver.stop();
- driver.join();
-
- Shutdown();
-}
-
-// TODO(jieyu): Add tests for checking duplicated persistence ID
-// against offered resources.
-
-// TODO(jieyu): Add tests for checking duplicated persistence ID
-// across task and executors.
-
-// TODO(jieyu): Add tests for checking duplicated persistence ID
-// within an executor.
-
-// TODO(benh): Add tests for checking correct slave IDs.
-
-// TODO(benh): Add tests for checking executor resource usage.
-
-// TODO(benh): Add tests which launch multiple tasks and check for
-// aggregate resource usage.
-
-
class ResourceOffersTest : public MesosTest {};