Option<Error> validateContainerId(const ContainerID& containerId) { const string& id = containerId.value(); // Check common Mesos ID rules. Option<Error> error = common::validation::validateID(id); if (error.isSome()) { return Error(error->message); } // Check ContainerID specific rules. // // Periods are disallowed because our string representation of // ContainerID uses periods: <uuid>.<child>.<grandchild>. // For example: <uuid>.redis.backup // // Spaces are disallowed as they can render logs confusing and // need escaping on terminals when dealing with paths. auto invalidCharacter = [](char c) { return c == '.' || c == ' '; }; if (std::any_of(id.begin(), id.end(), invalidCharacter)) { return Error("'ContainerID.value' '" + id + "'" " contains invalid characters"); } // TODO(bmahler): Print the invalid field nicely within the error // (e.g. 'parent.parent.parent.value'). For now we only have one // level of nesting so it's ok. if (containerId.has_parent()) { Option<Error> parentError = validateContainerId(containerId.parent()); if (parentError.isSome()) { return Error("'ContainerID.parent' is invalid: " + parentError->message); } } return None(); }
// This test verifies that access to the '/flags' endpoint can be authorized // without authentication if an authorization rule exists that applies to // anyone. The authorizer will map the absence of a principal to "ANY". TYPED_TEST(SlaveAuthorizerTest, AuthorizeFlagsEndpointWithoutPrincipal) { const string endpoint = "flags"; // Because the authenticators' lifetime is tied to libprocess's lifetime, // it may already be set by other tests. We have to unset it here to disable // HTTP authentication. // TODO(nfnt): Fix this behavior. The authenticator should be unset by // every test case that sets it, similar to how it's done for the master. http::authentication::unsetAuthenticator( slave::DEFAULT_HTTP_AUTHENTICATION_REALM); // Setup ACLs so that any principal can access the '/flags' endpoint. ACLs acls; acls.set_permissive(false); mesos::ACL::GetEndpoint* acl = acls.add_get_endpoints(); acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY); acl->mutable_paths()->add_values("/" + endpoint); slave::Flags agentFlags = this->CreateSlaveFlags(); agentFlags.acls = acls; agentFlags.authenticate_http = false; agentFlags.http_credentials = None(); // Create an `Authorizer` with the ACLs. Try<Authorizer*> create = TypeParam::create(parameterize(acls)); ASSERT_SOME(create); Owned<Authorizer> authorizer(create.get()); StandaloneMasterDetector detector; Try<Owned<cluster::Slave>> agent = this->StartSlave( &detector, authorizer.get(), agentFlags); ASSERT_SOME(agent); Future<Response> response = http::get(agent.get()->pid, endpoint); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response) << response.get().body; }
void CNamingTreeCtrl::OnCopy() { // TODO: Add your command handler code here CNamingObject* pObject = GetTreeObject(); try { CString IOR = m_pORB->object_to_string(pObject->Object()); // Copy to the clipboard by using the CEdit control. This is easier // that doing it the right way CEdit Temp; CRect None(0,0, 1, 1); Temp.Create(0, None, this, 0); Temp.SetWindowText(IOR); Temp.SetSel(0, IOR.GetLength()); Temp.Copy(); Temp.PostMessage(WM_CLOSE); } catch(CORBA::Exception& ex) { MessageBox(ACE_TEXT_CHAR_TO_TCHAR (ex._rep_id()), ACE_TEXT ("CORBA::Exception")); } }
// Tests the archive APIs for a simple file. TEST_F(TarTest, File) { // Create a test file. const Path testFile("testfile"); ASSERT_SOME(createTestFile(testFile)); // Archive the test file. const Path outputTarFile("test.tar"); AWAIT_ASSERT_READY(command::tar(testFile, outputTarFile, None())); ASSERT_TRUE(os::exists(outputTarFile)); // Remove the test file to make sure untar process creates new test file. ASSERT_SOME(os::rm(testFile)); ASSERT_FALSE(os::exists(testFile)); // Untar the tarball and verify that the original file is created. AWAIT_ASSERT_READY(command::untar(outputTarFile)); ASSERT_TRUE(os::exists(testFile)); // Verify that the content is same as original file. EXPECT_SOME_EQ("test", os::read(testFile)); }
namespace uri { /** * Construct an URI with the given parameters. No validation will be * performed in this function. */ URI construct( const std::string& scheme, const std::string& path = "", const Option<std::string>& host = None(), const Option<int>& port = None(), const Option<std::string>& query = None(), const Option<std::string>& fragment = None(), const Option<std::string>& user = None(), const Option<std::string>& password = None()); } // namespace uri {
// Tests that an agent endpoint handler forms // correct queries against the authorizer. TEST_P(SlaveEndpointTest, AuthorizedRequest) { const string endpoint = GetParam(); StandaloneMasterDetector detector; MockAuthorizer mockAuthorizer; Try<Owned<cluster::Slave>> agent = StartSlave(&detector, &mockAuthorizer); ASSERT_SOME(agent); Future<authorization::Request> request; EXPECT_CALL(mockAuthorizer, authorized(_)) .WillOnce(DoAll(FutureArg<0>(&request), Return(true))); Future<Response> response = http::get( agent.get()->pid, endpoint, None(), createBasicAuthHeaders(DEFAULT_CREDENTIAL)); AWAIT_READY(request); const string principal = DEFAULT_CREDENTIAL.principal(); EXPECT_EQ(principal, request.get().subject().value()); // TODO(bbannier): Once agent endpoint handlers use more than just // `GET_ENDPOINT_WITH_PATH` we should factor out the request method // and expected authorization action and parameterize // `SlaveEndpointTest` on that as well in addition to the endpoint. EXPECT_EQ(authorization::GET_ENDPOINT_WITH_PATH, request.get().action()); EXPECT_EQ("/" + endpoint, request.get().object().value()); AWAIT_EXPECT_RESPONSE_STATUS_EQ(OK().status, response) << response.get().body; }
TEST_F(ProtobufIOTest, Append) { const string file = ".protobuf_io_test_append"; const size_t writes = 10; for (size_t i = 0; i < writes; i++) { FrameworkID frameworkId; frameworkId.set_value(stringify(i)); Try<Nothing> result = ::protobuf::append(file, frameworkId); ASSERT_SOME(result); } Try<int_fd> fd = os::open( file, O_CREAT | O_RDONLY | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ASSERT_SOME(fd); Result<FrameworkID> read = None(); size_t reads = 0; while (true) { read = ::protobuf::read<FrameworkID>(fd.get()); if (!read.isSome()) { break; } EXPECT_EQ(read->value(), stringify(reads++)); } // Ensure we've hit the end of the file without reading a partial // protobuf. ASSERT_TRUE(read.isNone()); ASSERT_EQ(writes, reads); os::close(fd.get()); }
// Returns the netlink link object associated with a given link by its // interface index. Returns None if the link is not found. inline Result<Netlink<struct rtnl_link>> get(int index) { Try<Netlink<struct nl_sock>> socket = routing::socket(); if (socket.isError()) { return Error(socket.error()); } // Dump all the netlink link objects from kernel. Note that the flag // AF_UNSPEC means all available families. struct nl_cache* c = NULL; int error = rtnl_link_alloc_cache(socket.get().get(), AF_UNSPEC, &c); if (error != 0) { return Error(nl_geterror(error)); } Netlink<struct nl_cache> cache(c); struct rtnl_link* l = rtnl_link_get(cache.get(), index); if (l == NULL) { return None(); } return Netlink<struct rtnl_link>(l); }
// Launch 'ping' using the given capabilities and user. Try<Subprocess> ping( const Set<Capability>& capabilities, const Option<string>& user = None()) { CapabilitiesTestHelper helper; helper.flags.user = user; helper.flags.capabilities = capabilities::convert(capabilities); vector<string> argv = { "test-helper", CapabilitiesTestHelper::NAME }; return subprocess( getTestHelperPath("test-helper"), argv, Subprocess::FD(STDIN_FILENO), Subprocess::FD(STDOUT_FILENO), Subprocess::FD(STDERR_FILENO), NO_SETSID, &helper.flags); }
Try<Nothing> createTestFile( const Path& file, const Option<Path>& directory = None()) { const Path testFile( directory.isSome() ? path::join(directory.get(), file.value): file); const string& testFileDir = testFile.dirname(); if (!os::exists(testFileDir)) { Try<Nothing> mkdir = os::mkdir(testFileDir, true); if (mkdir.isError()) { return Error("Failed to create test directory: " + mkdir.error()); } } Try<Nothing> write = os::write(testFile, "test"); if (write.isError()) { return Error("Failed to create to test file: " + write.error()); } return Nothing(); }
// Tests that unauthorized requests for an agent endpoint are properly rejected. TEST_P(SlaveEndpointTest, UnauthorizedRequest) { const string endpoint = GetParam(); StandaloneMasterDetector detector; MockAuthorizer mockAuthorizer; Try<Owned<cluster::Slave>> agent = StartSlave(&detector, &mockAuthorizer); ASSERT_SOME(agent); EXPECT_CALL(mockAuthorizer, authorized(_)) .WillOnce(Return(false)); Future<Response> response = http::get( agent.get()->pid, endpoint, None(), createBasicAuthHeaders(DEFAULT_CREDENTIAL)); AWAIT_EXPECT_RESPONSE_STATUS_EQ(Forbidden().status, response) << response.get().body; }
int main(int argc, char** argv) { #ifdef __WINDOWS__ // Initialize the Windows socket stack. process::Winsock winsock; #endif // Initialize Google Mock/Test. testing::InitGoogleMock(&argc, argv); // Initialize libprocess. process::initialize(None(), process::DEFAULT_HTTP_AUTHENTICATION_REALM); // NOTE: Windows does not support signal semantics required for these // handlers to be useful. #ifndef __WINDOWS__ // Install GLOG's signal handler. google::InstallFailureSignalHandler(); // We reset the GLOG's signal handler for SIGTERM because // 'SubprocessTest.Status' sends SIGTERM to a subprocess which // results in a stack trace otherwise. os::signals::reset(SIGTERM); #endif // __WINDOWS__ // Add the libprocess test event listeners. ::testing::TestEventListeners& listeners = ::testing::UnitTest::GetInstance()->listeners(); listeners.Append(process::ClockTestEventListener::instance()); listeners.Append(process::FilterTestEventListener::instance()); int result = RUN_ALL_TESTS(); process::finalize(); return result; }
// Test that the environment decorator hook adds a new environment // variable to the executor runtime. // Test hook adds a new environment variable "FOO" to the executor // with a value "bar". We validate the hook by verifying the value // of this environment variable. TEST_F(HookTest, VerifySlaveExecutorEnvironmentDecorator) { const string& directory = os::getcwd(); // We're inside a temporary sandbox. Fetcher fetcher; Try<MesosContainerizer*> containerizer = MesosContainerizer::create(CreateSlaveFlags(), false, &fetcher); ASSERT_SOME(containerizer); ContainerID containerId; containerId.set_value("test_container"); // Test hook adds a new environment variable "FOO" to the executor // with a value "bar". A '0' (success) exit status for the following // command validates the hook. process::Future<bool> launch = containerizer.get()->launch( containerId, CREATE_EXECUTOR_INFO("executor", "test $FOO = 'bar'"), directory, None(), SlaveID(), process::PID<Slave>(), false); AWAIT_READY(launch); ASSERT_TRUE(launch.get()); // Wait on the container. process::Future<containerizer::Termination> wait = containerizer.get()->wait(containerId); AWAIT_READY(wait); // Check the executor exited correctly. EXPECT_TRUE(wait.get().has_status()); EXPECT_EQ(0, wait.get().status()); delete containerizer.get(); }
// Tests the archive APIs for a simple directory. TEST_F(TarTest, Directory) { const Path testDir("test_dir"); const Path testFile(path::join(testDir, "testfile")); // Create a test file in the test directory. ASSERT_SOME(createTestFile(testFile)); // Archive the test directory. const Path outputTarFile("test_dir.tar"); AWAIT_ASSERT_READY(command::tar(testDir, outputTarFile, None())); ASSERT_TRUE(os::exists(outputTarFile)); // Remove the test directory to make sure untar process creates new test file. ASSERT_SOME(os::rmdir(testDir)); ASSERT_FALSE(os::exists(testDir)); // Untar the tarball and verify that the original directory is created. AWAIT_ASSERT_READY(command::untar(outputTarFile)); ASSERT_TRUE(os::exists(testDir)); // Verify that the content is same as original file. EXPECT_SOME_EQ("test", os::read(testFile)); }
std::deque<http::Response*> decode(const char* data, size_t length) { size_t parsed = http_parser_execute(&parser, &settings, data, length); if (parsed != length) { // TODO(bmahler): joyent/http-parser exposes error reasons. failure = true; // If we're still writing the body, fail the writer! if (writer.isSome()) { http::Pipe::Writer writer_ = writer.get(); // Remove const. writer_.fail("failed to decode body"); writer = None(); } } if (!responses.empty()) { std::deque<http::Response*> result = responses; responses.clear(); return result; } return std::deque<http::Response*>(); }
OK(const JSON::Value& value, const Option<std::string>& jsonp = None()) { type = BODY; status = "200 OK"; std::ostringstream out; if (jsonp.isSome()) { out << jsonp.get() << "("; } out << value; if (jsonp.isSome()) { out << ");"; headers["Content-Type"] = "text/javascript"; } else { headers["Content-Type"] = "application/json"; } headers["Content-Length"] = stringify(out.str().size()); body = out.str().data(); }
const std::string Logging::TOGGLE_HELP() { return HELP( TLDR( "Sets the logging verbosity level for a specified duration."), DESCRIPTION( "The libprocess library uses [glog][glog] for logging. The library", "only uses verbose logging which means nothing will be output unless", "the verbosity level is set (by default it's 0, libprocess uses levels" " 1, 2, and 3).", "", "**NOTE:** If your application uses glog this will also affect", "your verbose logging.", "", "Query parameters:", "", "> level=VALUE Verbosity level (e.g., 1, 2, 3)", "> duration=VALUE Duration to keep verbosity level", "> toggled (e.g., 10secs, 15mins, etc.)"), AUTHENTICATION(true), None(), REFERENCES( "[glog]: https://code.google.com/p/google-glog")); }
void FlagsBase::add( Option<T> Flags::*option, const Name& name, const Option<Name>& alias, const std::string& help, F validate) { // Don't bother adding anything if the pointer is NULL. if (option == NULL) { return; } Flags* flags = dynamic_cast<Flags*>(this); if (flags == NULL) { ABORT("Attempted to add flag '" + name.value + "' with incompatible type"); } Flag flag; flag.name = name; flag.alias = alias; flag.help = help; flag.boolean = typeid(T) == typeid(bool); // NOTE: See comment above in Flags::T* overload of FLagsBase::add // for why we need to pass FlagsBase* (or const FlagsBase&) as a // parameter. flag.load = [option](FlagsBase* base, const std::string& value) -> Try<Nothing> { Flags* flags = dynamic_cast<Flags*>(base); if (flags != NULL) { // NOTE: 'fetch' "retrieves" the value if necessary and then // invokes 'parse'. See 'fetch' for more details. Try<T> t = fetch<T>(value); if (t.isSome()) { flags->*option = Some(t.get()); } else { return Error("Failed to load value '" + value + "': " + t.error()); } } return Nothing(); }; flag.stringify = [option](const FlagsBase& base) -> Option<std::string> { const Flags* flags = dynamic_cast<const Flags*>(&base); if (flags != NULL) { if ((flags->*option).isSome()) { return stringify((flags->*option).get()); } } return None(); }; flag.validate = [option, validate](const FlagsBase& base) -> Option<Error> { const Flags* flags = dynamic_cast<const Flags*>(&base); if (flags != NULL) { return validate(flags->*option); } return None(); }; add(flag); }
void FlagsBase::add( T1 Flags::*t1, const Name& name, const Option<Name>& alias, const std::string& help, const T2& t2, F validate) { // Don't bother adding anything if the pointer is NULL. if (t1 == NULL) { return; } Flags* flags = dynamic_cast<Flags*>(this); if (flags == NULL) { ABORT("Attempted to add flag '" + name.value + "' with incompatible type"); } else { flags->*t1 = t2; // Set the default. } Flag flag; flag.name = name; flag.alias = alias; flag.help = help; flag.boolean = typeid(T1) == typeid(bool); // NOTE: We need to take FlagsBase* (or const FlagsBase&) as the // first argument to 'load', 'stringify', and 'validate' so that we // use the correct instance of FlagsBase. In other words, we can't // capture 'this' here because it's possible that the FlagsBase // object that we're working with when we invoke FlagsBase::add is // not the same instance as 'this' when these lambdas get invoked. flag.load = [t1](FlagsBase* base, const std::string& value) -> Try<Nothing> { Flags* flags = dynamic_cast<Flags*>(base); if (base != NULL) { // NOTE: 'fetch' "retrieves" the value if necessary and then // invokes 'parse'. See 'fetch' for more details. Try<T1> t = fetch<T1>(value); if (t.isSome()) { flags->*t1 = t.get(); } else { return Error("Failed to load value '" + value + "': " + t.error()); } } return Nothing(); }; flag.stringify = [t1](const FlagsBase& base) -> Option<std::string> { const Flags* flags = dynamic_cast<const Flags*>(&base); if (flags != NULL) { return stringify(flags->*t1); } return None(); }; flag.validate = [t1, validate](const FlagsBase& base) -> Option<Error> { const Flags* flags = dynamic_cast<const Flags*>(&base); if (flags != NULL) { return validate(flags->*t1); } return None(); }; // Update the help string to include the default value. flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1 ? " (default: " // On same line, add space. : "(default: "; // On newline. flag.help += stringify(t2); flag.help += ")"; add(flag); }
Try<TaskState> TaskState::recover( const string& rootDir, const SlaveID& slaveId, const FrameworkID& frameworkId, const ExecutorID& executorId, const UUID& uuid, const TaskID& taskId, bool strict) { TaskState state; state.id = taskId; string message; // Read the task info. string path = paths::getTaskInfoPath( rootDir, slaveId, frameworkId, executorId, uuid, taskId); if (!os::exists(path)) { // This could happen if the slave died after creating the task // directory but before it checkpointed the task info. LOG(WARNING) << "Failed to find task info file '" << path << "'"; return state; } const Result<Task>& task = ::protobuf::read<Task>(path); if (task.isError()) { message = "Failed to read task info from '" + path + "': " + task.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; state.errors++; return state; } } if (task.isNone()) { // This could happen if the slave died after opening the file for // writing but before it checkpointed anything. LOG(WARNING) << "Found empty task info file '" << path << "'"; return state; } state.info = task.get(); // Read the status updates. path = paths::getTaskUpdatesPath( rootDir, slaveId, frameworkId, executorId, uuid, taskId); if (!os::exists(path)) { // This could happen if the slave died before it checkpointed // any status updates for this task. LOG(WARNING) << "Failed to find status updates file '" << path << "'"; return state; } // Open the status updates file for reading and writing (for truncating). const Try<int>& fd = os::open(path, O_RDWR); if (fd.isError()) { message = "Failed to open status updates file '" + path + "': " + fd.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; state.errors++; return state; } } // Now, read the updates. Result<StatusUpdateRecord> record = None(); while (true) { // Ignore errors due to partial protobuf read. record = ::protobuf::read<StatusUpdateRecord>(fd.get(), true); if (!record.isSome()) { break; } if (record.get().type() == StatusUpdateRecord::UPDATE) { state.updates.push_back(record.get().update()); } else { state.acks.insert(UUID::fromBytes(record.get().uuid())); } } // Always truncate the file to contain only valid updates. // NOTE: This is safe even though we ignore partial protobuf // read errors above, because the 'fd' is properly set to the // end of the last valid update by 'protobuf::read()'. if (ftruncate(fd.get(), lseek(fd.get(), 0, SEEK_CUR)) != 0) { return ErrnoError( "Failed to truncate status updates file '" + path + "'"); } // After reading a non-corrupted updates file, 'record' should be 'none'. if (record.isError()) { message = "Failed to read status updates file '" + path + "': " + record.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; state.errors++; return state; } } // Close the updates file. const Try<Nothing>& close = os::close(fd.get()); if (close.isError()) { message = "Failed to close status updates file '" + path + "': " + close.error(); if (strict) { return Error(message); } else { LOG(WARNING) << message; state.errors++; return state; } } return state; }
// This test verifies that the image specified in the volume will be // properly provisioned and mounted into the container if container // root filesystem is not specified. TEST_P(VolumeImageIsolatorTest, ROOT_ImageInVolumeWithoutRootFilesystem) { string registry = path::join(sandbox.get(), "registry"); AWAIT_READY(DockerArchive::create(registry, "test_image")); slave::Flags flags = CreateSlaveFlags(); flags.isolation = "filesystem/linux,volume/image,docker/runtime"; flags.docker_registry = registry; flags.docker_store_dir = path::join(sandbox.get(), "store"); flags.image_providers = "docker"; Fetcher fetcher(flags); Try<MesosContainerizer*> create = MesosContainerizer::create(flags, true, &fetcher); ASSERT_SOME(create); Owned<Containerizer> containerizer(create.get()); ContainerID containerId; containerId.set_value(id::UUID::random().toString()); ContainerInfo container = createContainerInfo( None(), {createVolumeFromDockerImage("rootfs", "test_image", Volume::RW)}); CommandInfo command = createCommandInfo("test -d rootfs/bin"); ExecutorInfo executor = createExecutorInfo( "test_executor", nesting ? createCommandInfo("sleep 1000") : command); if (!nesting) { executor.mutable_container()->CopyFrom(container); } string directory = path::join(flags.work_dir, "sandbox"); ASSERT_SOME(os::mkdir(directory)); Future<Containerizer::LaunchResult> launch = containerizer->launch( containerId, createContainerConfig(None(), executor, directory), map<string, string>(), None()); AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch); Future<Option<ContainerTermination>> wait = containerizer->wait(containerId); if (nesting) { ContainerID nestedContainerId; nestedContainerId.mutable_parent()->CopyFrom(containerId); nestedContainerId.set_value(id::UUID::random().toString()); launch = containerizer->launch( nestedContainerId, createContainerConfig(command, container), map<string, string>(), None()); AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch); wait = containerizer->wait(nestedContainerId); } AWAIT_READY(wait); ASSERT_SOME(wait.get()); ASSERT_TRUE(wait->get().has_status()); EXPECT_WEXITSTATUS_EQ(0, wait->get().status()); if (nesting) { Future<Option<ContainerTermination>> termination = containerizer->destroy(containerId); AWAIT_READY(termination); ASSERT_SOME(termination.get()); ASSERT_TRUE(termination->get().has_status()); EXPECT_WTERMSIG_EQ(SIGKILL, termination.get()->status()); } }
Option<Error> validate( const mesos::agent::Call& call, const Option<string>& principal) { if (!call.IsInitialized()) { return Error("Not initialized: " + call.InitializationErrorString()); } if (!call.has_type()) { return Error("Expecting 'type' to be present"); } switch (call.type()) { case mesos::agent::Call::UNKNOWN: return None(); case mesos::agent::Call::GET_HEALTH: return None(); case mesos::agent::Call::GET_FLAGS: return None(); case mesos::agent::Call::GET_VERSION: return None(); case mesos::agent::Call::GET_METRICS: if (!call.has_get_metrics()) { return Error("Expecting 'get_metrics' to be present"); } return None(); case mesos::agent::Call::GET_LOGGING_LEVEL: return None(); case mesos::agent::Call::SET_LOGGING_LEVEL: if (!call.has_set_logging_level()) { return Error("Expecting 'set_logging_level' to be present"); } return None(); case mesos::agent::Call::LIST_FILES: if (!call.has_list_files()) { return Error("Expecting 'list_files' to be present"); } return None(); case mesos::agent::Call::READ_FILE: if (!call.has_read_file()) { return Error("Expecting 'read_file' to be present"); } return None(); case mesos::agent::Call::GET_STATE: return None(); case mesos::agent::Call::GET_CONTAINERS: return None(); case mesos::agent::Call::GET_FRAMEWORKS: return None(); case mesos::agent::Call::GET_EXECUTORS: return None(); case mesos::agent::Call::GET_TASKS: return None(); case mesos::agent::Call::GET_AGENT: return None(); case mesos::agent::Call::LAUNCH_NESTED_CONTAINER: { if (!call.has_launch_nested_container()) { return Error("Expecting 'launch_nested_container' to be present"); } Option<Error> error = validation::container::validateContainerId( call.launch_nested_container().container_id()); if (error.isSome()) { return Error("'launch_nested_container.container_id' is invalid" ": " + error->message); } // The parent `ContainerID` is required, so that we know // which container to place it underneath. if (!call.launch_nested_container().container_id().has_parent()) { return Error("Expecting 'launch_nested_container.container_id.parent'" " to be present"); } if (call.launch_nested_container().has_command()) { error = common::validation::validateCommandInfo( call.launch_nested_container().command()); if (error.isSome()) { return Error("'launch_nested_container.command' is invalid" ": " + error->message); } } return None(); } case mesos::agent::Call::WAIT_NESTED_CONTAINER: { if (!call.has_wait_nested_container()) { return Error("Expecting 'wait_nested_container' to be present"); } Option<Error> error = validation::container::validateContainerId( call.wait_nested_container().container_id()); if (error.isSome()) { return Error("'wait_nested_container.container_id' is invalid" ": " + error->message); } // Nested containers always have at least one parent. if (!call.wait_nested_container().container_id().has_parent()) { return Error("Expecting 'wait_nested_container.container_id.parent'" " to be present"); } return None(); } case mesos::agent::Call::KILL_NESTED_CONTAINER: { if (!call.has_kill_nested_container()) { return Error("Expecting 'kill_nested_container' to be present"); } Option<Error> error = validation::container::validateContainerId( call.kill_nested_container().container_id()); if (error.isSome()) { return Error("'kill_nested_container.container_id' is invalid" ": " + error->message); } // Nested containers always have at least one parent. if (!call.kill_nested_container().container_id().has_parent()) { return Error("Expecting 'kill_nested_container.container_id.parent'" " to be present"); } return None(); } case mesos::agent::Call::REMOVE_NESTED_CONTAINER: { if (!call.has_remove_nested_container()) { return Error("Expecting 'remove_nested_container' to be present"); } Option<Error> error = validation::container::validateContainerId( call.remove_nested_container().container_id()); if (error.isSome()) { return Error("'remove_nested_container.container_id' is invalid" ": " + error->message); } // Nested containers always have at least one parent. if (!call.remove_nested_container().container_id().has_parent()) { return Error("Expecting 'remove_nested_container.container_id.parent'" " to be present"); } return None(); } case mesos::agent::Call::LAUNCH_NESTED_CONTAINER_SESSION: { if (!call.has_launch_nested_container_session()) { return Error( "Expecting 'launch_nested_container_session' to be present"); } Option<Error> error = validation::container::validateContainerId( call.launch_nested_container_session().container_id()); if (error.isSome()) { return Error("'launch_nested_container_session.container_id' is invalid" ": " + error->message); } // The parent `ContainerID` is required, so that we know // which container to place it underneath. if (!call.launch_nested_container_session().container_id().has_parent()) { return Error( "Expecting 'launch_nested_container_session.container_id.parent'" " to be present"); } if (call.launch_nested_container_session().has_command()) { error = common::validation::validateCommandInfo( call.launch_nested_container_session().command()); if (error.isSome()) { return Error("'launch_nested_container_session.command' is invalid" ": " + error->message); } } return None(); } case mesos::agent::Call::ATTACH_CONTAINER_INPUT: { if (!call.has_attach_container_input()) { return Error("Expecting 'attach_container_input' to be present"); } if (!call.attach_container_input().has_type()) { return Error("Expecting 'attach_container_input.type' to be present"); } switch (call.attach_container_input().type()) { case mesos::agent::Call::AttachContainerInput::UNKNOWN: return Error("'attach_container_input.type' is unknown"); case mesos::agent::Call::AttachContainerInput::CONTAINER_ID: { Option<Error> error = validation::container::validateContainerId( call.attach_container_input().container_id()); if (error.isSome()) { return Error("'attach_container_input.container_id' is invalid" ": " + error->message); } } case mesos::agent::Call::AttachContainerInput::PROCESS_IO: return None(); } UNREACHABLE(); } case mesos::agent::Call::ATTACH_CONTAINER_OUTPUT: { if (!call.has_attach_container_output()) { return Error("Expecting 'attach_container_output' to be present"); } Option<Error> error = validation::container::validateContainerId( call.attach_container_output().container_id()); if (error.isSome()) { return Error("'attach_container_output.container_id' is invalid" ": " + error->message); } return None(); } } UNREACHABLE(); }
Option<Error> validate(const mesos::executor::Call& call) { if (!call.IsInitialized()) { return Error("Not initialized: " + call.InitializationErrorString()); } if (!call.has_type()) { return Error("Expecting 'type' to be present"); } // All calls should have executor id set. if (!call.has_executor_id()) { return Error("Expecting 'executor_id' to be present"); } // All calls should have framework id set. if (!call.has_framework_id()) { return Error("Expecting 'framework_id' to be present"); } switch (call.type()) { case mesos::executor::Call::SUBSCRIBE: { if (!call.has_subscribe()) { return Error("Expecting 'subscribe' to be present"); } return None(); } case mesos::executor::Call::UPDATE: { if (!call.has_update()) { return Error("Expecting 'update' to be present"); } const TaskStatus& status = call.update().status(); if (!status.has_uuid()) { return Error("Expecting 'uuid' to be present"); } Try<UUID> uuid = UUID::fromBytes(status.uuid()); if (uuid.isError()) { return uuid.error(); } if (status.has_executor_id() && status.executor_id().value() != call.executor_id().value()) { return Error("ExecutorID in Call: " + call.executor_id().value() + " does not match ExecutorID in TaskStatus: " + call.update().status().executor_id().value() ); } if (status.source() != TaskStatus::SOURCE_EXECUTOR) { return Error("Received Call from executor " + call.executor_id().value() + " of framework " + call.framework_id().value() + " with invalid source, expecting 'SOURCE_EXECUTOR'" ); } if (status.state() == TASK_STAGING) { return Error("Received TASK_STAGING from executor " + call.executor_id().value() + " of framework " + call.framework_id().value() + " which is not allowed" ); } // TODO(alexr): Validate `check_status` is present if // the corresponding `TaskInfo.check` has been defined. if (status.has_check_status()) { Option<Error> validate = checks::validation::checkStatusInfo(status.check_status()); if (validate.isSome()) { return validate.get(); } } return None(); } case mesos::executor::Call::MESSAGE: { if (!call.has_message()) { return Error("Expecting 'message' to be present"); } return None(); } case mesos::executor::Call::UNKNOWN: { return None(); } } UNREACHABLE(); }
// Generate a `Process` object for the process associated with `pid`. If // process is not found, we return `None`; error is reserved for the case where // something went wrong. inline Result<Process> process(pid_t pid) { // Find process with pid. Result<PROCESSENTRY32> entry = process_entry(pid); if (entry.isError()) { return WindowsError(entry.error()); } else if (entry.isNone()) { return None(); } HANDLE process_handle = ::OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, false, pid); if (process_handle == INVALID_HANDLE_VALUE) { return WindowsError("os::process: Call to `OpenProcess` failed"); } SharedHandle safe_process_handle(process_handle, ::CloseHandle); // Get Windows Working set size (Resident set size in linux). PROCESS_MEMORY_COUNTERS proc_mem_counters; BOOL get_process_memory_info = ::GetProcessMemoryInfo( safe_process_handle.get(), &proc_mem_counters, sizeof(proc_mem_counters)); if (!get_process_memory_info) { return WindowsError("os::process: Call to `GetProcessMemoryInfo` failed"); } // Get session Id. pid_t session_id; BOOL process_id_to_session_id = ::ProcessIdToSessionId(pid, &session_id); if (!process_id_to_session_id) { return WindowsError("os::process: Call to `ProcessIdToSessionId` failed"); } // Get Process CPU time. FILETIME create_filetime, exit_filetime, kernel_filetime, user_filetime; BOOL get_process_times = ::GetProcessTimes( safe_process_handle.get(), &create_filetime, &exit_filetime, &kernel_filetime, &user_filetime); if (!get_process_times) { return WindowsError("os::process: Call to `GetProcessTimes` failed"); } // Get utime and stime. ULARGE_INTEGER lKernelTime, lUserTime; // In 100 nanoseconds. lKernelTime.HighPart = kernel_filetime.dwHighDateTime; lKernelTime.LowPart = kernel_filetime.dwLowDateTime; lUserTime.HighPart = user_filetime.dwHighDateTime; lUserTime.LowPart = user_filetime.dwLowDateTime; Try<Duration> utime = Nanoseconds(lKernelTime.QuadPart * 100); Try<Duration> stime = Nanoseconds(lUserTime.QuadPart * 100); return Process( pid, entry.get().th32ParentProcessID, // Parent process id. 0, // Group id. session_id, Bytes(proc_mem_counters.WorkingSetSize), utime.isSome() ? utime.get() : Option<Duration>::none(), stime.isSome() ? stime.get() : Option<Duration>::none(), entry.get().szExeFile, // Executable filename. false); // Is not zombie process. }
// Suspends execution of the calling process until a child specified by `pid` // has changed state. Unlike the POSIX standard function `::waitpid`, this // function does not use -1 and 0 to signify errors and nonblocking return. // Instead, we return `Result<pid_t>`: // * In case of error, we return `Error` rather than -1. For example, we // would return an `Error` in case of `EINVAL`. // * In case of nonblocking return, we return `None` rather than 0. For // example, if we pass `WNOHANG` in the `options`, we would expect 0 to be // returned in the case that children specified by `pid` exist, but have // not changed state yet. In this case we return `None` instead. // // NOTE: There are important differences between the POSIX and Windows // implementations of this function: // * On POSIX, `pid_t` is a signed number, but on Windows, PIDs are `DWORD`, // which is `unsigned long`. Thus, if we use `DWORD` to represent the `pid` // argument, passing -1 as the `pid` would (on most modern servers) // silently convert to a really large `pid`. This is undesirable. // * Since it is important to be able to detect -1 has been passed to // `os::waitpid`, as a matter of practicality, we choose to: // (1) Use `long` to represent the `pid` argument. // (2) Disable using any value <= 0 for `pid` on Windows. // * This decision is pragmatic. The reasoning is: // (1) The Windows code paths call `os::waitpid` in only a handful of // places, and in none of these conditions do we need `-1` as a value. // (2) Since PIDs virtually never take on values outside the range of // vanilla signed `long` it is likely that an accidental conversion // will never happen. // (3) Even though it is not formalized in the C specification, the // implementation of `long` on the vast majority of production servers // is 2's complement, so we expect that when we accidentally do // implicitly convert from `unsigned long` to `long`, we will "wrap // around" to negative values. And since we've disabled the negative // `pid` in the Windows implementation, we should error out. // * Finally, on Windows, we currently do not check that the process we are // attempting to await is a child process. inline Result<pid_t> waitpid(long pid, int* status, int options) { const bool wait_for_child = (options & WNOHANG) == 0; // NOTE: Windows does not implement pids <= 0. if (pid <= 0) { errno = ENOSYS; return ErrnoError( "os::waitpid: Value of pid is '" + stringify(pid) + "'; the Windows implementation currently does not allow values <= 0"); } else if (options != 0 && options != WNOHANG) { // NOTE: We only support `options == 0` or `options == WNOHANG`. On Windows // no flags other than `WNOHANG` are supported. errno = ENOSYS; return ErrnoError( "os::waitpid: Only flag `WNOHANG` is implemented on Windows"); } // TODO(hausdorff): Check that `pid` is one of the child processes. If not, // set `errno` to `ECHILD` and return -1. // Open the child process as a safe `SharedHandle`. const HANDLE process = ::OpenProcess( PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, static_cast<DWORD>(pid)); if (process == NULL) { return WindowsError("os::waitpid: Failed to open process for pid '" + stringify(pid) + "'"); } SharedHandle scoped_process(process, ::CloseHandle); // If `WNOHANG` flag is set, don't wait. Otherwise, wait for child to // terminate. const DWORD wait_time = wait_for_child ? INFINITE : 0; const DWORD wait_results = ::WaitForSingleObject( scoped_process.get(), wait_time); // Verify our wait exited correctly. const bool state_signaled = wait_results == WAIT_OBJECT_0; if (options == 0 && !state_signaled) { // If `WNOHANG` is not set, then we should have stopped waiting only for a // state change in `scoped_process`. errno = ECHILD; return WindowsError( "os::waitpid: Failed to wait for pid '" + stringify(pid) + "'. `::WaitForSingleObject` should have waited for child process to " + "exit, but returned code '" + stringify(wait_results) + "' instead"); } else if (wait_for_child && !state_signaled && wait_results != WAIT_TIMEOUT) { // If `WNOHANG` is set, then a successful wait should report either a // timeout (since we set the time to wait to `0`), or a successful state // change of `scoped_process`. Anything else is an error. errno = ECHILD; return WindowsError( "os::waitpid: Failed to wait for pid '" + stringify(pid) + "'. `ENOHANG` flag was passed in, so `::WaitForSingleObject` should " + "have either returned `WAIT_OBJECT_0` or `WAIT_TIMEOUT` (the " + "timeout was set to 0, because we are not waiting for the child), " + "but instead returned code '" + stringify(wait_results) + "'"); } if (!wait_for_child && wait_results == WAIT_TIMEOUT) { // Success. `ENOHANG` was set and we got a timeout, so return `None` (POSIX // `::waitpid` would return 0 here). return None(); } // Attempt to retrieve exit code from child process. Store that exit code in // the `status` variable if it's `NULL`. DWORD child_exit_code = 0; if (!::GetExitCodeProcess(scoped_process.get(), &child_exit_code)) { errno = ECHILD; return WindowsError( "os::waitpid: Successfully waited on child process with pid '" + std::to_string(pid) + "', but could not retrieve exit code"); } if (status != NULL) { *status = child_exit_code; } // Success. Return pid of the child process for which the status is reported. return pid; }
inline Try<std::set<pid_t>> pids() { return pids(None(), None()); }
Info (const hashmap<std::string, ContainerNetwork>& _containerNetworks, const Option<std::string> _rootfs = None()) : containerNetworks (_containerNetworks), rootfs(_rootfs) {}
// 'name' is the unique name for the instance of Gauge being constructed. // It will be the key exposed in the JSON endpoint. // // 'f' is the function that is called when the Metric value is requested. // The user of `Gauge` must ensure that `f` is safe to execute up until // the removal of the `Gauge` (via `process::metrics::remove(...)`) is // complete. explicit PushGauge(const std::string& name) : Metric(name, None()), data(new Data()) {}
LevelDBStorage::LevelDBStorage() : db(nullptr), first(None()) { // Nothing to see here. }
TestHttpServer() : ProcessBase("TestHttpServer") { route("/test", None(), &TestHttpServer::test); }