This is an automated email from the ASF dual-hosted git repository. bmahler pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mesos.git
commit b3e96e108c7972a3a93b3b3878cc9817026109cb Author: Benjamin Mahler <[email protected]> AuthorDate: Wed Feb 12 22:11:53 2020 -0500 Added a test to ensure agents cannot be reactivated while DRAINING. Review: https://reviews.apache.org/r/72126 --- src/tests/master_draining_tests.cpp | 135 +++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/src/tests/master_draining_tests.cpp b/src/tests/master_draining_tests.cpp index f1a00df..a91201e 100644 --- a/src/tests/master_draining_tests.cpp +++ b/src/tests/master_draining_tests.cpp @@ -47,6 +47,7 @@ #include "messages/messages.hpp" #include "tests/cluster.hpp" +#include "tests/containerizer.hpp" #include "tests/mesos.hpp" #include "tests/resources_utils.hpp" @@ -55,10 +56,15 @@ namespace http = process::http; using mesos::master::detector::MasterDetector; +using mesos::slave::ContainerTermination; + using process::Clock; using process::Failure; using process::Future; using process::Owned; +using process::Promise; + +using std::vector; using testing::_; using testing::AllOf; @@ -126,7 +132,7 @@ public: // Helper function to post a request to "/api/v1" master endpoint and return // the response. - Future<http::Response> post( + static Future<http::Response> post( const process::PID<master::Master>& pid, const v1::master::Call& call, const ContentType& contentType, @@ -1013,6 +1019,133 @@ TEST_P(MasterDrainingTest, DrainAgentUnreachable) EXPECT_EQ(agentId, offers->offers(0).agent_id()); } + +class MasterDrainingTest2 + : public MesosTest, + public WithParamInterface<ContentType> {}; + +// These tests are parameterized by the content type of the HTTP request. +INSTANTIATE_TEST_CASE_P( + ContentType, + MasterDrainingTest2, + ::testing::Values(ContentType::PROTOBUF, ContentType::JSON)); + + +// This test ensures that the user cannot reactivate an agent +// that is still in the DRAINING state (which was previously +// possible and problematic, see MESOS-10096). +// +// We use a mock executor that ignores the kill task request +// to ensure the agent doesn't transition out of draining. +TEST_P(MasterDrainingTest2, DisallowReactivationWhileDraining) +{ + Try<Owned<cluster::Master>> master = StartMaster(); + ASSERT_SOME(master); + + MockExecutor exec(DEFAULT_EXECUTOR_ID); + + TestContainerizer containerizer(&exec); + + Owned<MasterDetector> detector = master.get()->createDetector(); + auto slaveOptions = SlaveOptions(detector.get()) + .withFlags(CreateSlaveFlags()) + .withId(process::ID::generate()) + .withContainerizer(&containerizer); + Try<Owned<cluster::Slave>> slave = StartSlave(slaveOptions); + ASSERT_SOME(slave); + + FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO; + frameworkInfo.set_checkpoint(true); + + MockScheduler sched; + MesosSchedulerDriver driver( + &sched, frameworkInfo, master.get()->pid, DEFAULT_CREDENTIAL); + + // Start the scheduler and launch a task. + 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); + ASSERT_FALSE(offers->empty()); + Offer offer = offers.get()[0]; + + TaskInfo task; + task.set_name(""); + task.mutable_task_id()->set_value("1"); + *task.mutable_slave_id() = offer.slave_id(); + *task.mutable_resources() = offer.resources(); + *task.mutable_executor() = DEFAULT_EXECUTOR_INFO; + + EXPECT_CALL(exec, registered(_, _, _, _)); + + EXPECT_CALL(exec, launchTask(_, _)) + .WillRepeatedly(SendStatusUpdateFromTask(TASK_RUNNING)); + + EXPECT_CALL(containerizer, update(_, _)) + .WillRepeatedly(Return(Nothing())); + + Promise<Option<ContainerTermination>> hang; + + EXPECT_CALL(containerizer, wait(_)) + .WillRepeatedly(Return(hang.future())); + + Future<TaskStatus> statusRunning1; + EXPECT_CALL(sched, statusUpdate(&driver, _)) + .WillOnce(FutureArg<1>(&statusRunning1)); + + driver.launchTasks(offer.id(), {task}); + + AWAIT_READY(statusRunning1); + ASSERT_EQ(TASK_RUNNING, statusRunning1->state()); + + ContentType contentType = GetParam(); + + // Start draining the agent. + // Don't do anything upon the kill task request. + EXPECT_CALL(exec, killTask(_, _)); + + { + v1::master::Call::DrainAgent drainAgent; + *drainAgent.mutable_agent_id() = evolve(offer.slave_id()); + + v1::master::Call call; + call.set_type(v1::master::Call::DRAIN_AGENT); + *call.mutable_drain_agent() = drainAgent; + + AWAIT_EXPECT_RESPONSE_STATUS_EQ( + http::OK().status, + MasterAlreadyDrainedTest::post( + (*master)->pid, call, contentType)); + } + + // The agent should now be in the draining state. + + // Attempt to reactivate the agent while it is in + // draining, this should be rejected. + { + v1::master::Call::ReactivateAgent reactivateAgent; + *reactivateAgent.mutable_agent_id() = evolve(offer.slave_id()); + + v1::master::Call call; + call.set_type(v1::master::Call::REACTIVATE_AGENT); + call.mutable_reactivate_agent()->CopyFrom(reactivateAgent); + + Future<http::Response> response = + MasterAlreadyDrainedTest::post((*master)->pid, call, contentType); + + AWAIT_READY(response); + EXPECT_EQ(http::BadRequest().status, response->status); + EXPECT_EQ("Agent is still in the DRAINING state", + response->body); + } +} + } // namespace tests { } // namespace internal { } // namespace mesos {
