// Verify that a task that tries to consume more space than it has requested // is only allowed to consume exactly the assigned resources. We tell dd // to write 2MB but only give it 1MB of resources and (roughly) verify that // it exits with a failure (that should be a write error). TEST_F(ROOT_XFS_QuotaTest, DiskUsageExceedsQuota) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); Owned<MasterDetector> detector = master.get()->createDetector(); Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), CreateSlaveFlags()); ASSERT_SOME(slave); MockScheduler sched; MesosSchedulerDriver driver( &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, 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); ASSERT_FALSE(offers->empty()); const Offer& offer = offers.get()[0]; // Create a task which requests 1MB disk, but actually uses more // than 2MB disk. TaskInfo task = createTask( offer.slave_id(), Resources::parse("cpus:1;mem:128;disk:1").get(), "dd if=/dev/zero of=file bs=1048576 count=2"); Future<TaskStatus> startingStatus; Future<TaskStatus> runningStatus; Future<TaskStatus> failedStatus; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&startingStatus)) .WillOnce(FutureArg<1>(&runningStatus)) .WillOnce(FutureArg<1>(&failedStatus)); driver.launchTasks(offer.id(), {task}); AWAIT_READY(startingStatus); EXPECT_EQ(task.task_id(), startingStatus->task_id()); EXPECT_EQ(TASK_STARTING, startingStatus->state()); AWAIT_READY(runningStatus); EXPECT_EQ(task.task_id(), runningStatus->task_id()); EXPECT_EQ(TASK_RUNNING, runningStatus->state()); AWAIT_READY(failedStatus); EXPECT_EQ(task.task_id(), failedStatus->task_id()); EXPECT_EQ(TASK_FAILED, failedStatus->state()); // Unlike the 'disk/du' isolator, the reason for task failure // should be that dd got an IO error. EXPECT_EQ(TaskStatus::SOURCE_EXECUTOR, failedStatus->source()); EXPECT_EQ("Command exited with status 1", failedStatus->message()); driver.stop(); driver.join(); }
TEST_F(ResourceOffersTest, ResourcesGetReofferedAfterTaskInfoError) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); Owned<MasterDetector> detector = master.get()->createDetector(); Try<Owned<cluster::Slave>> slave = StartSlave(detector.get()); ASSERT_SOME(slave); MockScheduler sched1; MesosSchedulerDriver driver1( &sched1, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL); EXPECT_CALL(sched1, registered(&driver1, _, _)); Future<vector<Offer>> offers; EXPECT_CALL(sched1, resourceOffers(&driver1, _)) .WillOnce(FutureArg<1>(&offers)) .WillRepeatedly(Return()); // Ignore subsequent offers. driver1.start(); AWAIT_READY(offers); ASSERT_FALSE(offers->empty()); 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(-1); Resource* mem = task.add_resources(); mem->set_name("mem"); mem->set_type(Value::SCALAR); mem->mutable_scalar()->set_value(static_cast<double>(Gigabytes(1).bytes())); vector<TaskInfo> tasks; tasks.push_back(task); Future<TaskStatus> status; EXPECT_CALL(sched1, statusUpdate(&driver1, _)) .WillOnce(FutureArg<1>(&status)); driver1.launchTasks(offers.get()[0].id(), tasks); AWAIT_READY(status); EXPECT_EQ(task.task_id(), status->task_id()); EXPECT_EQ(TASK_ERROR, status->state()); EXPECT_EQ(TaskStatus::REASON_TASK_INVALID, status->reason()); EXPECT_TRUE(status->has_message()); EXPECT_TRUE(strings::contains(status->message(), "Invalid scalar resource")) << status->message(); MockScheduler sched2; MesosSchedulerDriver driver2( &sched2, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL); EXPECT_CALL(sched2, registered(&driver2, _, _)); EXPECT_CALL(sched2, resourceOffers(&driver2, _)) .WillOnce(FutureArg<1>(&offers)) .WillRepeatedly(Return()); // Ignore subsequent offers. driver2.start(); AWAIT_READY(offers); driver1.stop(); driver1.join(); driver2.stop(); driver2.join(); }
// This test verifies that default executor subscription fails if the executor // provides a properly-signed authentication token with invalid claims. TEST_F(ExecutorAuthorizationTest, FailedSubscribe) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); // Start an agent with permissive ACLs so that a task can be launched. ACLs acls; acls.set_permissive(true); Result<Authorizer*> authorizer = Authorizer::create(acls); ASSERT_SOME(authorizer); slave::Flags flags = CreateSlaveFlags(); flags.acls = acls; Owned<MasterDetector> detector = master.get()->createDetector(); auto executor = std::make_shared<v1::MockHTTPExecutor>(); v1::Resources resources = v1::Resources::parse("cpus:0.1;mem:32;disk:32").get(); v1::ExecutorInfo executorInfo; executorInfo.set_type(v1::ExecutorInfo::DEFAULT); executorInfo.mutable_executor_id()->CopyFrom(v1::DEFAULT_EXECUTOR_ID); executorInfo.mutable_resources()->CopyFrom(resources); Owned<TestContainerizer> containerizer( new TestContainerizer(devolve(executorInfo.executor_id()), executor)); // This pointer is passed to the agent, which will perform the cleanup. Owned<MockSecretGenerator> mockSecretGenerator(new MockSecretGenerator()); Try<Owned<cluster::Slave>> slave = StartSlave( detector.get(), containerizer.get(), mockSecretGenerator.get(), authorizer.get(), flags); ASSERT_SOME(slave); auto scheduler = std::make_shared<v1::MockHTTPScheduler>(); Future<Nothing> connected; EXPECT_CALL(*scheduler, connected(_)) .WillOnce(FutureSatisfy(&connected)); v1::scheduler::TestMesos 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()); // Ignore subsequent offers. EXPECT_CALL(*scheduler, heartbeat(_)) .WillRepeatedly(Return()); // Ignore heartbeats. mesos.send(v1::createCallSubscribe(v1::DEFAULT_FRAMEWORK_INFO)); AWAIT_READY(subscribed); v1::FrameworkID frameworkId(subscribed->framework_id()); executorInfo.mutable_framework_id()->CopyFrom(frameworkId); AWAIT_READY(offers); ASSERT_FALSE(offers->offers().empty()); Future<v1::executor::Mesos*> executorLib; EXPECT_CALL(*executor, connected(_)) .WillOnce(FutureArg<0>(&executorLib)); Owned<JWTSecretGenerator> jwtSecretGenerator( new JWTSecretGenerator(DEFAULT_JWT_SECRET_KEY)); // Create a principal which contains an incorrect ContainerID. hashmap<string, string> claims; claims["fid"] = frameworkId.value(); claims["eid"] = v1::DEFAULT_EXECUTOR_ID.value(); claims["cid"] = id::UUID::random().toString(); Principal principal(None(), claims); // Generate an authentication token which is signed using the correct key, // but contains an invalid set of claims. Future<Secret> authenticationToken = jwtSecretGenerator->generate(principal); AWAIT_READY(authenticationToken); EXPECT_CALL(*mockSecretGenerator, generate(_)) .WillOnce(Return(authenticationToken.get())); const v1::Offer& offer = offers->offers(0); const v1::AgentID& agentId = offer.agent_id(); { v1::TaskInfo taskInfo = v1::createTask(agentId, resources, SLEEP_COMMAND(1000)); v1::TaskGroupInfo taskGroup; taskGroup.add_tasks()->CopyFrom(taskInfo); v1::scheduler::Call call; call.mutable_framework_id()->CopyFrom(frameworkId); call.set_type(v1::scheduler::Call::ACCEPT); v1::scheduler::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(executorInfo); launchGroup->mutable_task_group()->CopyFrom(taskGroup); mesos.send(call); } AWAIT_READY(executorLib); { v1::executor::Call call; call.mutable_framework_id()->CopyFrom(frameworkId); call.mutable_executor_id()->CopyFrom(v1::DEFAULT_EXECUTOR_ID); call.set_type(v1::executor::Call::SUBSCRIBE); call.mutable_subscribe(); executorLib.get()->send(call); } Future<v1::executor::Event::Error> error; EXPECT_CALL(*executor, error(_, _)) .WillOnce(FutureArg<1>(&error)); AWAIT_READY(error); EXPECT_EQ( error->message(), "Received unexpected '403 Forbidden' () for SUBSCRIBE"); }