Try<Subprocess> run( const string& _command, const Option<string>& rootfs = None()) { slave::MesosContainerizerLaunch::Flags launchFlags; CommandInfo command; command.set_value(_command); launchFlags.command = JSON::Protobuf(command); launchFlags.directory = "/tmp"; launchFlags.pipe_read = open("/dev/zero", O_RDONLY); launchFlags.pipe_write = open("/dev/null", O_WRONLY); launchFlags.rootfs = rootfs; vector<string> argv(2); argv[0] = "mesos-containerizer"; argv[1] = slave::MesosContainerizerLaunch::NAME; Try<Subprocess> s = subprocess( path::join(tests::flags.build_dir, "src", "mesos-containerizer"), argv, Subprocess::PATH("/dev/null"), Subprocess::FD(STDOUT_FILENO), Subprocess::FD(STDERR_FILENO), launchFlags, None(), None(), lambda::bind(&clone, lambda::_1)); close(launchFlags.pipe_read.get()); close(launchFlags.pipe_write.get()); return s; }
TEST_F(DockerTest, ROOT_DOCKER_CheckPortResource) { const string containerName = NAME_PREFIX + "-port-resource-test"; Owned<Docker> docker(Docker::create(tests::flags.docker, tests::flags.docker_socket, false).get()); // Make sure the container is removed. Future<Nothing> remove = docker->rm(containerName, true); ASSERT_TRUE(process::internal::await(remove, Seconds(10))); ContainerInfo containerInfo; containerInfo.set_type(ContainerInfo::DOCKER); ContainerInfo::DockerInfo dockerInfo; dockerInfo.set_image("busybox"); dockerInfo.set_network(ContainerInfo::DockerInfo::BRIDGE); ContainerInfo::DockerInfo::PortMapping portMapping; portMapping.set_host_port(10000); portMapping.set_container_port(80); dockerInfo.add_port_mappings()->CopyFrom(portMapping); containerInfo.mutable_docker()->CopyFrom(dockerInfo); CommandInfo commandInfo; commandInfo.set_shell(false); commandInfo.set_value("true"); Resources resources = Resources::parse("ports:[9998-9999];ports:[10001-11000]").get(); Future<Nothing> run = docker->run( containerInfo, commandInfo, containerName, "dir", "/mnt/mesos/sandbox", resources); // Port should be out side of the provided ranges. AWAIT_EXPECT_FAILED(run); resources = Resources::parse("ports:[9998-9999];ports:[10000-11000]").get(); Try<string> directory = environment->mkdtemp(); CHECK_SOME(directory) << "Failed to create temporary directory"; run = docker->run( containerInfo, commandInfo, containerName, directory.get(), "/mnt/mesos/sandbox", resources); AWAIT_READY(run); }
void offers(const vector<Offer>& offers) { CHECK_EQ(SUBSCRIBED, state); static const Try<Resources> TASK_RESOURCES = Resources::parse(resources); if (TASK_RESOURCES.isError()) { EXIT(EXIT_FAILURE) << "Failed to parse resources '" << resources << "': " << TASK_RESOURCES.error(); } foreach (const Offer& offer, offers) { Resources offered = offer.resources(); if (!launched && offered.flatten().contains(TASK_RESOURCES.get())) { TaskInfo task; task.set_name(name); task.mutable_task_id()->set_value(name); task.mutable_agent_id()->MergeFrom(offer.agent_id()); // Takes resources first from the specified role, then from '*'. Option<Resources> resources = offered.find(TASK_RESOURCES.get().flatten(frameworkInfo.role())); CHECK_SOME(resources); task.mutable_resources()->CopyFrom(resources.get()); CommandInfo* commandInfo = task.mutable_command(); if (shell) { CHECK_SOME(command); commandInfo->set_shell(true); commandInfo->set_value(command.get()); } else { // TODO(gilbert): Treat 'command' as executable value and arguments. commandInfo->set_shell(false); } if (environment.isSome()) { Environment* environment_ = commandInfo->mutable_environment(); foreachpair ( const string& name, const string& value, environment.get()) { Environment::Variable* environmentVariable = environment_->add_variables(); environmentVariable->set_name(name); environmentVariable->set_value(value); } }
vector<TaskInfo> populateTasks( const string& cmd, const string& healthCmd, const Offer& offer, int gracePeriodSeconds = 0, const Option<int>& consecutiveFailures = None(), const Option<map<string, string> >& env = None()) { CommandInfo healthCommand; healthCommand.set_value(healthCmd); return populateTasks( cmd, healthCommand, offer, gracePeriodSeconds, consecutiveFailures, env); }
Future<Option<ContainerLaunchInfo>> LinuxFilesystemIsolatorProcess::prepare( const ContainerID& containerId, const ContainerConfig& containerConfig) { const string& directory = containerConfig.directory(); Option<string> user; if (containerConfig.has_user()) { user = containerConfig.user(); } if (infos.contains(containerId)) { return Failure("Container has already been prepared"); } Owned<Info> info(new Info( directory, containerConfig.executor_info())); infos.put(containerId, info); ContainerLaunchInfo launchInfo; launchInfo.set_namespaces(CLONE_NEWNS); // Prepare the commands that will be run in the container's mount // namespace right after forking the executor process. We use these // commands to mount those volumes specified in the container info // so that they don't pollute the host mount namespace. Try<string> _script = script(containerId, containerConfig); if (_script.isError()) { return Failure("Failed to generate isolation script: " + _script.error()); } CommandInfo* command = launchInfo.add_commands(); command->set_value(_script.get()); return update(containerId, containerConfig.executor_info().resources()) .then([launchInfo]() -> Future<Option<ContainerLaunchInfo>> { return launchInfo; }); }
// This test verifies mounting in an absolute path when running a // docker container works. TEST_F(DockerTest, ROOT_DOCKER_MountAbsolute) { Owned<Docker> docker = Docker::create( tests::flags.docker, tests::flags.docker_socket, false).get(); ContainerInfo containerInfo; containerInfo.set_type(ContainerInfo::DOCKER); Try<string> directory = environment->mkdtemp(); CHECK_SOME(directory) << "Failed to create temporary directory"; const string testFile = path::join(directory.get(), "test_file"); EXPECT_SOME(os::write(testFile, "data")); Volume* volume = containerInfo.add_volumes(); volume->set_host_path(testFile); volume->set_container_path("/tmp/test_file"); volume->set_mode(Volume::RO); ContainerInfo::DockerInfo dockerInfo; dockerInfo.set_image("busybox"); containerInfo.mutable_docker()->CopyFrom(dockerInfo); CommandInfo commandInfo; commandInfo.set_shell(true); commandInfo.set_value("ls /tmp/test_file"); Future<Nothing> run = docker->run( containerInfo, commandInfo, NAME_PREFIX + "-mount-absolute-test", directory.get(), directory.get()); AWAIT_READY(run); }
vector<TaskInfo> populateTasks( const string& cmd, CommandInfo healthCommand, const Offer& offer, int gracePeriodSeconds = 0, const Option<int>& consecutiveFailures = None(), const Option<map<string, string> >& env = None()) { TaskInfo task; task.set_name(""); task.mutable_task_id()->set_value("1"); task.mutable_slave_id()->CopyFrom(offer.slave_id()); task.mutable_resources()->CopyFrom(offer.resources()); CommandInfo command; command.set_value(cmd); Environment::Variable* variable = command.mutable_environment()->add_variables(); // We need to set the correct directory to launch health check process // instead of the default for tests. variable->set_name("MESOS_LAUNCHER_DIR"); variable->set_value(path::join(tests::flags.build_dir, "src")); task.mutable_command()->CopyFrom(command); HealthCheck healthCheck; if (env.isSome()) { foreachpair (const string& name, const string value, env.get()) { Environment::Variable* variable = healthCommand.mutable_environment()->mutable_variables()->Add(); variable->set_name(name); variable->set_value(value); } }
// This test runs a command without the command user field set. The // command will verify the assumption that the command is run as the // slave user (in this case, root). TEST_F(SlaveTest, ROOT_RunTaskWithCommandInfoWithoutUser) { Try<PID<Master> > master = StartMaster(); ASSERT_SOME(master); // Need flags for 'executor_registration_timeout'. slave::Flags flags = CreateSlaveFlags(); flags.isolation = "posix/cpu,posix/mem"; Try<MesosContainerizer*> containerizer = MesosContainerizer::create(flags, false); CHECK_SOME(containerizer); Try<PID<Slave> > slave = StartSlave(containerizer.get()); 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()); // Launch a task with the command executor. TaskInfo task; task.set_name(""); task.mutable_task_id()->set_value("1"); task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id()); task.mutable_resources()->MergeFrom(offers.get()[0].resources()); Result<string> user = os::user(); CHECK_SOME(user) << "Failed to get current user name" << (user.isError() ? ": " + user.error() : ""); // Command executor will run as user running test. CommandInfo command; command.set_value("test `whoami` = " + user.get()); task.mutable_command()->MergeFrom(command); vector<TaskInfo> tasks; tasks.push_back(task); Future<TaskStatus> statusRunning; Future<TaskStatus> statusFinished; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&statusRunning)) .WillOnce(FutureArg<1>(&statusFinished)); driver.launchTasks(offers.get()[0].id(), tasks); AWAIT_READY(statusRunning); EXPECT_EQ(TASK_RUNNING, statusRunning.get().state()); AWAIT_READY(statusFinished); EXPECT_EQ(TASK_FINISHED, statusFinished.get().state()); driver.stop(); driver.join(); Shutdown(); // Must shutdown before 'containerizer' gets deallocated. }
// Test that we can run the mesos-executor and specify an "override" // command to use via the --override argument. TEST_F(SlaveTest, MesosExecutorWithOverride) { Try<PID<Master> > master = StartMaster(); ASSERT_SOME(master); TestContainerizer containerizer; Try<PID<Slave> > slave = StartSlave(&containerizer); 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()); // Launch a task with the command executor. TaskInfo task; task.set_name(""); task.mutable_task_id()->set_value("1"); task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id()); task.mutable_resources()->MergeFrom(offers.get()[0].resources()); CommandInfo command; command.set_value("sleep 10"); task.mutable_command()->MergeFrom(command); vector<TaskInfo> tasks; tasks.push_back(task); // Expect the launch and just assume it was sucessful since we'll be // launching the executor ourselves manually below. Future<Nothing> launch; EXPECT_CALL(containerizer, launch(_, _, _, _, _, _, _)) .WillOnce(DoAll(FutureSatisfy(&launch), Return(true))); // Expect wait after launch is called but don't return anything // until after we've finished everything below. Future<Nothing> wait; process::Promise<containerizer::Termination> promise; EXPECT_CALL(containerizer, wait(_)) .WillOnce(DoAll(FutureSatisfy(&wait), Return(promise.future()))); driver.launchTasks(offers.get()[0].id(), tasks); // Once we get the launch the mesos-executor with --override. AWAIT_READY(launch); // Set up fake environment for executor. map<string, string> environment; environment["MESOS_SLAVE_PID"] = stringify(slave.get()); environment["MESOS_SLAVE_ID"] = stringify(offers.get()[0].slave_id()); environment["MESOS_FRAMEWORK_ID"] = stringify(offers.get()[0].framework_id()); environment["MESOS_EXECUTOR_ID"] = stringify(task.task_id()); environment["MESOS_DIRECTORY"] = ""; // Create temporary file to store validation string. If command is // succesfully replaced, this file will end up containing the string // 'Hello World\n'. Otherwise, the original task command i.e. // 'sleep' will be called and the test will fail. Try<std::string> file = os::mktemp(); ASSERT_SOME(file); string executorCommand = path::join(tests::flags.build_dir, "src", "mesos-executor") + " --override -- /bin/sh -c 'echo hello world >" + file.get() + "'"; // Expect two status updates, one for once the mesos-executor says // the task is running and one for after our overridden command // above finishes. Future<TaskStatus> status1, status2; EXPECT_CALL(sched, statusUpdate(_, _)) .WillOnce(FutureArg<1>(&status1)) .WillOnce(FutureArg<1>(&status2)); Try<process::Subprocess> executor = process::subprocess( executorCommand, process::Subprocess::PIPE(), process::Subprocess::PIPE(), process::Subprocess::PIPE(), environment); ASSERT_SOME(executor); // Scheduler should receive the TASK_RUNNING update. AWAIT_READY(status1); ASSERT_EQ(TASK_RUNNING, status1.get().state()); AWAIT_READY(status2); ASSERT_EQ(TASK_FINISHED, status2.get().state()); AWAIT_READY(wait); containerizer::Termination termination; termination.set_killed(false); termination.set_message("Killed executor"); termination.set_status(0); promise.set(termination); driver.stop(); driver.join(); AWAIT_READY(executor.get().status()); // Verify file contents. Try<std::string> validate = os::read(file.get()); ASSERT_SOME(validate); EXPECT_EQ(validate.get(), "hello world\n"); os::rm(file.get()); Shutdown(); }
// Test that the prepare launch docker hook execute before launch // a docker container. Test hook create a file "foo" in the sandbox // directory. When the docker container launched, the sandbox directory // is mounted to the docker container. We validate the hook by verifying // the "foo" file exists in the docker container or not. TEST_F(HookTest, ROOT_DOCKER_VerifySlavePreLaunchDockerHook) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); MockDocker* mockDocker = new MockDocker(tests::flags.docker, tests::flags.docker_socket); Shared<Docker> docker(mockDocker); slave::Flags flags = CreateSlaveFlags(); Fetcher fetcher; Try<ContainerLogger*> logger = ContainerLogger::create(flags.container_logger); ASSERT_SOME(logger); MockDockerContainerizer containerizer( flags, &fetcher, Owned<ContainerLogger>(logger.get()), docker); Owned<MasterDetector> detector = master.get()->createDetector(); Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), &containerizer, flags); 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)) .WillRepeatedly(Return()); // Ignore subsequent offers. driver.start(); AWAIT_READY(frameworkId); AWAIT_READY(offers); ASSERT_NE(0u, offers.get().size()); const Offer& offer = offers.get()[0]; SlaveID slaveId = offer.slave_id(); TaskInfo task; task.set_name(""); task.mutable_task_id()->set_value("1"); task.mutable_slave_id()->CopyFrom(offer.slave_id()); task.mutable_resources()->CopyFrom(offer.resources()); CommandInfo command; command.set_value("test -f " + path::join(flags.sandbox_directory, "foo")); ContainerInfo containerInfo; containerInfo.set_type(ContainerInfo::DOCKER); // TODO(tnachen): Use local image to test if possible. ContainerInfo::DockerInfo dockerInfo; dockerInfo.set_image("alpine"); containerInfo.mutable_docker()->CopyFrom(dockerInfo); task.mutable_command()->CopyFrom(command); task.mutable_container()->CopyFrom(containerInfo); vector<TaskInfo> tasks; tasks.push_back(task); Future<ContainerID> containerId; EXPECT_CALL(containerizer, launch(_, _, _, _, _, _, _, _)) .WillOnce(DoAll(FutureArg<0>(&containerId), Invoke(&containerizer, &MockDockerContainerizer::_launch))); Future<TaskStatus> statusRunning; Future<TaskStatus> statusFinished; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&statusRunning)) .WillOnce(FutureArg<1>(&statusFinished)) .WillRepeatedly(DoDefault()); driver.launchTasks(offers.get()[0].id(), tasks); AWAIT_READY_FOR(containerId, Seconds(60)); AWAIT_READY_FOR(statusRunning, Seconds(60)); EXPECT_EQ(TASK_RUNNING, statusRunning.get().state()); AWAIT_READY_FOR(statusFinished, Seconds(60)); EXPECT_EQ(TASK_FINISHED, statusFinished.get().state()); Future<containerizer::Termination> termination = containerizer.wait(containerId.get()); driver.stop(); driver.join(); AWAIT_READY(termination); Future<list<Docker::Container>> containers = docker.get()->ps(true, slave::DOCKER_NAME_PREFIX); AWAIT_READY(containers); // Cleanup all mesos launched containers. foreach (const Docker::Container& container, containers.get()) { AWAIT_READY_FOR(docker.get()->rm(container.id, true), Seconds(30)); } }
// This test verifies that the environment secrets are resolved when launching a // task. TEST_F(EnvironmentSecretIsolatorTest, ResolveSecret) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); mesos::internal::slave::Flags flags = CreateSlaveFlags(); Fetcher fetcher(flags); Try<SecretResolver*> secretResolver = SecretResolver::create(); EXPECT_SOME(secretResolver); Try<MesosContainerizer*> containerizer = MesosContainerizer::create(flags, false, &fetcher, secretResolver.get()); EXPECT_SOME(containerizer); Owned<MasterDetector> detector = master.get()->createDetector(); Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), containerizer.get()); ASSERT_SOME(slave); MockScheduler sched; MesosSchedulerDriver driver( &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL); EXPECT_CALL(sched, registered(&driver, _, _)); Future<std::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->empty()); const string commandString = strings::format( "env; test \"$%s\" = \"%s\"", SECRET_ENV_NAME, SECRET_VALUE).get(); CommandInfo command; command.set_value(commandString); // Request a secret. // TODO(kapil): Update createEnvironment() to support secrets. mesos::Environment::Variable *env = command.mutable_environment()->add_variables(); env->set_name(SECRET_ENV_NAME); env->set_type(mesos::Environment::Variable::SECRET); mesos::Secret* secret = env->mutable_secret(); secret->set_type(Secret::VALUE); secret->mutable_value()->set_data(SECRET_VALUE); TaskInfo task = createTask( offers.get()[0].slave_id(), Resources::parse("cpus:0.1;mem:32").get(), command); // NOTE: Successful tasks will output two status updates. Future<TaskStatus> statusRunning; Future<TaskStatus> statusFinished; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&statusRunning)) .WillOnce(FutureArg<1>(&statusFinished)); driver.launchTasks(offers.get()[0].id(), {task}); AWAIT_READY(statusRunning); EXPECT_EQ(TASK_RUNNING, statusRunning.get().state()); AWAIT_READY(statusFinished); EXPECT_EQ(TASK_FINISHED, statusFinished.get().state()); driver.stop(); driver.join(); }
// This test launches a container which has an image and joins host // network, and then verifies that the container can access Internet. TEST_F(CniIsolatorTest, ROOT_INTERNET_CURL_LaunchContainerInHostNetwork) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); slave::Flags flags = CreateSlaveFlags(); flags.isolation = "docker/runtime,filesystem/linux"; flags.image_providers = "docker"; flags.docker_store_dir = path::join(sandbox.get(), "store"); Owned<MasterDetector> detector = master.get()->createDetector(); Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), flags); 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_EQ(1u, offers->size()); const Offer& offer = offers.get()[0]; // NOTE: We use a non-shell command here because 'sh' might not be // in the PATH. 'alpine' does not specify env PATH in the image. CommandInfo command; command.set_shell(false); command.set_value("/bin/ping"); command.add_arguments("/bin/ping"); command.add_arguments("-c1"); command.add_arguments("google.com"); TaskInfo task = createTask( offer.slave_id(), Resources::parse("cpus:1;mem:128").get(), command); Image image; image.set_type(Image::DOCKER); image.mutable_docker()->set_name("alpine"); ContainerInfo* container = task.mutable_container(); container->set_type(ContainerInfo::MESOS); container->mutable_mesos()->mutable_image()->CopyFrom(image); Future<TaskStatus> statusRunning; Future<TaskStatus> statusFinished; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&statusRunning)) .WillOnce(FutureArg<1>(&statusFinished)); driver.launchTasks(offer.id(), {task}); AWAIT_READY_FOR(statusRunning, Seconds(60)); EXPECT_EQ(task.task_id(), statusRunning->task_id()); EXPECT_EQ(TASK_RUNNING, statusRunning->state()); AWAIT_READY(statusFinished); EXPECT_EQ(task.task_id(), statusFinished->task_id()); EXPECT_EQ(TASK_FINISHED, statusFinished->state()); driver.stop(); driver.join(); }
// This test launches a command task which has checkpoint enabled, and // agent is terminated when the task is running, after agent is restarted, // kill the task and then verify we can receive TASK_KILLED for the task. TEST_F(CniIsolatorTest, ROOT_SlaveRecovery) { Try<Owned<cluster::Master>> master = StartMaster(); ASSERT_SOME(master); slave::Flags flags = CreateSlaveFlags(); flags.isolation = "network/cni"; flags.network_cni_plugins_dir = cniPluginDir; flags.network_cni_config_dir = cniConfigDir; Owned<MasterDetector> detector = master.get()->createDetector(); Try<Owned<cluster::Slave>> slave = StartSlave(detector.get(), flags); ASSERT_SOME(slave); MockScheduler sched; // Enable checkpointing for the framework. FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO; frameworkInfo.set_checkpoint(true); MesosSchedulerDriver driver( &sched, frameworkInfo, master.get()->pid, DEFAULT_CREDENTIAL); EXPECT_CALL(sched, registered(_, _, _)); Future<vector<Offer>> offers; EXPECT_CALL(sched, resourceOffers(&driver, _)) .WillOnce(FutureArg<1>(&offers)) .WillRepeatedly(Return()); // Ignore subsequent offers. driver.start(); AWAIT_READY(offers); ASSERT_EQ(1u, offers->size()); const Offer& offer = offers.get()[0]; CommandInfo command; command.set_value("sleep 1000"); TaskInfo task = createTask( offer.slave_id(), Resources::parse("cpus:1;mem:128").get(), command); ContainerInfo* container = task.mutable_container(); container->set_type(ContainerInfo::MESOS); // Make sure the container join the mock CNI network. container->add_network_infos()->set_name("__MESOS_TEST__"); Future<TaskStatus> statusRunning; Future<TaskStatus> statusKilled; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&statusRunning)) .WillOnce(FutureArg<1>(&statusKilled)); EXPECT_CALL(sched, offerRescinded(&driver, _)) .Times(AtMost(1)); Future<Nothing> ack = FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement); driver.launchTasks(offer.id(), {task}); AWAIT_READY(statusRunning); EXPECT_EQ(task.task_id(), statusRunning->task_id()); EXPECT_EQ(TASK_RUNNING, statusRunning->state()); // Wait for the ACK to be checkpointed. AWAIT_READY(ack); // Stop the slave after TASK_RUNNING is received. slave.get()->terminate(); // Restart the slave. slave = StartSlave(detector.get(), flags); ASSERT_SOME(slave); // Kill the task. driver.killTask(task.task_id()); AWAIT_READY(statusKilled); EXPECT_EQ(task.task_id(), statusKilled->task_id()); EXPECT_EQ(TASK_KILLED, statusKilled->state()); driver.stop(); driver.join(); }
// This test runs a command _with_ the command user field set. The // command will verify the assumption that the command is run as the // specified user. We use (and assume the precense) of the // unprivileged 'nobody' user which should be available on both Linux // and Mac OS X. TEST_F(SlaveTest, DISABLED_ROOT_RunTaskWithCommandInfoWithUser) { // TODO(nnielsen): Introduce STOUT abstraction for user verification // instead of flat getpwnam call. const string testUser = "******"; if (::getpwnam(testUser.c_str()) == NULL) { LOG(WARNING) << "Cannot run ROOT_RunTaskWithCommandInfoWithUser test:" << " user '" << testUser << "' is not present"; return; } Try<PID<Master> > master = StartMaster(); ASSERT_SOME(master); // Need flags for 'executor_registration_timeout'. slave::Flags flags = CreateSlaveFlags(); flags.isolation = "posix/cpu,posix/mem"; Try<MesosContainerizer*> containerizer = MesosContainerizer::create(flags, false); CHECK_SOME(containerizer); Try<PID<Slave> > slave = StartSlave(containerizer.get()); 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()); // Launch a task with the command executor. TaskInfo task; task.set_name(""); task.mutable_task_id()->set_value("1"); task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id()); task.mutable_resources()->MergeFrom(offers.get()[0].resources()); CommandInfo command; command.set_value("test `whoami` = " + testUser); command.set_user(testUser); task.mutable_command()->MergeFrom(command); vector<TaskInfo> tasks; tasks.push_back(task); Future<TaskStatus> statusRunning; Future<TaskStatus> statusFinished; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&statusRunning)) .WillOnce(FutureArg<1>(&statusFinished)); driver.launchTasks(offers.get()[0].id(), tasks); AWAIT_READY(statusRunning); EXPECT_EQ(TASK_RUNNING, statusRunning.get().state()); AWAIT_READY(statusFinished); EXPECT_EQ(TASK_FINISHED, statusFinished.get().state()); driver.stop(); driver.join(); Shutdown(); // Must shutdown before 'containerizer' gets deallocated. }
virtual void resourceOffers( SchedulerDriver* driver, const vector<Offer>& offers) { static const Try<Resources> TASK_RESOURCES = Resources::parse(resources); if (TASK_RESOURCES.isError()) { cerr << "Failed to parse resources '" << resources << "': " << TASK_RESOURCES.error() << endl; driver->abort(); return; } foreach (const Offer& offer, offers) { if (!launched && Resources(offer.resources()).contains(TASK_RESOURCES.get())) { TaskInfo task; task.set_name(name); task.mutable_task_id()->set_value(name); task.mutable_slave_id()->MergeFrom(offer.slave_id()); task.mutable_resources()->CopyFrom(TASK_RESOURCES.get()); CommandInfo* commandInfo = task.mutable_command(); commandInfo->set_value(command); if (environment.isSome()) { Environment* environment_ = commandInfo->mutable_environment(); foreachpair (const std::string& name, const std::string& value, environment.get()) { Environment_Variable* environmentVariable = environment_->add_variables(); environmentVariable->set_name(name); environmentVariable->set_value(value); } } if (uri.isSome()) { task.mutable_command()->add_uris()->set_value(uri.get()); } if (dockerImage.isSome()) { ContainerInfo containerInfo; if (containerizer == "mesos") { containerInfo.set_type(ContainerInfo::MESOS); ContainerInfo::MesosInfo mesosInfo; Image mesosImage; mesosImage.set_type(Image::DOCKER); mesosImage.mutable_docker()->set_name(dockerImage.get()); mesosInfo.mutable_image()->CopyFrom(mesosImage); containerInfo.mutable_mesos()->CopyFrom(mesosInfo); } else if (containerizer == "docker") { containerInfo.set_type(ContainerInfo::DOCKER); ContainerInfo::DockerInfo dockerInfo; dockerInfo.set_image(dockerImage.get()); containerInfo.mutable_docker()->CopyFrom(dockerInfo); } else { cerr << "Unsupported containerizer: " << containerizer << endl;; driver->abort(); return; } task.mutable_container()->CopyFrom(containerInfo); } vector<TaskInfo> tasks; tasks.push_back(task); driver->launchTasks(offer.id(), tasks); cout << "task " << name << " submitted to slave " << offer.slave_id() << endl; launched = true; } else {
TEST_F(SlaveTest, ShutdownUnregisteredExecutor) { Try<PID<Master> > master = StartMaster(); ASSERT_SOME(master); // Need flags for 'executor_registration_timeout'. slave::Flags flags = CreateSlaveFlags(); // Set the isolation flag so we know a MesoContainerizer will be created. flags.isolation = "posix/cpu,posix/mem"; Try<MesosContainerizer*> containerizer = MesosContainerizer::create(flags, false); CHECK_SOME(containerizer); Try<PID<Slave> > slave = StartSlave(containerizer.get()); 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()); // Launch a task with the command executor. TaskInfo task; task.set_name(""); task.mutable_task_id()->set_value("1"); task.mutable_slave_id()->MergeFrom(offers.get()[0].slave_id()); task.mutable_resources()->MergeFrom(offers.get()[0].resources()); CommandInfo command; command.set_value("sleep 10"); task.mutable_command()->MergeFrom(command); vector<TaskInfo> tasks; tasks.push_back(task); // Drop the registration message from the executor to the slave. Future<process::Message> registerExecutor = DROP_MESSAGE(Eq(RegisterExecutorMessage().GetTypeName()), _, _); driver.launchTasks(offers.get()[0].id(), tasks); AWAIT_READY(registerExecutor); Clock::pause(); Future<TaskStatus> status; EXPECT_CALL(sched, statusUpdate(&driver, _)) .WillOnce(FutureArg<1>(&status)); // Ensure that the slave times out and kills the executor. Future<Nothing> destroyExecutor = FUTURE_DISPATCH(_, &MesosContainerizerProcess::destroy); Clock::advance(flags.executor_registration_timeout); AWAIT_READY(destroyExecutor); Clock::settle(); // Wait for Containerizer::destroy to complete. // Now advance time until the reaper reaps the executor. while (status.isPending()) { Clock::advance(Seconds(1)); Clock::settle(); } AWAIT_READY(status); ASSERT_EQ(TASK_FAILED, status.get().state()); Clock::resume(); driver.stop(); driver.join(); Shutdown(); // Must shutdown before 'containerizer' gets deallocated. }
// This test tests the functionality of the docker's interfaces. TEST(DockerTest, ROOT_DOCKER_interface) { string containerName = "mesos-docker-test"; Resources resources = Resources::parse("cpus:1;mem:512").get(); Docker docker = Docker::create(tests::flags.docker, false).get(); // Cleaning up the container first if it exists. Future<Nothing> status = docker.rm(containerName, true); ASSERT_TRUE(status.await(Seconds(10))); // Verify that we do not see the container. Future<list<Docker::Container> > containers = docker.ps(true, containerName); AWAIT_READY(containers); foreach (const Docker::Container& container, containers.get()) { EXPECT_NE("/" + containerName, container.name); } Try<string> directory = environment->mkdtemp(); CHECK_SOME(directory) << "Failed to create temporary directory"; ContainerInfo containerInfo; containerInfo.set_type(ContainerInfo::DOCKER); ContainerInfo::DockerInfo dockerInfo; dockerInfo.set_image("busybox"); containerInfo.mutable_docker()->CopyFrom(dockerInfo); CommandInfo commandInfo; commandInfo.set_value("sleep 120"); // Start the container. status = docker.run( containerInfo, commandInfo, containerName, directory.get(), "/mnt/mesos/sandbox", resources); AWAIT_READY(status); // Should be able to see the container now. containers = docker.ps(); AWAIT_READY(containers); bool found = false; foreach (const Docker::Container& container, containers.get()) { if ("/" + containerName == container.name) { found = true; break; } } EXPECT_TRUE(found); Future<Docker::Container> container = docker.inspect(containerName); AWAIT_READY(container); // Test some fields of the container. EXPECT_NE("", container.get().id); EXPECT_EQ("/" + containerName, container.get().name); EXPECT_SOME(container.get().pid); // Kill the container. status = docker.kill(containerName); AWAIT_READY(status); // Now, the container should not appear in the result of ps(). // But it should appear in the result of ps(true). containers = docker.ps(); AWAIT_READY(containers); foreach (const Docker::Container& container, containers.get()) { EXPECT_NE("/" + containerName, container.name); } containers = docker.ps(true, containerName); AWAIT_READY(containers); found = false; foreach (const Docker::Container& container, containers.get()) { if ("/" + containerName == container.name) { found = true; break; } } EXPECT_TRUE(found); // Check the container's info, both id and name should remain the // same since we haven't removed it, but the pid should be none // since it's not running. container = docker.inspect(containerName); AWAIT_READY(container); EXPECT_NE("", container.get().id); EXPECT_EQ("/" + containerName, container.get().name); EXPECT_NONE(container.get().pid); // Remove the container. status = docker.rm(containerName); AWAIT_READY(status); // Should not be able to inspect the container. container = docker.inspect(containerName); AWAIT_FAILED(container); // Also, now we should not be able to see the container by invoking // ps(true). containers = docker.ps(true, containerName); AWAIT_READY(containers); foreach (const Docker::Container& container, containers.get()) { EXPECT_NE("/" + containerName, container.name); } // Start the container again, this time we will do a "rm -f" // directly, instead of killing and rm. status = docker.run( containerInfo, commandInfo, containerName, directory.get(), "/mnt/mesos/sandbox", resources); AWAIT_READY(status); // Verify that the container is there. containers = docker.ps(); AWAIT_READY(containers); found = false; foreach (const Docker::Container& container, containers.get()) { if ("/" + containerName == container.name) { found = true; break; } } EXPECT_TRUE(found); // Then do a "rm -f". status = docker.rm(containerName, true); AWAIT_READY(status); // Verify that the container is totally removed, that is we can't // find it by ps() or ps(true). containers = docker.ps(); AWAIT_READY(containers); foreach (const Docker::Container& container, containers.get()) { EXPECT_NE("/" + containerName, container.name); } containers = docker.ps(true, containerName); AWAIT_READY(containers); foreach (const Docker::Container& container, containers.get()) { EXPECT_NE("/" + containerName, container.name); } }