You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@mesos.apache.org by Vinod Kone <vi...@apache.org> on 2015/01/15 21:46:49 UTC

Re: [1/4] mesos git commit: Added example hook module.

Hey Niklas. One of the hook tests is continously failing on our internal
CI. I've filed https://issues.apache.org/jira/browse/MESOS-2226. Can you
take a look?

Also, https://issues.apache.org/jira/browse/MESOS-2227

On Wed, Jan 14, 2015 at 3:41 PM, <nn...@apache.org> wrote:

> Repository: mesos
> Updated Branches:
>   refs/heads/master 60007b9e7 -> 4e110d5d0
>
>
> Added example hook module.
>
> This module provides hooks for master label decorator, slave executor
> environment decorator and slave remove executor.  A couple of test
> cases are also provided to verify the hooks from this module.
>
> Review: https://reviews.apache.org/r/29496
>
>
> Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
> Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/4e110d5d
> Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/4e110d5d
> Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/4e110d5d
>
> Branch: refs/heads/master
> Commit: 4e110d5d0f0c6cd65b8b6e5edff30bb4c589a278
> Parents: 0773763
> Author: Kapil Arya <ka...@mesosphere.io>
> Authored: Wed Jan 14 13:49:06 2015 -0800
> Committer: Niklas Q. Nielsen <ni...@mesosphere.io>
> Committed: Wed Jan 14 15:11:37 2015 -0800
>
> ----------------------------------------------------------------------
>  src/Makefile.am                   |   7 +
>  src/examples/test_hook_module.cpp | 138 +++++++++++++++++
>  src/tests/hook_tests.cpp          | 272 +++++++++++++++++++++++++++++++++
>  src/tests/module.cpp              |  21 +++
>  src/tests/module.hpp              |   3 +-
>  5 files changed, 440 insertions(+), 1 deletion(-)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/Makefile.am
> ----------------------------------------------------------------------
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 100db39..256384e 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -1237,6 +1237,12 @@ libtestauthentication_la_SOURCES =
> examples/test_authentication_modules.cpp
>  libtestauthentication_la_CPPFLAGS = $(MESOS_CPPFLAGS)
>  libtestauthentication_la_LDFLAGS = -release $(PACKAGE_VERSION) -shared
>
> +# Library containing test Hook module.
> +lib_LTLIBRARIES += libtesthook.la
> +libtesthook_la_SOURCES = examples/test_hook_module.cpp
> +libtesthook_la_CPPFLAGS = $(MESOS_CPPFLAGS)
> +libtesthook_la_LDFLAGS = -release $(PACKAGE_VERSION) -shared
> +
>  mesos_tests_SOURCES =                          \
>    tests/attributes_tests.cpp                   \
>    tests/authentication_tests.cpp               \
> @@ -1260,6 +1266,7 @@ mesos_tests_SOURCES =                             \
>    tests/flags.cpp                              \
>    tests/gc_tests.cpp                           \
>    tests/hierarchical_allocator_tests.cpp       \
> +  tests/hook_tests.cpp                         \
>    tests/isolator_tests.cpp                     \
>    tests/log_tests.cpp                          \
>    tests/logging_tests.cpp                      \
>
>
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/examples/test_hook_module.cpp
> ----------------------------------------------------------------------
> diff --git a/src/examples/test_hook_module.cpp
> b/src/examples/test_hook_module.cpp
> new file mode 100644
> index 0000000..04fd43e
> --- /dev/null
> +++ b/src/examples/test_hook_module.cpp
> @@ -0,0 +1,138 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License.  You may obtain a copy of the License at
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <string>
> +
> +#include <mesos/mesos.hpp>
> +#include <mesos/module.hpp>
> +
> +#include <stout/foreach.hpp>
> +#include <stout/os.hpp>
> +#include <stout/try.hpp>
> +
> +#include "hook/hook.hpp"
> +#include "master/master.hpp"
> +#include "module/hook.hpp"
> +#include "slave/slave.hpp"
> +
> +using std::string;
> +
> +using namespace mesos;
> +using namespace mesos::internal;
> +
> +// Must be kept in sync with variables of the same name in
> +// tests/hook_tests.cpp.
> +const char* testLabelKey = "MESOS_Test_Label";
> +const char* testLabelValue = "ApacheMesos";
> +const char* testEnvironmentVariableName =
> "MESOS_TEST_ENVIRONMENT_VARIABLE";
> +
> +class TestHook : public Hook
> +{
> +public:
> +  virtual Result<Labels> masterLaunchTaskLabelDecorator(
> +      const TaskInfo& taskInfo,
> +      const FrameworkInfo& frameworkInfo,
> +      const SlaveInfo& slaveInfo)
> +  {
> +    LOG(INFO) << "Executing 'masterLaunchTaskLabelDecorator' hook";
> +
> +    Labels labels;
> +    Label *label = labels.add_labels();
> +    label->set_key(testLabelKey);
> +    label->set_value(testLabelValue);
> +
> +    return labels;
> +  }
> +
> +
> +  // In this hook, we create a temporary file and add its path to an
> +  // environment variable.  Later on, this environment variable is
> +  // looked up by the removeExecutorHook to locate and delete this
> +  // file.
> +  virtual Result<Environment> slaveLaunchExecutorEnvironmentDecorator(
> +      const ExecutorInfo& executorInfo,
> +      const TaskInfo& taskInfo)
> +  {
> +    LOG(INFO) << "Executing 'slaveLaunchExecutorEnvironmentDecorator'
> hook";
> +
> +    // Find the label value for the label that was created in the
> +    // label decorator hook above.
> +    Option<string> labelValue;
> +    foreach (const Label& label, taskInfo.labels().labels()) {
> +      if (label.key() == testLabelKey) {
> +        labelValue = label.value();
> +        CHECK_EQ(labelValue.get(), testLabelValue);
> +      }
> +    }
> +    CHECK_SOME(labelValue);
> +
> +    // Create a temporary file.
> +    Try<string> file = os::mktemp();
> +    CHECK_SOME(file);
> +    CHECK_SOME(os::write(file.get(), labelValue.get()));
> +
> +    // Inject file path into command environment.
> +    Environment environment;
> +    Environment::Variable* variable = environment.add_variables();
> +    variable->set_name(testEnvironmentVariableName);
> +    variable->set_value(file.get());
> +
> +    return environment;
> +  }
> +
> +
> +  // This hook locates the file created by environment decorator hook
> +  // and deletes it.
> +  virtual Try<Nothing> slaveRemoveExecutorHook(
> +      const FrameworkInfo& frameworkInfo,
> +      const ExecutorInfo& executorInfo)
> +  {
> +    LOG(INFO) << "Executing 'slaveRemoveExecutorHook'";
> +
> +    foreach (const Environment::Variable& variable,
> +        executorInfo.command().environment().variables()) {
> +      if (variable.name() == testEnvironmentVariableName) {
> +        string path = variable.value();
> +        // The removeExecutor hook may be called multiple times; thus
> +        // we ignore the subsequent calls.
> +        if (os::isfile(path)) {
> +          CHECK_SOME(os::rm(path));
> +        }
> +        break;
> +      }
> +    }
> +    return Nothing();
> +  }
> +};
> +
> +
> +static Hook* createHook(const Parameters& parameters)
> +{
> +  return new TestHook();
> +}
> +
> +
> +// Declares a Hook module named 'TestHook'.
> +mesos::modules::Module<Hook> org_apache_mesos_TestHook(
> +    MESOS_MODULE_API_VERSION,
> +    MESOS_VERSION,
> +    "Apache Mesos",
> +    "modules@mesos.apache.org",
> +    "Test Hook module.",
> +    NULL,
> +    createHook);
>
>
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/tests/hook_tests.cpp
> ----------------------------------------------------------------------
> diff --git a/src/tests/hook_tests.cpp b/src/tests/hook_tests.cpp
> new file mode 100644
> index 0000000..4e9a3a7
> --- /dev/null
> +++ b/src/tests/hook_tests.cpp
> @@ -0,0 +1,272 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License.  You may obtain a copy of the License at
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <mesos/module.hpp>
> +
> +#include <process/future.hpp>
> +#include <process/gmock.hpp>
> +#include <process/pid.hpp>
> +
> +#include <stout/option.hpp>
> +#include <stout/os.hpp>
> +#include <stout/try.hpp>
> +
> +#include "hook/manager.hpp"
> +#include "module/manager.hpp"
> +#include "master/flags.hpp"
> +#include "master/master.hpp"
> +#include "slave/flags.hpp"
> +#include "slave/slave.hpp"
> +
> +#include "tests/containerizer.hpp"
> +#include "tests/flags.hpp"
> +#include "tests/mesos.hpp"
> +
> +using std::string;
> +
> +using namespace mesos;
> +using namespace mesos::internal;
> +using namespace mesos::internal::tests;
> +using namespace mesos::modules;
> +
> +using mesos::internal::master::Master;
> +using mesos::internal::slave::Slave;
> +
> +using process::Future;
> +using process::PID;
> +
> +using std::string;
> +using std::vector;
> +
> +using testing::_;
> +using testing::Return;
> +
> +const char* HOOK_MODULE_LIBRARY_NAME = "testhook";
> +const char* HOOK_MODULE_NAME = "org_apache_mesos_TestHook";
> +
> +// Must be kept in sync with variables of the same name in
> +// examples/test_hook_module.cpp.
> +const char* testLabelKey = "MESOS_Test_Label";
> +const char* testLabelValue = "ApacheMesos";
> +const char* testEnvironmentVariableName =
> "MESOS_TEST_ENVIRONMENT_VARIABLE";
> +
> +class HookTest : public MesosTest
> +{
> +protected:
> +  // TODO(karya): Replace constructor/destructor with SetUp/TearDown.
> +  // Currently, using SetUp/TearDown causes VerifySlave* test to
> +  // fail with a duplicate slave id message.  However, everything
> +  // seems normal when using this construction/destructor combo.
> +  HookTest()
> +  {
> +    // Install hooks.
> +    EXPECT_SOME(HookManager::initialize(HOOK_MODULE_NAME));
> +  }
> +
> +  ~HookTest()
> +  {
> +    // Unload the hooks so a subsequent install may succeed.
> +    EXPECT_SOME(HookManager::unload(HOOK_MODULE_NAME));
> +  }
> +};
> +
> +
> +// Test varioud hook install/uninstall mechanisms.
> +TEST_F(HookTest, HookLoading)
> +{
> +  // Installing unknown hooks should fail.
> +  EXPECT_ERROR(HookManager::initialize("Unknown Hook"));
> +
> +  // Uninstalling an unknown hook should fail.
> +  EXPECT_ERROR(HookManager::unload("Unknown Hook"));
> +
> +  // Installing an already installed hook should fail.
> +  EXPECT_ERROR(HookManager::initialize(HOOK_MODULE_NAME));
> +
> +  // Uninstalling a hook should succeed.
> +  EXPECT_SOME(HookManager::unload(HOOK_MODULE_NAME));
> +
> +  // Uninstalling an already uninstalled hook should fail.
> +  EXPECT_ERROR(HookManager::unload(HOOK_MODULE_NAME));
> +  // This is needed to allow the tear-down to succeed.
> +  EXPECT_SOME(HookManager::initialize(HOOK_MODULE_NAME));
> +}
> +
> +
> +// Test that the label decorator hook hangs a new label off the
> +// taskinfo message during master launch task.
> +TEST_F(HookTest, VerifyMasterLaunchTaskHook)
> +{
> +  Try<PID<Master> > master = StartMaster(CreateMasterFlags());
> +  ASSERT_SOME(master);
> +
> +  TestContainerizer containerizer;
> +
> +  StandaloneMasterDetector detector(master.get());
> +
> +  // Start a mock slave since we aren't testing the slave hooks yet.
> +  MockSlave slave(CreateSlaveFlags(), &detector, &containerizer);
> +  process::spawn(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());
> +
> +  CommandInfo command;
> +  command.set_value("sleep 10");
> +
> +  // 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());
> +  task.mutable_command()->MergeFrom(command);
> +
> +  vector<TaskInfo> tasks;
> +  tasks.push_back(task);
> +
> +  Future<TaskInfo> taskInfo;
> +  EXPECT_CALL(slave, runTask(_, _, _, _, _))
> +    .Times(1)
> +    .WillOnce(FutureArg<4>(&taskInfo));
> +
> +  driver.launchTasks(offers.get()[0].id(), tasks);
> +
> +  AWAIT_READY(taskInfo);
> +
> +  // At launchTasks, the label decorator hook inside should have been
> +  // executed and we should see the labels now.
> +  Option<string> labelValue;
> +  foreach (const Label& label, taskInfo.get().labels().labels()) {
> +    if (label.key() == testLabelKey) {
> +      labelValue = label.value();
> +    }
> +  }
> +  EXPECT_SOME_EQ(testLabelValue, labelValue);
> +
> +  driver.stop();
> +  driver.join();
> +
> +  process::terminate(slave);
> +  process::wait(slave);
> +
> +  Shutdown(); // Must shutdown before 'containerizer' gets deallocated.
> +}
> +
> +
> +// Test executor environment decorator hook and remove executor hook
> +// for slave.  We expect the environment-decorator hook to create a
> +// temporary file and the remove-executor hook to delete that file.
> +TEST_F(HookTest, VerifySlaveLaunchExecutorHook)
> +{
> +  master::Flags masterFlags = CreateMasterFlags();
> +
> +  Try<PID<Master> > master = StartMaster(masterFlags);
> +  ASSERT_SOME(master);
> +
> +  slave::Flags slaveFlags = CreateSlaveFlags();
> +
> +  MockExecutor exec(DEFAULT_EXECUTOR_ID);
> +
> +  TestContainerizer containerizer(&exec);
> +
> +  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());
> +  task.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
> +
> +  vector<TaskInfo> tasks;
> +  tasks.push_back(task);
> +
> +  EXPECT_CALL(exec, launchTask(_, _));
> +
> +  Future<ExecutorInfo> executorInfo;
> +  EXPECT_CALL(exec, registered(_, _, _, _))
> +    .Times(1)
> +    .WillOnce(FutureArg<1>(&executorInfo));
> +
> +  driver.launchTasks(offers.get()[0].id(), tasks);
> +
> +  AWAIT_READY(executorInfo);
> +
> +  // At launchTasks, the label decorator hook inside should have been
> +  // executed and we should see the labels now.
> +  // Further, the environment decorator hook should also have been
> +  // executed.  In that hook, we create a temp file and set the path
> +  // as the value of the environment variable.
> +  // Here we verify that the environment variable is present and the
> +  // file is present on the disk.
> +  Option<string> path;
> +  foreach (const Environment::Variable& variable,
> +           executorInfo.get().command().environment().variables()) {
> +    if (variable.name() == testEnvironmentVariableName) {
> +      path = variable.value();
> +      break;
> +    }
> +  }
> +
> +  EXPECT_SOME(path);
> +  // The file must have been create by the environment decorator hook.
> +  EXPECT_TRUE(os::isfile(path.get()));
> +
> +  driver.stop();
> +  driver.join();
> +
> +  Shutdown(); // Must shutdown before 'containerizer' gets deallocated.
> +
> +  // The removeExecutor hook in the test module deletes the temp file.
> +  // Verify that the file is not present.
> +  EXPECT_FALSE(os::isfile(path.get()));
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/tests/module.cpp
> ----------------------------------------------------------------------
> diff --git a/src/tests/module.cpp b/src/tests/module.cpp
> index 6cec1cb..e6dbf94 100644
> --- a/src/tests/module.cpp
> +++ b/src/tests/module.cpp
> @@ -93,6 +93,24 @@ static void addAuthenticationModules(Modules& modules)
>  }
>
>
> +static void addHookModules(Modules& modules)
> +{
> +  const string libraryPath = path::join(
> +      tests::flags.build_dir,
> +      "src",
> +      ".libs",
> +      os::libraries::expandName("testhook"));
> +
> +  // Now add our test hook module.
> +  Modules::Library* library = modules.add_libraries();
> +  library->set_file(libraryPath);
> +
> +  // To add a new module from this library, create a new ModuleID enum
> +  // and tie it with a module name.
> +  addModule(library, TestHook, "org_apache_mesos_TestHook");
> +}
> +
> +
>  Try<Nothing> tests::initModules(const Option<Modules>& modules)
>  {
>    // First get the user provided modules.
> @@ -107,6 +125,9 @@ Try<Nothing> tests::initModules(const Option<Modules>&
> modules)
>    // Add authentication modules from testauthentication library.
>    addAuthenticationModules(mergedModules);
>
> +  // Add hook modules from testhook library.
> +  addHookModules(mergedModules);
> +
>    return ModuleManager::load(mergedModules);
>  }
>
>
>
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/tests/module.hpp
> ----------------------------------------------------------------------
> diff --git a/src/tests/module.hpp b/src/tests/module.hpp
> index bc1a37d..21e8fc4 100644
> --- a/src/tests/module.hpp
> +++ b/src/tests/module.hpp
> @@ -42,7 +42,8 @@ enum ModuleID
>    TestMemIsolator,
>    TestCpuIsolator,
>    TestCRAMMD5Authenticatee,
> -  TestCRAMMD5Authenticator
> +  TestCRAMMD5Authenticator,
> +  TestHook
>  };
>
>
>
>

