TEST_F(FetcherEnvironmentTest, MultipleURIs) { CommandInfo commandInfo; CommandInfo::URI uri; uri.set_value("hdfs:///uri1"); uri.set_executable(false); commandInfo.add_uris()->MergeFrom(uri); uri.set_value("hdfs:///uri2"); uri.set_executable(true); commandInfo.add_uris()->MergeFrom(uri); string directory = "/tmp/directory"; Option<string> user("user"); slave::Flags flags; flags.frameworks_home = "/tmp/frameworks"; flags.hadoop_home = "/tmp/hadoop"; map<string, string> environment = fetcher::environment(commandInfo, directory, user, flags); EXPECT_EQ(5u, environment.size()); EXPECT_EQ(stringify(JSON::Protobuf(commandInfo)), environment["MESOS_COMMAND_INFO"]); EXPECT_EQ(directory, environment["MESOS_WORK_DIRECTORY"]); EXPECT_EQ(user.get(), environment["MESOS_USER"]); EXPECT_EQ(flags.frameworks_home, environment["MESOS_FRAMEWORKS_HOME"]); EXPECT_EQ(flags.hadoop_home, environment["HADOOP_HOME"]); }
TEST_F(MesosContainerizerProcessTest, MultipleURIs) { CommandInfo commandInfo; CommandInfo::URI uri; uri.set_value("hdfs:///uri1"); uri.set_executable(false); commandInfo.add_uris()->MergeFrom(uri); uri.set_value("hdfs:///uri2"); uri.set_executable(true); commandInfo.add_uris()->MergeFrom(uri); string directory = "/tmp/directory"; Option<string> user("user"); Flags flags; flags.frameworks_home = "/tmp/frameworks"; flags.hadoop_home = "/tmp/hadoop"; map<string, string> environment = fetcherEnvironment(commandInfo, directory, user, flags); EXPECT_EQ(5u, environment.size()); EXPECT_EQ( "hdfs:///uri1+0X hdfs:///uri2+1X", environment["MESOS_EXECUTOR_URIS"]); EXPECT_EQ(directory, environment["MESOS_WORK_DIRECTORY"]); EXPECT_EQ(user.get(), environment["MESOS_USER"]); EXPECT_EQ(flags.frameworks_home, environment["MESOS_FRAMEWORKS_HOME"]); EXPECT_EQ(flags.hadoop_home, environment["HADOOP_HOME"]); }
TEST_F(FetcherTest, FileLocalhostURI) { string fromDir = path::join(os::getcwd(), "from"); ASSERT_SOME(os::mkdir(fromDir)); string testFile = path::join(fromDir, "test"); EXPECT_FALSE(os::write(testFile, "data").isError()); string localFile = path::join(os::getcwd(), "test"); EXPECT_FALSE(os::exists(localFile)); slave::Flags flags; flags.frameworks_home = "/tmp/frameworks"; CommandInfo commandInfo; CommandInfo::URI* uri = commandInfo.add_uris(); uri->set_value(path::join("file://localhost", testFile)); map<string, string> env = fetcher::environment(commandInfo, os::getcwd(), None(), flags); Try<Subprocess> fetcherProcess = process::subprocess( path::join(mesos::internal::tests::flags.build_dir, "src/mesos-fetcher"), env); ASSERT_SOME(fetcherProcess); Future<Option<int>> status = fetcherProcess.get().status(); AWAIT_READY(status); ASSERT_SOME(status.get()); EXPECT_EQ(0, status.get().get()); EXPECT_TRUE(os::exists(localFile)); }
TEST_F(FetcherTest, ExtractNotExecutable) { // First construct a temporary file that can be fetched and archive // with tar gzip. Try<string> path = os::mktemp(); ASSERT_SOME(path); ASSERT_SOME(os::write(path.get(), "hello world")); // TODO(benh): Update os::tar so that we can capture or ignore // stdout/stderr output. ASSERT_SOME(os::tar(path.get(), path.get() + ".tar.gz")); CommandInfo commandInfo; CommandInfo::URI* uri = commandInfo.add_uris(); uri->set_value(path.get() + ".tar.gz"); uri->set_executable(false); uri->set_extract(true); Option<int> stdout = None(); Option<int> stderr = None(); // Redirect mesos-fetcher output if running the tests verbosely. if (tests::flags.verbose) { stdout = STDOUT_FILENO; stderr = STDERR_FILENO; } slave::Flags flags; flags.launcher_dir = path::join(tests::flags.build_dir, "src"); Future<Option<int>> run = fetcher::run(commandInfo, os::getcwd(), None(), flags, stdout, stderr); AWAIT_READY(run); EXPECT_SOME_EQ(0, run.get()); ASSERT_TRUE(os::exists(path::join(".", path.get()))); ASSERT_SOME_EQ("hello world", os::read(path::join(".", path.get()))); Try<os::Permissions> permissions = os::permissions(path::join(".", path.get())); ASSERT_SOME(permissions); EXPECT_FALSE(permissions.get().owner.x); EXPECT_FALSE(permissions.get().group.x); EXPECT_FALSE(permissions.get().others.x); ASSERT_SOME(os::rm(path.get())); }
TEST_F(FetcherTest, NoExtractExecutable) { // First construct a temporary file that can be fetched. Try<string> path = os::mktemp(); ASSERT_SOME(path); CommandInfo commandInfo; CommandInfo::URI* uri = commandInfo.add_uris(); uri->set_value(path.get()); uri->set_executable(true); uri->set_extract(false); Option<int> stdout = None(); Option<int> stderr = None(); // Redirect mesos-fetcher output if running the tests verbosely. if (tests::flags.verbose) { stdout = STDOUT_FILENO; stderr = STDERR_FILENO; } slave::Flags flags; flags.launcher_dir = path::join(tests::flags.build_dir, "src"); Future<Option<int>> run = fetcher::run(commandInfo, os::getcwd(), None(), flags, stdout, stderr); AWAIT_READY(run); EXPECT_SOME_EQ(0, run.get()); Try<string> basename = os::basename(path.get()); ASSERT_SOME(basename); Try<os::Permissions> permissions = os::permissions(basename.get()); ASSERT_SOME(permissions); EXPECT_TRUE(permissions.get().owner.x); EXPECT_TRUE(permissions.get().group.x); EXPECT_TRUE(permissions.get().others.x); ASSERT_SOME(os::rm(path.get())); }
TEST_F(MesosContainerizerProcessTest, NoUser) { CommandInfo commandInfo; CommandInfo::URI uri; uri.set_value("hdfs:///uri"); uri.set_executable(false); commandInfo.add_uris()->MergeFrom(uri); string directory = "/tmp/directory"; Flags flags; flags.frameworks_home = "/tmp/frameworks"; flags.hadoop_home = "/tmp/hadoop"; map<string, string> environment = fetcherEnvironment(commandInfo, directory, None(), flags); EXPECT_EQ(4u, environment.size()); EXPECT_EQ("hdfs:///uri+0X", environment["MESOS_EXECUTOR_URIS"]); EXPECT_EQ(directory, environment["MESOS_WORK_DIRECTORY"]); EXPECT_EQ(flags.frameworks_home, environment["MESOS_FRAMEWORKS_HOME"]); EXPECT_EQ(flags.hadoop_home, environment["HADOOP_HOME"]); }
TEST_F(FetcherEnvironmentTest, NoHadoop) { CommandInfo commandInfo; CommandInfo::URI* uri = commandInfo.add_uris(); uri->set_value("hdfs:///uri"); uri->set_executable(false); string directory = "/tmp/directory"; Option<string> user = "******"; slave::Flags flags; flags.frameworks_home = "/tmp/frameworks"; map<string, string> environment = fetcher::environment(commandInfo, directory, user, flags); EXPECT_EQ(4u, environment.size()); EXPECT_EQ(stringify(JSON::Protobuf(commandInfo)), environment["MESOS_COMMAND_INFO"]); EXPECT_EQ(directory, environment["MESOS_WORK_DIRECTORY"]); EXPECT_EQ(user.get(), environment["MESOS_USER"]); EXPECT_EQ(flags.frameworks_home, environment["MESOS_FRAMEWORKS_HOME"]); }
TEST_F(FetcherTest, OSNetUriTest) { HttpProcess process; spawn(process); string url = "http://" + net::getHostname(process.self().node.ip).get() + ":" + stringify(process.self().node.port) + "/help"; string localFile = path::join(os::getcwd(), "help"); EXPECT_FALSE(os::exists(localFile)); slave::Flags flags; flags.frameworks_home = "/tmp/frameworks"; CommandInfo commandInfo; CommandInfo::URI* uri = commandInfo.add_uris(); uri->set_value(url); map<string, string> env = fetcher::environment(commandInfo, os::getcwd(), None(), flags); Try<Subprocess> fetcherProcess = process::subprocess( path::join(mesos::internal::tests::flags.build_dir, "src/mesos-fetcher"), env); ASSERT_SOME(fetcherProcess); Future<Option<int>> status = fetcherProcess.get().status(); AWAIT_READY(status); ASSERT_SOME(status.get()); EXPECT_EQ(0, status.get().get()); EXPECT_TRUE(os::exists(localFile)); }
int main(int argc, char* argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; CommandInfo commandInfo; // Construct URIs from the encoded environment string. const std::string& uris = os::getenv("MESOS_EXECUTOR_URIS"); foreach (const std::string& token, strings::tokenize(uris, " ")) { // Delimiter between URI, execute permission and extract options // Expected format: {URI}+[01][XN] // {URI} - The actual URI for the asset to fetch // [01] - 1 if the execute permission should be set else 0 // [XN] - X if we should extract the URI (if it's compressed) else N size_t pos = token.rfind("+"); CHECK(pos != std::string::npos) << "Invalid executor uri token in env " << token; CommandInfo::URI uri; uri.set_value(token.substr(0, pos)); uri.set_executable(token.substr(pos + 1, 1) == "1"); uri.set_extract(token.substr(pos + 2, 1) == "X"); commandInfo.add_uris()->MergeFrom(uri); } CHECK(os::hasenv("MESOS_WORK_DIRECTORY")) << "Missing MESOS_WORK_DIRECTORY environment variable"; std::string directory = os::getenv("MESOS_WORK_DIRECTORY"); // We cannot use Some in the ternary expression because the compiler needs to // be able to infer the type, thus the explicit Option<string>. // TODO(idownes): Add an os::hasenv that returns an Option<string>. Option<std::string> user = os::hasenv("MESOS_USER") ? Option<std::string>(os::getenv("MESOS_USER")) // Explicit so it compiles. : None(); // Fetch each URI to a local file, chmod, then chown if a user is provided. foreach (const CommandInfo::URI& uri, commandInfo.uris()) { // Fetch the URI to a local file. Try<string> fetched = fetch(uri.value(), directory); if (fetched.isError()) { EXIT(1) << "Failed to fetch: " << uri.value(); } // Chmod the fetched URI if it's executable, else assume it's an archive // that should be extracted. if (uri.executable()) { Try<Nothing> chmod = os::chmod( fetched.get(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); if (chmod.isError()) { EXIT(1) << "Failed to chmod " << fetched.get() << ": " << chmod.error(); } } else if (uri.extract()) { //TODO(idownes): Consider removing the archive once extracted. // Try to extract the file if it's recognized as an archive. Try<bool> extracted = extract(fetched.get(), directory); if (extracted.isError()) { EXIT(1) << "Failed to extract " << fetched.get() << ":" << extracted.error(); } } else { LOG(INFO) << "Skipped extracting path '" << fetched.get() << "'"; } // Recursively chown the directory if a user is provided. if (user.isSome()) { Try<Nothing> chowned = os::chown(user.get(), directory); if (chowned.isError()) { EXIT(1) << "Failed to chown " << directory << ": " << chowned.error(); } } } return 0; }