TEST_F(FilesTest, DownloadTest) { Files files; process::UPID upid("files", process::address()); // This is a one-pixel black gif image. const unsigned char gifData[] = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x01, 0x00, 0x01, 0x00, 0x91, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x02, 0x4c, 0x01, 0x00, 0x3b, 0x00 }; string data((const char*) gifData, sizeof(gifData)); ASSERT_SOME(os::write("binary", "no file extension")); ASSERT_SOME(os::write("black.gif", data)); AWAIT_EXPECT_READY(files.attach("binary", "binary")); AWAIT_EXPECT_READY(files.attach("black.gif", "black.gif")); Future<Response> response = process::http::get(upid, "download.json", "path=binary"); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ( "application/octet-stream", "Content-Type", response); AWAIT_EXPECT_RESPONSE_BODY_EQ("no file extension", response); response = process::http::get(upid, "download.json", "path=black.gif"); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ("image/gif", "Content-Type", response); AWAIT_EXPECT_RESPONSE_BODY_EQ(data, response); }
TEST_F(MesosSchedulerDriverTest, MetricsEndpoint) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); MockScheduler sched; MesosSchedulerDriver driver( &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL); Future<Nothing> registered; EXPECT_CALL(sched, registered(&driver, _, _)) .WillOnce(FutureSatisfy(®istered)); ASSERT_EQ(DRIVER_RUNNING, driver.start()); AWAIT_READY(registered); Future<process::http::Response> response = process::http::get(MetricsProcess::instance()->self(), "snapshot"); 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 metrics = parse.get(); EXPECT_EQ(1u, metrics.values.count("scheduler/event_queue_messages")); EXPECT_EQ(1u, metrics.values.count("scheduler/event_queue_dispatches")); driver.stop(); driver.join(); }
TEST_P(SchedulerHttpApiTest, DefaultAccept) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL); headers["Accept"] = "*/*"; // Only subscribe needs to 'Accept' json or protobuf. Call call; call.set_type(Call::SUBSCRIBE); Call::Subscribe* subscribe = call.mutable_subscribe(); subscribe->mutable_framework_info()->CopyFrom(v1::DEFAULT_FRAMEWORK_INFO); // Retrieve the parameter passed as content type to this test. const string contentType = GetParam(); Future<Response> response = process::http::streaming::post( master.get()->pid, "api/v1/scheduler", headers, serialize(call, contentType), contentType); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response); }
JSON::Object Metrics() { process::UPID upid("metrics", process::address()); process::Future<process::http::Response> response = process::http::get(upid, "snapshot"); AWAIT_EXPECT_RESPONSE_STATUS_EQ(process::http::OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response); Try<JSON::Object> parse = JSON::parse<JSON::Object>(response.get().body); CHECK_SOME(parse); return parse.get(); }
// This test verifies if the role is invalid in scheduler's framework message, // the event is error on the stream in response to a Subscribe call request. TEST_P(SchedulerHttpApiTest, RejectFrameworkWithInvalidRole) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); Call call; call.set_type(Call::SUBSCRIBE); Call::Subscribe* subscribe = call.mutable_subscribe(); v1::FrameworkInfo framework = v1::DEFAULT_FRAMEWORK_INFO; // Set invalid role. framework.set_role("/test/test1"); subscribe->mutable_framework_info()->CopyFrom(framework); // Retrieve the parameter passed as content type to this test. const string contentType = GetParam(); process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL); headers["Accept"] = contentType; Future<Response> response = process::http::streaming::post( master.get()->pid, "api/v1/scheduler", headers, serialize(call, contentType), contentType); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ("chunked", "Transfer-Encoding", response); ASSERT_EQ(Response::PIPE, response.get().type); Option<Pipe::Reader> reader = response.get().reader; ASSERT_SOME(reader); auto deserializer = lambda::bind( &SchedulerHttpApiTest::deserialize, this, contentType, lambda::_1); Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get()); Future<Result<Event>> event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); // Check event type is error. ASSERT_EQ(Event::ERROR, event.get().get().type()); }
// This test verifies the correct handling of the statistics // endpoint when there is no executor running. TEST(MonitorTest, NoExecutor) { ResourceMonitor monitor([]() -> Future<ResourceUsage> { return ResourceUsage(); }); UPID upid("monitor", process::address()); Future<http::Response> response = http::get(upid, "statistics"); AWAIT_READY(response); AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ( "application/json", "Content-Type", response); AWAIT_EXPECT_RESPONSE_BODY_EQ("[]", response); }
JSON::Object Metrics() { process::UPID upid("metrics", process::address()); // TODO(neilc): This request might timeout if the current value of a // metric cannot be determined. In tests, a common cause for this is // MESOS-6231 when multiple scheduler drivers are in use. process::Future<process::http::Response> response = process::http::get(upid, "snapshot"); AWAIT_EXPECT_RESPONSE_STATUS_EQ(process::http::OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response); Try<JSON::Object> parse = JSON::parse<JSON::Object>(response.get().body); CHECK_SOME(parse); return parse.get(); }
// This test verifies the correct handling of the statistics // endpoint when statistics is missing in ResourceUsage. TEST(MonitorTest, MissingStatistics) { ResourceMonitor monitor([]() -> Future<ResourceUsage> { FrameworkID frameworkId; frameworkId.set_value("framework"); ExecutorID executorId; executorId.set_value("executor"); ExecutorInfo executorInfo; executorInfo.mutable_executor_id()->CopyFrom(executorId); executorInfo.mutable_framework_id()->CopyFrom(frameworkId); executorInfo.set_name("name"); executorInfo.set_source("source"); Resources resources = Resources::parse("cpus:1;mem:2").get(); ResourceUsage usage; ResourceUsage::Executor* executor = usage.add_executors(); executor->mutable_executor_info()->CopyFrom(executorInfo); executor->mutable_allocated()->CopyFrom(resources); return usage; }); UPID upid("monitor", process::address()); Future<http::Response> response = http::get(upid, "statistics"); AWAIT_READY(response); AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ( "application/json", "Content-Type", response); AWAIT_EXPECT_RESPONSE_BODY_EQ("[]", response); }
TEST(MonitorTest, Statistics) { FrameworkID frameworkId; frameworkId.set_value("framework"); ExecutorID executorId; executorId.set_value("executor"); ExecutorInfo executorInfo; executorInfo.mutable_executor_id()->CopyFrom(executorId); executorInfo.mutable_framework_id()->CopyFrom(frameworkId); executorInfo.set_name("name"); executorInfo.set_source("source"); ResourceStatistics statistics; statistics.set_cpus_nr_periods(100); statistics.set_cpus_nr_throttled(2); statistics.set_cpus_user_time_secs(4); statistics.set_cpus_system_time_secs(1); statistics.set_cpus_throttled_time_secs(0.5); statistics.set_cpus_limit(1.0); statistics.set_mem_file_bytes(0); statistics.set_mem_anon_bytes(0); statistics.set_mem_mapped_file_bytes(0); statistics.set_mem_rss_bytes(1024); statistics.set_mem_limit_bytes(2048); statistics.set_timestamp(0); ResourceMonitor monitor([=]() -> Future<ResourceUsage> { Resources resources = Resources::parse("cpus:1;mem:2").get(); ResourceUsage usage; ResourceUsage::Executor* executor = usage.add_executors(); executor->mutable_executor_info()->CopyFrom(executorInfo); executor->mutable_allocated()->CopyFrom(resources); executor->mutable_statistics()->CopyFrom(statistics); return usage; }); UPID upid("monitor", process::address()); Future<http::Response> response = http::get(upid, "statistics"); AWAIT_READY(response); AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ( "application/json", "Content-Type", response); JSON::Array expected; JSON::Object usage; usage.values["executor_id"] = "executor"; usage.values["executor_name"] = "name"; usage.values["framework_id"] = "framework"; usage.values["source"] = "source"; usage.values["statistics"] = JSON::Protobuf(statistics); expected.values.push_back(usage); Try<JSON::Array> result = JSON::parse<JSON::Array>(response.get().body); ASSERT_SOME(result); ASSERT_EQ(expected, result.get()); }
// This is an end-to-end test that verfies that the slave returns the // correct ResourceUsage based on the currently running executors, and // the values get from the statistics endpoint are as expected. TEST_F(MonitorIntegrationTest, RunningExecutor) { 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_FALSE(offers.get().empty()); const Offer& offer = offers.get()[0]; // Launch a task and wait until it is in RUNNING status. TaskInfo task = createTask( offer.slave_id(), Resources::parse("cpus:1;mem:32").get(), "sleep 1000"); Future<TaskStatus> status; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&status)); driver.launchTasks(offer.id(), {task}); AWAIT_READY(status); EXPECT_EQ(task.task_id(), status.get().task_id()); EXPECT_EQ(TASK_RUNNING, status.get().state()); // Hit the statistics endpoint and expect the response contains the // resource statistics for the running container. UPID upid("monitor", process::address()); Future<http::Response> response = http::get(upid, "statistics"); AWAIT_READY(response); AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ( "application/json", "Content-Type", response); // Verify that the statistics in the response contains the proper // resource limits for the container. Try<JSON::Value> value = JSON::parse(response.get().body); ASSERT_SOME(value); Try<JSON::Value> expected = JSON::parse(strings::format( "[{" "\"statistics\":{" "\"cpus_limit\":%g," "\"mem_limit_bytes\":%lu" "}" "}]", 1 + slave::DEFAULT_EXECUTOR_CPUS, (Megabytes(32) + slave::DEFAULT_EXECUTOR_MEM).bytes()).get()); ASSERT_SOME(expected); EXPECT_TRUE(value.get().contains(expected.get())); driver.stop(); driver.join(); Shutdown(); }
TEST_F(MetricsTest, Master) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); // Get the snapshot. process::UPID upid("metrics", process::address()); process::Future<process::http::Response> response = process::http::get(upid, "snapshot"); AWAIT_EXPECT_RESPONSE_STATUS_EQ(process::http::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 stats = parse.get(); EXPECT_EQ(1u, stats.values.count("master/uptime_secs")); EXPECT_EQ(1u, stats.values.count("master/elected")); EXPECT_EQ(1u, stats.values.count("master/slaves_connected")); EXPECT_EQ(1u, stats.values.count("master/slaves_disconnected")); EXPECT_EQ(1u, stats.values.count("master/slaves_active")); EXPECT_EQ(1u, stats.values.count("master/slaves_inactive")); EXPECT_EQ(1u, stats.values.count("master/frameworks_connected")); EXPECT_EQ(1u, stats.values.count("master/frameworks_disconnected")); EXPECT_EQ(1u, stats.values.count("master/frameworks_active")); EXPECT_EQ(1u, stats.values.count("master/frameworks_inactive")); EXPECT_EQ(1u, stats.values.count("master/outstanding_offers")); EXPECT_EQ(1u, stats.values.count("master/tasks_staging")); EXPECT_EQ(1u, stats.values.count("master/tasks_starting")); EXPECT_EQ(1u, stats.values.count("master/tasks_running")); EXPECT_EQ(1u, stats.values.count("master/tasks_killing")); EXPECT_EQ(1u, stats.values.count("master/tasks_finished")); EXPECT_EQ(1u, stats.values.count("master/tasks_failed")); EXPECT_EQ(1u, stats.values.count("master/tasks_killed")); EXPECT_EQ(1u, stats.values.count("master/tasks_lost")); EXPECT_EQ(1u, stats.values.count("master/tasks_error")); EXPECT_EQ(1u, stats.values.count("master/dropped_messages")); // Messages from schedulers. EXPECT_EQ(1u, stats.values.count("master/messages_register_framework")); EXPECT_EQ(1u, stats.values.count("master/messages_reregister_framework")); EXPECT_EQ(1u, stats.values.count("master/messages_unregister_framework")); EXPECT_EQ(1u, stats.values.count("master/messages_deactivate_framework")); EXPECT_EQ(1u, stats.values.count("master/messages_kill_task")); EXPECT_EQ(1u, stats.values.count( "master/messages_status_update_acknowledgement")); EXPECT_EQ(1u, stats.values.count("master/messages_resource_request")); EXPECT_EQ(1u, stats.values.count("master/messages_launch_tasks")); EXPECT_EQ(1u, stats.values.count("master/messages_decline_offers")); EXPECT_EQ(1u, stats.values.count("master/messages_revive_offers")); EXPECT_EQ(1u, stats.values.count("master/messages_suppress_offers")); EXPECT_EQ(1u, stats.values.count("master/messages_reconcile_tasks")); EXPECT_EQ(1u, stats.values.count("master/messages_framework_to_executor")); // Messages from slaves. EXPECT_EQ(1u, stats.values.count("master/messages_register_slave")); EXPECT_EQ(1u, stats.values.count("master/messages_reregister_slave")); EXPECT_EQ(1u, stats.values.count("master/messages_unregister_slave")); EXPECT_EQ(1u, stats.values.count("master/messages_status_update")); EXPECT_EQ(1u, stats.values.count("master/messages_exited_executor")); // Messages from both schedulers and slaves. EXPECT_EQ(1u, stats.values.count("master/messages_authenticate")); EXPECT_EQ(1u, stats.values.count( "master/valid_framework_to_executor_messages")); EXPECT_EQ(1u, stats.values.count( "master/invalid_framework_to_executor_messages")); EXPECT_EQ(1u, stats.values.count("master/valid_status_updates")); EXPECT_EQ(1u, stats.values.count("master/invalid_status_updates")); EXPECT_EQ(1u, stats.values.count( "master/valid_status_update_acknowledgements")); EXPECT_EQ(1u, stats.values.count( "master/invalid_status_update_acknowledgements")); EXPECT_EQ(1u, stats.values.count("master/recovery_slave_removals")); EXPECT_EQ(1u, stats.values.count("master/event_queue_messages")); EXPECT_EQ(1u, stats.values.count("master/event_queue_dispatches")); EXPECT_EQ(1u, stats.values.count("master/event_queue_http_requests")); EXPECT_EQ(1u, stats.values.count("master/cpus_total")); EXPECT_EQ(1u, stats.values.count("master/cpus_used")); EXPECT_EQ(1u, stats.values.count("master/cpus_percent")); EXPECT_EQ(1u, stats.values.count("master/mem_total")); EXPECT_EQ(1u, stats.values.count("master/mem_used")); EXPECT_EQ(1u, stats.values.count("master/mem_percent")); EXPECT_EQ(1u, stats.values.count("master/disk_total")); EXPECT_EQ(1u, stats.values.count("master/disk_used")); EXPECT_EQ(1u, stats.values.count("master/disk_percent")); EXPECT_EQ(1u, stats.values.count("registrar/queued_operations")); EXPECT_EQ(1u, stats.values.count("registrar/registry_size_bytes")); EXPECT_EQ(1u, stats.values.count("registrar/state_fetch_ms")); EXPECT_EQ(1u, stats.values.count("registrar/state_store_ms")); }
TEST_F(MetricsTest, Slave) { // TODO(dhamon): https://issues.apache.org/jira/browse/MESOS-2134 to allow // only a Slave to be started. 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); // Get the snapshot. process::UPID upid("metrics", process::address()); process::Future<process::http::Response> response = process::http::get(upid, "snapshot"); AWAIT_EXPECT_RESPONSE_STATUS_EQ(process::http::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 stats = parse.get(); EXPECT_EQ(1u, stats.values.count("slave/uptime_secs")); EXPECT_EQ(1u, stats.values.count("slave/registered")); EXPECT_EQ(1u, stats.values.count("slave/recovery_errors")); EXPECT_EQ(1u, stats.values.count("slave/frameworks_active")); EXPECT_EQ(1u, stats.values.count("slave/tasks_staging")); EXPECT_EQ(1u, stats.values.count("slave/tasks_starting")); EXPECT_EQ(1u, stats.values.count("slave/tasks_running")); EXPECT_EQ(1u, stats.values.count("slave/tasks_killing")); EXPECT_EQ(1u, stats.values.count("slave/tasks_finished")); EXPECT_EQ(1u, stats.values.count("slave/tasks_failed")); EXPECT_EQ(1u, stats.values.count("slave/tasks_killed")); EXPECT_EQ(1u, stats.values.count("slave/tasks_lost")); EXPECT_EQ(1u, stats.values.count("slave/executors_registering")); EXPECT_EQ(1u, stats.values.count("slave/executors_running")); EXPECT_EQ(1u, stats.values.count("slave/executors_terminating")); EXPECT_EQ(1u, stats.values.count("slave/executors_terminated")); EXPECT_EQ(1u, stats.values.count("slave/valid_status_updates")); EXPECT_EQ(1u, stats.values.count("slave/invalid_status_updates")); EXPECT_EQ(1u, stats.values.count("slave/valid_framework_messages")); EXPECT_EQ(1u, stats.values.count("slave/invalid_framework_messages")); EXPECT_EQ(1u, stats.values.count("slave/container_launch_errors")); EXPECT_EQ(1u, stats.values.count("slave/cpus_total")); EXPECT_EQ(1u, stats.values.count("slave/cpus_used")); EXPECT_EQ(1u, stats.values.count("slave/cpus_percent")); EXPECT_EQ(1u, stats.values.count("slave/mem_total")); EXPECT_EQ(1u, stats.values.count("slave/mem_used")); EXPECT_EQ(1u, stats.values.count("slave/mem_percent")); EXPECT_EQ(1u, stats.values.count("slave/disk_total")); EXPECT_EQ(1u, stats.values.count("slave/disk_used")); EXPECT_EQ(1u, stats.values.count("slave/disk_percent")); }
// This test verifies that if master receives a status update // for an already terminated task it forwards it without // changing the state of the task. TEST_F(StatusUpdateManagerTest, DuplicatedTerminalStatusUpdate) { Try<PID<Master> > master = StartMaster(); ASSERT_SOME(master); MockExecutor exec(DEFAULT_EXECUTOR_ID); Try<PID<Slave>> slave = StartSlave(&exec); ASSERT_SOME(slave); FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO; frameworkInfo.set_checkpoint(true); // Enable checkpointing. MockScheduler sched; MesosSchedulerDriver driver( &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL); FrameworkID frameworkId; EXPECT_CALL(sched, registered(_, _, _)) .WillOnce(SaveArg<1>(&frameworkId)); Future<vector<Offer> > offers; EXPECT_CALL(sched, resourceOffers(_, _)) .WillOnce(FutureArg<1>(&offers)) .WillRepeatedly(Return()); // Ignore subsequent offers. driver.start(); AWAIT_READY(offers); EXPECT_NE(0u, offers.get().size()); ExecutorDriver* execDriver; EXPECT_CALL(exec, registered(_, _, _, _)) .WillOnce(SaveArg<0>(&execDriver)); // Send a terminal update right away. EXPECT_CALL(exec, launchTask(_, _)) .WillOnce(SendStatusUpdateFromTask(TASK_FINISHED)); Future<TaskStatus> status; EXPECT_CALL(sched, statusUpdate(_, _)) .WillOnce(FutureArg<1>(&status)); Future<Nothing> _statusUpdateAcknowledgement = FUTURE_DISPATCH(slave.get(), &Slave::_statusUpdateAcknowledgement); driver.launchTasks(offers.get()[0].id(), createTasks(offers.get()[0])); AWAIT_READY(status); EXPECT_EQ(TASK_FINISHED, status.get().state()); AWAIT_READY(_statusUpdateAcknowledgement); Future<TaskStatus> update; EXPECT_CALL(sched, statusUpdate(_, _)) .WillOnce(FutureArg<1>(&update)); Future<Nothing> _statusUpdateAcknowledgement2 = FUTURE_DISPATCH(slave.get(), &Slave::_statusUpdateAcknowledgement); Clock::pause(); // Now send a TASK_KILLED update for the same task. TaskStatus status2 = status.get(); status2.set_state(TASK_KILLED); execDriver->sendStatusUpdate(status2); // Ensure the scheduler receives TASK_KILLED. AWAIT_READY(update); EXPECT_EQ(TASK_KILLED, update.get().state()); // Ensure the slave properly handles the ACK. // Clock::settle() ensures that the slave successfully // executes Slave::_statusUpdateAcknowledgement(). AWAIT_READY(_statusUpdateAcknowledgement2); // Verify the latest task status. Future<process::http::Response> tasks = process::http::get(master.get(), "tasks"); AWAIT_EXPECT_RESPONSE_STATUS_EQ(process::http::OK().status, tasks); AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", tasks); Try<JSON::Object> parse = JSON::parse<JSON::Object>(tasks.get().body); ASSERT_SOME(parse); Result<JSON::String> state = parse.get().find<JSON::String>("tasks[0].state"); ASSERT_SOME_EQ(JSON::String("TASK_FINISHED"), state); Clock::resume(); EXPECT_CALL(exec, shutdown(_)) .Times(AtMost(1)); driver.stop(); driver.join(); Shutdown(); }
// This test verifies that we are able to downgrade from a HTTP based // framework to PID. TEST_P(SchedulerHttpApiTest, UpdateHttpToPidScheduler) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); v1::FrameworkInfo frameworkInfo = v1::DEFAULT_FRAMEWORK_INFO; Call call; call.set_type(Call::SUBSCRIBE); Call::Subscribe* subscribe = call.mutable_subscribe(); subscribe->mutable_framework_info()->CopyFrom(frameworkInfo); // Retrieve the parameter passed as content type to this test. const string contentType = GetParam(); process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL); headers["Accept"] = contentType; Future<Response> response = process::http::streaming::post( master.get()->pid, "api/v1/scheduler", headers, serialize(call, contentType), contentType); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ("chunked", "Transfer-Encoding", response); ASSERT_EQ(Response::PIPE, response.get().type); Option<Pipe::Reader> reader = response.get().reader; ASSERT_SOME(reader); auto deserializer = lambda::bind( &SchedulerHttpApiTest::deserialize, this, contentType, lambda::_1); Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get()); Future<Result<Event>> event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); // Check event type is subscribed and the framework id is set. ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type()); frameworkInfo.mutable_id()-> CopyFrom(event.get().get().subscribed().framework_id()); // Make sure it receives a heartbeat. event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); ASSERT_EQ(Event::HEARTBEAT, event.get().get().type()); MockScheduler sched; MesosSchedulerDriver driver( &sched, devolve(frameworkInfo), master.get()->pid, DEFAULT_CREDENTIAL); Future<FrameworkID> frameworkId; EXPECT_CALL(sched, registered(&driver, _, _)) .WillOnce(FutureArg<1>(&frameworkId)); driver.start(); AWAIT_READY(frameworkId); ASSERT_EQ(evolve(frameworkId.get()), frameworkInfo.id()); driver.stop(); driver.join(); }
// This test verifies if we are able to upgrade from a PID based // scheduler to HTTP scheduler. TEST_P(SchedulerHttpApiTest, UpdatePidToHttpScheduler) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); v1::FrameworkInfo frameworkInfo = v1::DEFAULT_FRAMEWORK_INFO; frameworkInfo.set_failover_timeout(Weeks(2).secs()); MockScheduler sched; StandaloneMasterDetector detector(master.get()->pid); TestingMesosSchedulerDriver driver(&sched, &detector, devolve(frameworkInfo)); Future<FrameworkID> frameworkId; EXPECT_CALL(sched, registered(&driver, _, _)) .WillOnce(FutureArg<1>(&frameworkId)); // Check that driver is notified with an error when the http // framework is connected. Future<FrameworkErrorMessage> errorMessage = FUTURE_PROTOBUF(FrameworkErrorMessage(), _, _); EXPECT_CALL(sched, error(_, _)); driver.start(); AWAIT_READY(frameworkId); EXPECT_NE("", frameworkId.get().value()); // Now try to subscribe as an HTTP framework. Call call; call.set_type(Call::SUBSCRIBE); call.mutable_framework_id()->CopyFrom(evolve(frameworkId.get())); Call::Subscribe* subscribe = call.mutable_subscribe(); subscribe->mutable_framework_info()->CopyFrom(frameworkInfo); subscribe->mutable_framework_info()->mutable_id()-> CopyFrom(evolve(frameworkId.get())); // Retrieve the parameter passed as content type to this test. const string contentType = GetParam(); process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL); headers["Accept"] = contentType; Future<Response> response = process::http::streaming::post( master.get()->pid, "api/v1/scheduler", headers, serialize(call, contentType), contentType); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ("chunked", "Transfer-Encoding", response); ASSERT_EQ(Response::PIPE, response.get().type); Option<Pipe::Reader> reader = response.get().reader; ASSERT_SOME(reader); auto deserializer = lambda::bind( &SchedulerHttpApiTest::deserialize, this, contentType, lambda::_1); Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get()); Future<Result<Event>> event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); // Check event type is subscribed and the framework id is set. ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type()); EXPECT_EQ(evolve(frameworkId.get()), event.get().get().subscribed().framework_id()); // Make sure it receives a heartbeat. event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); ASSERT_EQ(Event::HEARTBEAT, event.get().get().type()); driver.stop(); driver.join(); }
// This test verifies if the scheduler is able to receive a Subscribed // event and heartbeat events on the stream in response to a Subscribe // call request. TEST_P(SchedulerHttpApiTest, Subscribe) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); Call call; call.set_type(Call::SUBSCRIBE); Call::Subscribe* subscribe = call.mutable_subscribe(); subscribe->mutable_framework_info()->CopyFrom(v1::DEFAULT_FRAMEWORK_INFO); // Retrieve the parameter passed as content type to this test. const string contentType = GetParam(); process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL); headers["Accept"] = contentType; Future<Response> response = process::http::streaming::post( master.get()->pid, "api/v1/scheduler", headers, serialize(call, contentType), contentType); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ("chunked", "Transfer-Encoding", response); ASSERT_EQ(Response::PIPE, response.get().type); ASSERT_TRUE(response.get().headers.contains("Mesos-Stream-Id")); EXPECT_NE("", response.get().headers.at("Mesos-Stream-Id")); Option<Pipe::Reader> reader = response.get().reader; ASSERT_SOME(reader); auto deserializer = lambda::bind( &SchedulerHttpApiTest::deserialize, this, contentType, lambda::_1); Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get()); Future<Result<Event>> event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); // Check event type is subscribed and the framework id is set. ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type()); EXPECT_NE("", event.get().get().subscribed().framework_id().value()); // Make sure it receives a heartbeat. event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); ASSERT_EQ(Event::HEARTBEAT, event.get().get().type()); // Advance the clock to receive another heartbeat. Clock::pause(); Clock::advance(DEFAULT_HEARTBEAT_INTERVAL); event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); ASSERT_EQ(Event::HEARTBEAT, event.get().get().type()); }
// This test does not set any Accept header for the subscribe call. // The default response media type should be "application/json" in // this case. TEST_P(ExecutorHttpApiTest, NoAcceptHeader) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); ExecutorID executorId = DEFAULT_EXECUTOR_ID; MockExecutor exec(executorId); TestContainerizer containerizer(&exec); Owned<MasterDetector> detector = master.get()->createDetector(); Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), &containerizer); ASSERT_SOME(slave); MockScheduler sched; MesosSchedulerDriver driver( &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL); Future<FrameworkID> frameworkId; EXPECT_CALL(sched, registered(&driver, _, _)) .WillOnce(FutureArg<1>(&frameworkId)); Future<vector<Offer>> offers; EXPECT_CALL(sched, resourceOffers(&driver, _)) .WillOnce(FutureArg<1>(&offers)); Future<Nothing> statusUpdate; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureSatisfy(&statusUpdate)); driver.start(); AWAIT_READY(frameworkId); AWAIT_READY(offers); ASSERT_EQ(1u, offers.get().size()); EXPECT_CALL(exec, registered(_, _, _, _)) .Times(1); EXPECT_CALL(exec, launchTask(_, _)) .WillOnce(SendStatusUpdateFromTask(TASK_RUNNING)); TaskInfo taskInfo = createTask(offers.get()[0], "", executorId); driver.launchTasks(offers.get()[0].id(), {taskInfo}); // Wait until status update is received on the scheduler before sending // an executor subscribe request. AWAIT_READY(statusUpdate); // Only subscribe needs to 'Accept' JSON or protobuf. Call call; call.mutable_framework_id()->CopyFrom(evolve(frameworkId.get())); call.mutable_executor_id()->CopyFrom(evolve(executorId)); call.set_type(Call::SUBSCRIBE); call.mutable_subscribe(); // Retrieve the parameter passed as content type to this test. const ContentType contentType = GetParam(); // No 'Accept' header leads to all media types considered // acceptable. JSON will be chosen by default. process::http::Headers headers; Future<Response> response = process::http::streaming::post( slave.get()->pid, "api/v1/executor", headers, serialize(contentType, call), stringify(contentType)); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ(APPLICATION_JSON, "Content-Type", response); driver.stop(); driver.join(); }
// This test verifies that the scheduler will receive a `BadRequest` response // when a teardown call is made with an incorrect stream ID header. TEST_P(SchedulerHttpApiTest, TeardownWrongStreamId) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); // Retrieve the parameter passed as content type to this test. const string contentType = GetParam(); process::http::Headers headers = createBasicAuthHeaders(DEFAULT_CREDENTIAL); headers["Accept"] = contentType; v1::FrameworkID frameworkId; string streamId; // Subscribe once to get a valid stream ID. { Call call; call.set_type(Call::SUBSCRIBE); Call::Subscribe* subscribe = call.mutable_subscribe(); subscribe->mutable_framework_info()->CopyFrom(v1::DEFAULT_FRAMEWORK_INFO); Future<Response> response = process::http::streaming::post( master.get()->pid, "api/v1/scheduler", headers, serialize(call, contentType), contentType); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ("chunked", "Transfer-Encoding", response); ASSERT_EQ(Response::PIPE, response.get().type); ASSERT_TRUE(response.get().headers.contains("Mesos-Stream-Id")); streamId = response.get().headers.at("Mesos-Stream-Id"); Option<Pipe::Reader> reader = response.get().reader; ASSERT_SOME(reader); auto deserializer = lambda::bind( &SchedulerHttpApiTest::deserialize, this, contentType, lambda::_1); Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get()); Future<Result<Event>> event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); // Check that the event type is subscribed and the framework ID is set. ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type()); EXPECT_NE("", event.get().get().subscribed().framework_id().value()); frameworkId = event.get().get().subscribed().framework_id(); } // Subscribe again to invalidate the first stream ID and acquire another one. { Call call; call.set_type(Call::SUBSCRIBE); Call::Subscribe* subscribe = call.mutable_subscribe(); subscribe->mutable_framework_info()->CopyFrom(v1::DEFAULT_FRAMEWORK_INFO); // Set the framework ID in the subscribe call. call.mutable_framework_id()->CopyFrom(frameworkId); subscribe->mutable_framework_info()->mutable_id()->CopyFrom(frameworkId); Future<Response> response = process::http::streaming::post( master.get()->pid, "api/v1/scheduler", headers, serialize(call, contentType), contentType); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ("chunked", "Transfer-Encoding", response); ASSERT_EQ(Response::PIPE, response.get().type); ASSERT_TRUE(response.get().headers.contains("Mesos-Stream-Id")); // Make sure that the new stream ID is different. ASSERT_NE(streamId, response.get().headers.at("Mesos-Stream-Id")); Option<Pipe::Reader> reader = response.get().reader; ASSERT_SOME(reader); auto deserializer = lambda::bind( &SchedulerHttpApiTest::deserialize, this, contentType, lambda::_1); Reader<Event> responseDecoder(Decoder<Event>(deserializer), reader.get()); Future<Result<Event>> event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type()); EXPECT_NE("", event.get().get().subscribed().framework_id().value()); } { Call call; call.set_type(Call::TEARDOWN); call.mutable_framework_id()->CopyFrom(frameworkId); // Send the first (now incorrect) stream ID with the teardown call. headers["Mesos-Stream-Id"] = streamId; Future<Response> response = process::http::streaming::post( master.get()->pid, "api/v1/scheduler", headers, serialize(call, contentType), contentType); AWAIT_EXPECT_RESPONSE_STATUS_EQ(BadRequest().status, response); } }
// This test verifies if the executor is able to receive a Subscribed // event in response to a Subscribe call request. TEST_P(ExecutorHttpApiTest, Subscribe) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); ExecutorID executorId = DEFAULT_EXECUTOR_ID; MockExecutor exec(executorId); TestContainerizer containerizer(&exec); Owned<MasterDetector> detector = master.get()->createDetector(); Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), &containerizer); ASSERT_SOME(slave); MockScheduler sched; MesosSchedulerDriver driver( &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL); Future<FrameworkID> frameworkId; EXPECT_CALL(sched, registered(&driver, _, _)) .WillOnce(FutureArg<1>(&frameworkId)); Future<vector<Offer>> offers; EXPECT_CALL(sched, resourceOffers(&driver, _)) .WillOnce(FutureArg<1>(&offers)); driver.start(); AWAIT_READY(frameworkId); AWAIT_READY(offers); ASSERT_EQ(1u, offers.get().size()); Future<Message> registerExecutorMessage = DROP_MESSAGE(Eq(RegisterExecutorMessage().GetTypeName()), _, _); TaskInfo taskInfo = createTask(offers.get()[0], "", executorId); driver.launchTasks(offers.get()[0].id(), {taskInfo}); // Drop the `RegisterExecutorMessage` and then send a `Subscribe` request // from the HTTP based executor. AWAIT_READY(registerExecutorMessage); Call call; call.mutable_framework_id()->CopyFrom(evolve(frameworkId.get())); call.mutable_executor_id()->CopyFrom(evolve(executorId)); call.set_type(Call::SUBSCRIBE); call.mutable_subscribe(); // Retrieve the parameter passed as content type to this test. const ContentType contentType = GetParam(); const string contentTypeString = stringify(contentType); process::http::Headers headers; headers["Accept"] = contentTypeString; Future<Response> response = process::http::streaming::post( slave.get()->pid, "api/v1/executor", headers, serialize(contentType, call), contentTypeString); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); AWAIT_EXPECT_RESPONSE_HEADER_EQ("chunked", "Transfer-Encoding", response); AWAIT_EXPECT_RESPONSE_HEADER_EQ(contentTypeString, "Content-Type", response); ASSERT_EQ(Response::PIPE, response.get().type); Option<Pipe::Reader> reader = response.get().reader; ASSERT_SOME(reader); auto deserializer = lambda::bind(deserialize<Event>, contentType, lambda::_1); Reader<Event> responseDecoder( Decoder<Event>(deserializer), reader.get()); Future<Result<Event>> event = responseDecoder.read(); AWAIT_READY(event); ASSERT_SOME(event.get()); // Check event type is subscribed and if the ExecutorID matches. ASSERT_EQ(Event::SUBSCRIBED, event.get().get().type()); ASSERT_EQ(event.get().get().subscribed().executor_info().executor_id(), call.executor_id()); reader.get().close(); driver.stop(); driver.join(); }
// Testing route with authorization header and good credentials. TEST_F(TeardownTest, Success) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); MockScheduler sched; MesosSchedulerDriver driver( &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL); Future<FrameworkID> frameworkId; EXPECT_CALL(sched, registered(&driver, _, _)) .WillOnce(FutureArg<1>(&frameworkId)); ASSERT_EQ(DRIVER_RUNNING, driver.start()); AWAIT_READY(frameworkId); { Future<Response> response = process::http::post( master.get()->pid, "teardown", createBasicAuthHeaders(DEFAULT_CREDENTIAL), "frameworkId=" + frameworkId.get().value()); AWAIT_READY(response); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response); } // Check that the framework that was shutdown appears in the // "completed_frameworks" list in the master's "/state" endpoint. { Future<Response> response = process::http::get( master.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::Array frameworks = parse->values["frameworks"].as<JSON::Array>(); EXPECT_TRUE(frameworks.values.empty()); JSON::Array completedFrameworks = parse->values["completed_frameworks"].as<JSON::Array>(); ASSERT_EQ(1u, completedFrameworks.values.size()); JSON::Object completedFramework = completedFrameworks.values.front().as<JSON::Object>(); JSON::String completedFrameworkId = completedFramework.values["id"].as<JSON::String>(); EXPECT_EQ(frameworkId.get(), completedFrameworkId.value); } driver.stop(); driver.join(); }