Re: [1/4] mesos git commit: Added example hook module.

Posted by Michael Park <mc...@gmail.com>.
Hey Vinod,

I've closed https://issues.apache.org/jira/browse/MESOS-2227 as a duplicate
of https://issues.apache.org/jira/browse/MESOS-2049 and the review request
is available at: https://reviews.apache.org/r/29948.

Thanks,

MPark

On 15 January 2015 at 15:51, Niklas Nielsen <ni...@mesosphere.io> wrote:

> Sure thing - apologize the inconvenience.
>
> Niklas
>
> On 15 January 2015 at 12:46, Vinod Kone <vi...@apache.org> wrote:
>
> > Hey Niklas. One of the hook tests is continously failing on our internal
> > CI. I've filed https://issues.apache.org/jira/browse/MESOS-2226. Can you
> > take a look?
> >
> > Also, https://issues.apache.org/jira/browse/MESOS-2227
> >
> > On Wed, Jan 14, 2015 at 3:41 PM, <nn...@apache.org> wrote:
> >
> > > Repository: mesos
> > > Updated Branches:
> > >   refs/heads/master 60007b9e7 -> 4e110d5d0
> > >
> > >
> > > Added example hook module.
> > >
> > > This module provides hooks for master label decorator, slave executor
> > > environment decorator and slave remove executor.  A couple of test
> > > cases are also provided to verify the hooks from this module.
> > >
> > > Review: https://reviews.apache.org/r/29496
> > >
> > >
> > > Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
> > > Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/4e110d5d
> > > Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/4e110d5d
> > > Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/4e110d5d
> > >
> > > Branch: refs/heads/master
> > > Commit: 4e110d5d0f0c6cd65b8b6e5edff30bb4c589a278
> > > Parents: 0773763
> > > Author: Kapil Arya <ka...@mesosphere.io>
> > > Authored: Wed Jan 14 13:49:06 2015 -0800
> > > Committer: Niklas Q. Nielsen <ni...@mesosphere.io>
> > > Committed: Wed Jan 14 15:11:37 2015 -0800
> > >
> > > ----------------------------------------------------------------------
> > >  src/Makefile.am                   |   7 +
> > >  src/examples/test_hook_module.cpp | 138 +++++++++++++++++
> > >  src/tests/hook_tests.cpp          | 272
> > +++++++++++++++++++++++++++++++++
> > >  src/tests/module.cpp              |  21 +++
> > >  src/tests/module.hpp              |   3 +-
> > >  5 files changed, 440 insertions(+), 1 deletion(-)
> > > ----------------------------------------------------------------------
> > >
> > >
> > >
> >
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/Makefile.am
> > > ----------------------------------------------------------------------
> > > diff --git a/src/Makefile.am b/src/Makefile.am
> > > index 100db39..256384e 100644
> > > --- a/src/Makefile.am
> > > +++ b/src/Makefile.am
> > > @@ -1237,6 +1237,12 @@ libtestauthentication_la_SOURCES =
> > > examples/test_authentication_modules.cpp
> > >  libtestauthentication_la_CPPFLAGS = $(MESOS_CPPFLAGS)
> > >  libtestauthentication_la_LDFLAGS = -release $(PACKAGE_VERSION) -shared
> > >
> > > +# Library containing test Hook module.
> > > +lib_LTLIBRARIES += libtesthook.la
> > > +libtesthook_la_SOURCES = examples/test_hook_module.cpp
> > > +libtesthook_la_CPPFLAGS = $(MESOS_CPPFLAGS)
> > > +libtesthook_la_LDFLAGS = -release $(PACKAGE_VERSION) -shared
> > > +
> > >  mesos_tests_SOURCES =                          \
> > >    tests/attributes_tests.cpp                   \
> > >    tests/authentication_tests.cpp               \
> > > @@ -1260,6 +1266,7 @@ mesos_tests_SOURCES =
>  \
> > >    tests/flags.cpp                              \
> > >    tests/gc_tests.cpp                           \
> > >    tests/hierarchical_allocator_tests.cpp       \
> > > +  tests/hook_tests.cpp                         \
> > >    tests/isolator_tests.cpp                     \
> > >    tests/log_tests.cpp                          \
> > >    tests/logging_tests.cpp                      \
> > >
> > >
> > >
> >
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/examples/test_hook_module.cpp
> > > ----------------------------------------------------------------------
> > > diff --git a/src/examples/test_hook_module.cpp
> > > b/src/examples/test_hook_module.cpp
> > > new file mode 100644
> > > index 0000000..04fd43e
> > > --- /dev/null
> > > +++ b/src/examples/test_hook_module.cpp
> > > @@ -0,0 +1,138 @@
> > > +/**
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License.  You may obtain a copy of the License at
> > > + *
> > > + *     http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing, software
> > > + * distributed under the License is distributed on an "AS IS" BASIS,
> > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> > > implied.
> > > + * See the License for the specific language governing permissions and
> > > + * limitations under the License.
> > > + */
> > > +
> > > +#include <string>
> > > +
> > > +#include <mesos/mesos.hpp>
> > > +#include <mesos/module.hpp>
> > > +
> > > +#include <stout/foreach.hpp>
> > > +#include <stout/os.hpp>
> > > +#include <stout/try.hpp>
> > > +
> > > +#include "hook/hook.hpp"
> > > +#include "master/master.hpp"
> > > +#include "module/hook.hpp"
> > > +#include "slave/slave.hpp"
> > > +
> > > +using std::string;
> > > +
> > > +using namespace mesos;
> > > +using namespace mesos::internal;
> > > +
> > > +// Must be kept in sync with variables of the same name in
> > > +// tests/hook_tests.cpp.
> > > +const char* testLabelKey = "MESOS_Test_Label";
> > > +const char* testLabelValue = "ApacheMesos";
> > > +const char* testEnvironmentVariableName =
> > > "MESOS_TEST_ENVIRONMENT_VARIABLE";
> > > +
> > > +class TestHook : public Hook
> > > +{
> > > +public:
> > > +  virtual Result<Labels> masterLaunchTaskLabelDecorator(
> > > +      const TaskInfo& taskInfo,
> > > +      const FrameworkInfo& frameworkInfo,
> > > +      const SlaveInfo& slaveInfo)
> > > +  {
> > > +    LOG(INFO) << "Executing 'masterLaunchTaskLabelDecorator' hook";
> > > +
> > > +    Labels labels;
> > > +    Label *label = labels.add_labels();
> > > +    label->set_key(testLabelKey);
> > > +    label->set_value(testLabelValue);
> > > +
> > > +    return labels;
> > > +  }
> > > +
> > > +
> > > +  // In this hook, we create a temporary file and add its path to an
> > > +  // environment variable.  Later on, this environment variable is
> > > +  // looked up by the removeExecutorHook to locate and delete this
> > > +  // file.
> > > +  virtual Result<Environment> slaveLaunchExecutorEnvironmentDecorator(
> > > +      const ExecutorInfo& executorInfo,
> > > +      const TaskInfo& taskInfo)
> > > +  {
> > > +    LOG(INFO) << "Executing 'slaveLaunchExecutorEnvironmentDecorator'
> > > hook";
> > > +
> > > +    // Find the label value for the label that was created in the
> > > +    // label decorator hook above.
> > > +    Option<string> labelValue;
> > > +    foreach (const Label& label, taskInfo.labels().labels()) {
> > > +      if (label.key() == testLabelKey) {
> > > +        labelValue = label.value();
> > > +        CHECK_EQ(labelValue.get(), testLabelValue);
> > > +      }
> > > +    }
> > > +    CHECK_SOME(labelValue);
> > > +
> > > +    // Create a temporary file.
> > > +    Try<string> file = os::mktemp();
> > > +    CHECK_SOME(file);
> > > +    CHECK_SOME(os::write(file.get(), labelValue.get()));
> > > +
> > > +    // Inject file path into command environment.
> > > +    Environment environment;
> > > +    Environment::Variable* variable = environment.add_variables();
> > > +    variable->set_name(testEnvironmentVariableName);
> > > +    variable->set_value(file.get());
> > > +
> > > +    return environment;
> > > +  }
> > > +
> > > +
> > > +  // This hook locates the file created by environment decorator hook
> > > +  // and deletes it.
> > > +  virtual Try<Nothing> slaveRemoveExecutorHook(
> > > +      const FrameworkInfo& frameworkInfo,
> > > +      const ExecutorInfo& executorInfo)
> > > +  {
> > > +    LOG(INFO) << "Executing 'slaveRemoveExecutorHook'";
> > > +
> > > +    foreach (const Environment::Variable& variable,
> > > +        executorInfo.command().environment().variables()) {
> > > +      if (variable.name() == testEnvironmentVariableName) {
> > > +        string path = variable.value();
> > > +        // The removeExecutor hook may be called multiple times; thus
> > > +        // we ignore the subsequent calls.
> > > +        if (os::isfile(path)) {
> > > +          CHECK_SOME(os::rm(path));
> > > +        }
> > > +        break;
> > > +      }
> > > +    }
> > > +    return Nothing();
> > > +  }
> > > +};
> > > +
> > > +
> > > +static Hook* createHook(const Parameters& parameters)
> > > +{
> > > +  return new TestHook();
> > > +}
> > > +
> > > +
> > > +// Declares a Hook module named 'TestHook'.
> > > +mesos::modules::Module<Hook> org_apache_mesos_TestHook(
> > > +    MESOS_MODULE_API_VERSION,
> > > +    MESOS_VERSION,
> > > +    "Apache Mesos",
> > > +    "modules@mesos.apache.org",
> > > +    "Test Hook module.",
> > > +    NULL,
> > > +    createHook);
> > >
> > >
> > >
> >
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/tests/hook_tests.cpp
> > > ----------------------------------------------------------------------
> > > diff --git a/src/tests/hook_tests.cpp b/src/tests/hook_tests.cpp
> > > new file mode 100644
> > > index 0000000..4e9a3a7
> > > --- /dev/null
> > > +++ b/src/tests/hook_tests.cpp
> > > @@ -0,0 +1,272 @@
> > > +/**
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License.  You may obtain a copy of the License at
> > > + *
> > > + *     http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing, software
> > > + * distributed under the License is distributed on an "AS IS" BASIS,
> > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> > > implied.
> > > + * See the License for the specific language governing permissions and
> > > + * limitations under the License.
> > > + */
> > > +
> > > +#include <mesos/module.hpp>
> > > +
> > > +#include <process/future.hpp>
> > > +#include <process/gmock.hpp>
> > > +#include <process/pid.hpp>
> > > +
> > > +#include <stout/option.hpp>
> > > +#include <stout/os.hpp>
> > > +#include <stout/try.hpp>
> > > +
> > > +#include "hook/manager.hpp"
> > > +#include "module/manager.hpp"
> > > +#include "master/flags.hpp"
> > > +#include "master/master.hpp"
> > > +#include "slave/flags.hpp"
> > > +#include "slave/slave.hpp"
> > > +
> > > +#include "tests/containerizer.hpp"
> > > +#include "tests/flags.hpp"
> > > +#include "tests/mesos.hpp"
> > > +
> > > +using std::string;
> > > +
> > > +using namespace mesos;
> > > +using namespace mesos::internal;
> > > +using namespace mesos::internal::tests;
> > > +using namespace mesos::modules;
> > > +
> > > +using mesos::internal::master::Master;
> > > +using mesos::internal::slave::Slave;
> > > +
> > > +using process::Future;
> > > +using process::PID;
> > > +
> > > +using std::string;
> > > +using std::vector;
> > > +
> > > +using testing::_;
> > > +using testing::Return;
> > > +
> > > +const char* HOOK_MODULE_LIBRARY_NAME = "testhook";
> > > +const char* HOOK_MODULE_NAME = "org_apache_mesos_TestHook";
> > > +
> > > +// Must be kept in sync with variables of the same name in
> > > +// examples/test_hook_module.cpp.
> > > +const char* testLabelKey = "MESOS_Test_Label";
> > > +const char* testLabelValue = "ApacheMesos";
> > > +const char* testEnvironmentVariableName =
> > > "MESOS_TEST_ENVIRONMENT_VARIABLE";
> > > +
> > > +class HookTest : public MesosTest
> > > +{
> > > +protected:
> > > +  // TODO(karya): Replace constructor/destructor with SetUp/TearDown.
> > > +  // Currently, using SetUp/TearDown causes VerifySlave* test to
> > > +  // fail with a duplicate slave id message.  However, everything
> > > +  // seems normal when using this construction/destructor combo.
> > > +  HookTest()
> > > +  {
> > > +    // Install hooks.
> > > +    EXPECT_SOME(HookManager::initialize(HOOK_MODULE_NAME));
> > > +  }
> > > +
> > > +  ~HookTest()
> > > +  {
> > > +    // Unload the hooks so a subsequent install may succeed.
> > > +    EXPECT_SOME(HookManager::unload(HOOK_MODULE_NAME));
> > > +  }
> > > +};
> > > +
> > > +
> > > +// Test varioud hook install/uninstall mechanisms.
> > > +TEST_F(HookTest, HookLoading)
> > > +{
> > > +  // Installing unknown hooks should fail.
> > > +  EXPECT_ERROR(HookManager::initialize("Unknown Hook"));
> > > +
> > > +  // Uninstalling an unknown hook should fail.
> > > +  EXPECT_ERROR(HookManager::unload("Unknown Hook"));
> > > +
> > > +  // Installing an already installed hook should fail.
> > > +  EXPECT_ERROR(HookManager::initialize(HOOK_MODULE_NAME));
> > > +
> > > +  // Uninstalling a hook should succeed.
> > > +  EXPECT_SOME(HookManager::unload(HOOK_MODULE_NAME));
> > > +
> > > +  // Uninstalling an already uninstalled hook should fail.
> > > +  EXPECT_ERROR(HookManager::unload(HOOK_MODULE_NAME));
> > > +  // This is needed to allow the tear-down to succeed.
> > > +  EXPECT_SOME(HookManager::initialize(HOOK_MODULE_NAME));
> > > +}
> > > +
> > > +
> > > +// Test that the label decorator hook hangs a new label off the
> > > +// taskinfo message during master launch task.
> > > +TEST_F(HookTest, VerifyMasterLaunchTaskHook)
> > > +{
> > > +  Try<PID<Master> > master = StartMaster(CreateMasterFlags());
> > > +  ASSERT_SOME(master);
> > > +
> > > +  TestContainerizer containerizer;
> > > +
> > > +  StandaloneMasterDetector detector(master.get());
> > > +
> > > +  // Start a mock slave since we aren't testing the slave hooks yet.
> > > +  MockSlave slave(CreateSlaveFlags(), &detector, &containerizer);
> > > +  process::spawn(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());
> > > +
> > > +  CommandInfo command;
> > > +  command.set_value("sleep 10");
> > > +
> > > +  // 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());
> > > +  task.mutable_command()->MergeFrom(command);
> > > +
> > > +  vector<TaskInfo> tasks;
> > > +  tasks.push_back(task);
> > > +
> > > +  Future<TaskInfo> taskInfo;
> > > +  EXPECT_CALL(slave, runTask(_, _, _, _, _))
> > > +    .Times(1)
> > > +    .WillOnce(FutureArg<4>(&taskInfo));
> > > +
> > > +  driver.launchTasks(offers.get()[0].id(), tasks);
> > > +
> > > +  AWAIT_READY(taskInfo);
> > > +
> > > +  // At launchTasks, the label decorator hook inside should have been
> > > +  // executed and we should see the labels now.
> > > +  Option<string> labelValue;
> > > +  foreach (const Label& label, taskInfo.get().labels().labels()) {
> > > +    if (label.key() == testLabelKey) {
> > > +      labelValue = label.value();
> > > +    }
> > > +  }
> > > +  EXPECT_SOME_EQ(testLabelValue, labelValue);
> > > +
> > > +  driver.stop();
> > > +  driver.join();
> > > +
> > > +  process::terminate(slave);
> > > +  process::wait(slave);
> > > +
> > > +  Shutdown(); // Must shutdown before 'containerizer' gets
> deallocated.
> > > +}
> > > +
> > > +
> > > +// Test executor environment decorator hook and remove executor hook
> > > +// for slave.  We expect the environment-decorator hook to create a
> > > +// temporary file and the remove-executor hook to delete that file.
> > > +TEST_F(HookTest, VerifySlaveLaunchExecutorHook)
> > > +{
> > > +  master::Flags masterFlags = CreateMasterFlags();
> > > +
> > > +  Try<PID<Master> > master = StartMaster(masterFlags);
> > > +  ASSERT_SOME(master);
> > > +
> > > +  slave::Flags slaveFlags = CreateSlaveFlags();
> > > +
> > > +  MockExecutor exec(DEFAULT_EXECUTOR_ID);
> > > +
> > > +  TestContainerizer containerizer(&exec);
> > > +
> > > +  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());
> > > +  task.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
> > > +
> > > +  vector<TaskInfo> tasks;
> > > +  tasks.push_back(task);
> > > +
> > > +  EXPECT_CALL(exec, launchTask(_, _));
> > > +
> > > +  Future<ExecutorInfo> executorInfo;
> > > +  EXPECT_CALL(exec, registered(_, _, _, _))
> > > +    .Times(1)
> > > +    .WillOnce(FutureArg<1>(&executorInfo));
> > > +
> > > +  driver.launchTasks(offers.get()[0].id(), tasks);
> > > +
> > > +  AWAIT_READY(executorInfo);
> > > +
> > > +  // At launchTasks, the label decorator hook inside should have been
> > > +  // executed and we should see the labels now.
> > > +  // Further, the environment decorator hook should also have been
> > > +  // executed.  In that hook, we create a temp file and set the path
> > > +  // as the value of the environment variable.
> > > +  // Here we verify that the environment variable is present and the
> > > +  // file is present on the disk.
> > > +  Option<string> path;
> > > +  foreach (const Environment::Variable& variable,
> > > +           executorInfo.get().command().environment().variables()) {
> > > +    if (variable.name() == testEnvironmentVariableName) {
> > > +      path = variable.value();
> > > +      break;
> > > +    }
> > > +  }
> > > +
> > > +  EXPECT_SOME(path);
> > > +  // The file must have been create by the environment decorator hook.
> > > +  EXPECT_TRUE(os::isfile(path.get()));
> > > +
> > > +  driver.stop();
> > > +  driver.join();
> > > +
> > > +  Shutdown(); // Must shutdown before 'containerizer' gets
> deallocated.
> > > +
> > > +  // The removeExecutor hook in the test module deletes the temp file.
> > > +  // Verify that the file is not present.
> > > +  EXPECT_FALSE(os::isfile(path.get()));
> > > +}
> > >
> > >
> > >
> >
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/tests/module.cpp
> > > ----------------------------------------------------------------------
> > > diff --git a/src/tests/module.cpp b/src/tests/module.cpp
> > > index 6cec1cb..e6dbf94 100644
> > > --- a/src/tests/module.cpp
> > > +++ b/src/tests/module.cpp
> > > @@ -93,6 +93,24 @@ static void addAuthenticationModules(Modules&
> modules)
> > >  }
> > >
> > >
> > > +static void addHookModules(Modules& modules)
> > > +{
> > > +  const string libraryPath = path::join(
> > > +      tests::flags.build_dir,
> > > +      "src",
> > > +      ".libs",
> > > +      os::libraries::expandName("testhook"));
> > > +
> > > +  // Now add our test hook module.
> > > +  Modules::Library* library = modules.add_libraries();
> > > +  library->set_file(libraryPath);
> > > +
> > > +  // To add a new module from this library, create a new ModuleID enum
> > > +  // and tie it with a module name.
> > > +  addModule(library, TestHook, "org_apache_mesos_TestHook");
> > > +}
> > > +
> > > +
> > >  Try<Nothing> tests::initModules(const Option<Modules>& modules)
> > >  {
> > >    // First get the user provided modules.
> > > @@ -107,6 +125,9 @@ Try<Nothing> tests::initModules(const
> > Option<Modules>&
> > > modules)
> > >    // Add authentication modules from testauthentication library.
> > >    addAuthenticationModules(mergedModules);
> > >
> > > +  // Add hook modules from testhook library.
> > > +  addHookModules(mergedModules);
> > > +
> > >    return ModuleManager::load(mergedModules);
> > >  }
> > >
> > >
> > >
> > >
> >
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/tests/module.hpp
> > > ----------------------------------------------------------------------
> > > diff --git a/src/tests/module.hpp b/src/tests/module.hpp
> > > index bc1a37d..21e8fc4 100644
> > > --- a/src/tests/module.hpp
> > > +++ b/src/tests/module.hpp
> > > @@ -42,7 +42,8 @@ enum ModuleID
> > >    TestMemIsolator,
> > >    TestCpuIsolator,
> > >    TestCRAMMD5Authenticatee,
> > > -  TestCRAMMD5Authenticator
> > > +  TestCRAMMD5Authenticator,
> > > +  TestHook
> > >  };
> > >
> > >
> > >
> > >
> >
>

Re: [1/4] mesos git commit: Added example hook module.

Posted by Niklas Nielsen <ni...@mesosphere.io>.
Sure thing - apologize the inconvenience.

Niklas

On 15 January 2015 at 12:46, Vinod Kone <vi...@apache.org> wrote:

> Hey Niklas. One of the hook tests is continously failing on our internal
> CI. I've filed https://issues.apache.org/jira/browse/MESOS-2226. Can you
> take a look?
>
> Also, https://issues.apache.org/jira/browse/MESOS-2227
>
> On Wed, Jan 14, 2015 at 3:41 PM, <nn...@apache.org> wrote:
>
> > Repository: mesos
> > Updated Branches:
> >   refs/heads/master 60007b9e7 -> 4e110d5d0
> >
> >
> > Added example hook module.
> >
> > This module provides hooks for master label decorator, slave executor
> > environment decorator and slave remove executor.  A couple of test
> > cases are also provided to verify the hooks from this module.
> >
> > Review: https://reviews.apache.org/r/29496
> >
> >
> > Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
> > Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/4e110d5d
> > Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/4e110d5d
> > Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/4e110d5d
> >
> > Branch: refs/heads/master
> > Commit: 4e110d5d0f0c6cd65b8b6e5edff30bb4c589a278
> > Parents: 0773763
> > Author: Kapil Arya <ka...@mesosphere.io>
> > Authored: Wed Jan 14 13:49:06 2015 -0800
> > Committer: Niklas Q. Nielsen <ni...@mesosphere.io>
> > Committed: Wed Jan 14 15:11:37 2015 -0800
> >
> > ----------------------------------------------------------------------
> >  src/Makefile.am                   |   7 +
> >  src/examples/test_hook_module.cpp | 138 +++++++++++++++++
> >  src/tests/hook_tests.cpp          | 272
> +++++++++++++++++++++++++++++++++
> >  src/tests/module.cpp              |  21 +++
> >  src/tests/module.hpp              |   3 +-
> >  5 files changed, 440 insertions(+), 1 deletion(-)
> > ----------------------------------------------------------------------
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/Makefile.am
> > ----------------------------------------------------------------------
> > diff --git a/src/Makefile.am b/src/Makefile.am
> > index 100db39..256384e 100644
> > --- a/src/Makefile.am
> > +++ b/src/Makefile.am
> > @@ -1237,6 +1237,12 @@ libtestauthentication_la_SOURCES =
> > examples/test_authentication_modules.cpp
> >  libtestauthentication_la_CPPFLAGS = $(MESOS_CPPFLAGS)
> >  libtestauthentication_la_LDFLAGS = -release $(PACKAGE_VERSION) -shared
> >
> > +# Library containing test Hook module.
> > +lib_LTLIBRARIES += libtesthook.la
> > +libtesthook_la_SOURCES = examples/test_hook_module.cpp
> > +libtesthook_la_CPPFLAGS = $(MESOS_CPPFLAGS)
> > +libtesthook_la_LDFLAGS = -release $(PACKAGE_VERSION) -shared
> > +
> >  mesos_tests_SOURCES =                          \
> >    tests/attributes_tests.cpp                   \
> >    tests/authentication_tests.cpp               \
> > @@ -1260,6 +1266,7 @@ mesos_tests_SOURCES =                             \
> >    tests/flags.cpp                              \
> >    tests/gc_tests.cpp                           \
> >    tests/hierarchical_allocator_tests.cpp       \
> > +  tests/hook_tests.cpp                         \
> >    tests/isolator_tests.cpp                     \
> >    tests/log_tests.cpp                          \
> >    tests/logging_tests.cpp                      \
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/examples/test_hook_module.cpp
> > ----------------------------------------------------------------------
> > diff --git a/src/examples/test_hook_module.cpp
> > b/src/examples/test_hook_module.cpp
> > new file mode 100644
> > index 0000000..04fd43e
> > --- /dev/null
> > +++ b/src/examples/test_hook_module.cpp
> > @@ -0,0 +1,138 @@
> > +/**
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License.  You may obtain a copy of the License at
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> > implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +#include <string>
> > +
> > +#include <mesos/mesos.hpp>
> > +#include <mesos/module.hpp>
> > +
> > +#include <stout/foreach.hpp>
> > +#include <stout/os.hpp>
> > +#include <stout/try.hpp>
> > +
> > +#include "hook/hook.hpp"
> > +#include "master/master.hpp"
> > +#include "module/hook.hpp"
> > +#include "slave/slave.hpp"
> > +
> > +using std::string;
> > +
> > +using namespace mesos;
> > +using namespace mesos::internal;
> > +
> > +// Must be kept in sync with variables of the same name in
> > +// tests/hook_tests.cpp.
> > +const char* testLabelKey = "MESOS_Test_Label";
> > +const char* testLabelValue = "ApacheMesos";
> > +const char* testEnvironmentVariableName =
> > "MESOS_TEST_ENVIRONMENT_VARIABLE";
> > +
> > +class TestHook : public Hook
> > +{
> > +public:
> > +  virtual Result<Labels> masterLaunchTaskLabelDecorator(
> > +      const TaskInfo& taskInfo,
> > +      const FrameworkInfo& frameworkInfo,
> > +      const SlaveInfo& slaveInfo)
> > +  {
> > +    LOG(INFO) << "Executing 'masterLaunchTaskLabelDecorator' hook";
> > +
> > +    Labels labels;
> > +    Label *label = labels.add_labels();
> > +    label->set_key(testLabelKey);
> > +    label->set_value(testLabelValue);
> > +
> > +    return labels;
> > +  }
> > +
> > +
> > +  // In this hook, we create a temporary file and add its path to an
> > +  // environment variable.  Later on, this environment variable is
> > +  // looked up by the removeExecutorHook to locate and delete this
> > +  // file.
> > +  virtual Result<Environment> slaveLaunchExecutorEnvironmentDecorator(
> > +      const ExecutorInfo& executorInfo,
> > +      const TaskInfo& taskInfo)
> > +  {
> > +    LOG(INFO) << "Executing 'slaveLaunchExecutorEnvironmentDecorator'
> > hook";
> > +
> > +    // Find the label value for the label that was created in the
> > +    // label decorator hook above.
> > +    Option<string> labelValue;
> > +    foreach (const Label& label, taskInfo.labels().labels()) {
> > +      if (label.key() == testLabelKey) {
> > +        labelValue = label.value();
> > +        CHECK_EQ(labelValue.get(), testLabelValue);
> > +      }
> > +    }
> > +    CHECK_SOME(labelValue);
> > +
> > +    // Create a temporary file.
> > +    Try<string> file = os::mktemp();
> > +    CHECK_SOME(file);
> > +    CHECK_SOME(os::write(file.get(), labelValue.get()));
> > +
> > +    // Inject file path into command environment.
> > +    Environment environment;
> > +    Environment::Variable* variable = environment.add_variables();
> > +    variable->set_name(testEnvironmentVariableName);
> > +    variable->set_value(file.get());
> > +
> > +    return environment;
> > +  }
> > +
> > +
> > +  // This hook locates the file created by environment decorator hook
> > +  // and deletes it.
> > +  virtual Try<Nothing> slaveRemoveExecutorHook(
> > +      const FrameworkInfo& frameworkInfo,
> > +      const ExecutorInfo& executorInfo)
> > +  {
> > +    LOG(INFO) << "Executing 'slaveRemoveExecutorHook'";
> > +
> > +    foreach (const Environment::Variable& variable,
> > +        executorInfo.command().environment().variables()) {
> > +      if (variable.name() == testEnvironmentVariableName) {
> > +        string path = variable.value();
> > +        // The removeExecutor hook may be called multiple times; thus
> > +        // we ignore the subsequent calls.
> > +        if (os::isfile(path)) {
> > +          CHECK_SOME(os::rm(path));
> > +        }
> > +        break;
> > +      }
> > +    }
> > +    return Nothing();
> > +  }
> > +};
> > +
> > +
> > +static Hook* createHook(const Parameters& parameters)
> > +{
> > +  return new TestHook();
> > +}
> > +
> > +
> > +// Declares a Hook module named 'TestHook'.
> > +mesos::modules::Module<Hook> org_apache_mesos_TestHook(
> > +    MESOS_MODULE_API_VERSION,
> > +    MESOS_VERSION,
> > +    "Apache Mesos",
> > +    "modules@mesos.apache.org",
> > +    "Test Hook module.",
> > +    NULL,
> > +    createHook);
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/tests/hook_tests.cpp
> > ----------------------------------------------------------------------
> > diff --git a/src/tests/hook_tests.cpp b/src/tests/hook_tests.cpp
> > new file mode 100644
> > index 0000000..4e9a3a7
> > --- /dev/null
> > +++ b/src/tests/hook_tests.cpp
> > @@ -0,0 +1,272 @@
> > +/**
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License.  You may obtain a copy of the License at
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> > implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +#include <mesos/module.hpp>
> > +
> > +#include <process/future.hpp>
> > +#include <process/gmock.hpp>
> > +#include <process/pid.hpp>
> > +
> > +#include <stout/option.hpp>
> > +#include <stout/os.hpp>
> > +#include <stout/try.hpp>
> > +
> > +#include "hook/manager.hpp"
> > +#include "module/manager.hpp"
> > +#include "master/flags.hpp"
> > +#include "master/master.hpp"
> > +#include "slave/flags.hpp"
> > +#include "slave/slave.hpp"
> > +
> > +#include "tests/containerizer.hpp"
> > +#include "tests/flags.hpp"
> > +#include "tests/mesos.hpp"
> > +
> > +using std::string;
> > +
> > +using namespace mesos;
> > +using namespace mesos::internal;
> > +using namespace mesos::internal::tests;
> > +using namespace mesos::modules;
> > +
> > +using mesos::internal::master::Master;
> > +using mesos::internal::slave::Slave;
> > +
> > +using process::Future;
> > +using process::PID;
> > +
> > +using std::string;
> > +using std::vector;
> > +
> > +using testing::_;
> > +using testing::Return;
> > +
> > +const char* HOOK_MODULE_LIBRARY_NAME = "testhook";
> > +const char* HOOK_MODULE_NAME = "org_apache_mesos_TestHook";
> > +
> > +// Must be kept in sync with variables of the same name in
> > +// examples/test_hook_module.cpp.
> > +const char* testLabelKey = "MESOS_Test_Label";
> > +const char* testLabelValue = "ApacheMesos";
> > +const char* testEnvironmentVariableName =
> > "MESOS_TEST_ENVIRONMENT_VARIABLE";
> > +
> > +class HookTest : public MesosTest
> > +{
> > +protected:
> > +  // TODO(karya): Replace constructor/destructor with SetUp/TearDown.
> > +  // Currently, using SetUp/TearDown causes VerifySlave* test to
> > +  // fail with a duplicate slave id message.  However, everything
> > +  // seems normal when using this construction/destructor combo.
> > +  HookTest()
> > +  {
> > +    // Install hooks.
> > +    EXPECT_SOME(HookManager::initialize(HOOK_MODULE_NAME));
> > +  }
> > +
> > +  ~HookTest()
> > +  {
> > +    // Unload the hooks so a subsequent install may succeed.
> > +    EXPECT_SOME(HookManager::unload(HOOK_MODULE_NAME));
> > +  }
> > +};
> > +
> > +
> > +// Test varioud hook install/uninstall mechanisms.
> > +TEST_F(HookTest, HookLoading)
> > +{
> > +  // Installing unknown hooks should fail.
> > +  EXPECT_ERROR(HookManager::initialize("Unknown Hook"));
> > +
> > +  // Uninstalling an unknown hook should fail.
> > +  EXPECT_ERROR(HookManager::unload("Unknown Hook"));
> > +
> > +  // Installing an already installed hook should fail.
> > +  EXPECT_ERROR(HookManager::initialize(HOOK_MODULE_NAME));
> > +
> > +  // Uninstalling a hook should succeed.
> > +  EXPECT_SOME(HookManager::unload(HOOK_MODULE_NAME));
> > +
> > +  // Uninstalling an already uninstalled hook should fail.
> > +  EXPECT_ERROR(HookManager::unload(HOOK_MODULE_NAME));
> > +  // This is needed to allow the tear-down to succeed.
> > +  EXPECT_SOME(HookManager::initialize(HOOK_MODULE_NAME));
> > +}
> > +
> > +
> > +// Test that the label decorator hook hangs a new label off the
> > +// taskinfo message during master launch task.
> > +TEST_F(HookTest, VerifyMasterLaunchTaskHook)
> > +{
> > +  Try<PID<Master> > master = StartMaster(CreateMasterFlags());
> > +  ASSERT_SOME(master);
> > +
> > +  TestContainerizer containerizer;
> > +
> > +  StandaloneMasterDetector detector(master.get());
> > +
> > +  // Start a mock slave since we aren't testing the slave hooks yet.
> > +  MockSlave slave(CreateSlaveFlags(), &detector, &containerizer);
> > +  process::spawn(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());
> > +
> > +  CommandInfo command;
> > +  command.set_value("sleep 10");
> > +
> > +  // 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());
> > +  task.mutable_command()->MergeFrom(command);
> > +
> > +  vector<TaskInfo> tasks;
> > +  tasks.push_back(task);
> > +
> > +  Future<TaskInfo> taskInfo;
> > +  EXPECT_CALL(slave, runTask(_, _, _, _, _))
> > +    .Times(1)
> > +    .WillOnce(FutureArg<4>(&taskInfo));
> > +
> > +  driver.launchTasks(offers.get()[0].id(), tasks);
> > +
> > +  AWAIT_READY(taskInfo);
> > +
> > +  // At launchTasks, the label decorator hook inside should have been
> > +  // executed and we should see the labels now.
> > +  Option<string> labelValue;
> > +  foreach (const Label& label, taskInfo.get().labels().labels()) {
> > +    if (label.key() == testLabelKey) {
> > +      labelValue = label.value();
> > +    }
> > +  }
> > +  EXPECT_SOME_EQ(testLabelValue, labelValue);
> > +
> > +  driver.stop();
> > +  driver.join();
> > +
> > +  process::terminate(slave);
> > +  process::wait(slave);
> > +
> > +  Shutdown(); // Must shutdown before 'containerizer' gets deallocated.
> > +}
> > +
> > +
> > +// Test executor environment decorator hook and remove executor hook
> > +// for slave.  We expect the environment-decorator hook to create a
> > +// temporary file and the remove-executor hook to delete that file.
> > +TEST_F(HookTest, VerifySlaveLaunchExecutorHook)
> > +{
> > +  master::Flags masterFlags = CreateMasterFlags();
> > +
> > +  Try<PID<Master> > master = StartMaster(masterFlags);
> > +  ASSERT_SOME(master);
> > +
> > +  slave::Flags slaveFlags = CreateSlaveFlags();
> > +
> > +  MockExecutor exec(DEFAULT_EXECUTOR_ID);
> > +
> > +  TestContainerizer containerizer(&exec);
> > +
> > +  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());
> > +  task.mutable_executor()->MergeFrom(DEFAULT_EXECUTOR_INFO);
> > +
> > +  vector<TaskInfo> tasks;
> > +  tasks.push_back(task);
> > +
> > +  EXPECT_CALL(exec, launchTask(_, _));
> > +
> > +  Future<ExecutorInfo> executorInfo;
> > +  EXPECT_CALL(exec, registered(_, _, _, _))
> > +    .Times(1)
> > +    .WillOnce(FutureArg<1>(&executorInfo));
> > +
> > +  driver.launchTasks(offers.get()[0].id(), tasks);
> > +
> > +  AWAIT_READY(executorInfo);
> > +
> > +  // At launchTasks, the label decorator hook inside should have been
> > +  // executed and we should see the labels now.
> > +  // Further, the environment decorator hook should also have been
> > +  // executed.  In that hook, we create a temp file and set the path
> > +  // as the value of the environment variable.
> > +  // Here we verify that the environment variable is present and the
> > +  // file is present on the disk.
> > +  Option<string> path;
> > +  foreach (const Environment::Variable& variable,
> > +           executorInfo.get().command().environment().variables()) {
> > +    if (variable.name() == testEnvironmentVariableName) {
> > +      path = variable.value();
> > +      break;
> > +    }
> > +  }
> > +
> > +  EXPECT_SOME(path);
> > +  // The file must have been create by the environment decorator hook.
> > +  EXPECT_TRUE(os::isfile(path.get()));
> > +
> > +  driver.stop();
> > +  driver.join();
> > +
> > +  Shutdown(); // Must shutdown before 'containerizer' gets deallocated.
> > +
> > +  // The removeExecutor hook in the test module deletes the temp file.
> > +  // Verify that the file is not present.
> > +  EXPECT_FALSE(os::isfile(path.get()));
> > +}
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/tests/module.cpp
> > ----------------------------------------------------------------------
> > diff --git a/src/tests/module.cpp b/src/tests/module.cpp
> > index 6cec1cb..e6dbf94 100644
> > --- a/src/tests/module.cpp
> > +++ b/src/tests/module.cpp
> > @@ -93,6 +93,24 @@ static void addAuthenticationModules(Modules& modules)
> >  }
> >
> >
> > +static void addHookModules(Modules& modules)
> > +{
> > +  const string libraryPath = path::join(
> > +      tests::flags.build_dir,
> > +      "src",
> > +      ".libs",
> > +      os::libraries::expandName("testhook"));
> > +
> > +  // Now add our test hook module.
> > +  Modules::Library* library = modules.add_libraries();
> > +  library->set_file(libraryPath);
> > +
> > +  // To add a new module from this library, create a new ModuleID enum
> > +  // and tie it with a module name.
> > +  addModule(library, TestHook, "org_apache_mesos_TestHook");
> > +}
> > +
> > +
> >  Try<Nothing> tests::initModules(const Option<Modules>& modules)
> >  {
> >    // First get the user provided modules.
> > @@ -107,6 +125,9 @@ Try<Nothing> tests::initModules(const
> Option<Modules>&
> > modules)
> >    // Add authentication modules from testauthentication library.
> >    addAuthenticationModules(mergedModules);
> >
> > +  // Add hook modules from testhook library.
> > +  addHookModules(mergedModules);
> > +
> >    return ModuleManager::load(mergedModules);
> >  }
> >
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/mesos/blob/4e110d5d/src/tests/module.hpp
> > ----------------------------------------------------------------------
> > diff --git a/src/tests/module.hpp b/src/tests/module.hpp
> > index bc1a37d..21e8fc4 100644
> > --- a/src/tests/module.hpp
> > +++ b/src/tests/module.hpp
> > @@ -42,7 +42,8 @@ enum ModuleID
> >    TestMemIsolator,
> >    TestCpuIsolator,
> >    TestCRAMMD5Authenticatee,
> > -  TestCRAMMD5Authenticator
> > +  TestCRAMMD5Authenticator,
> > +  TestHook
> >  };
> >
> >
> >
> >
>