Skip to content

Commit 7595efd

Browse files
author
Frédéric Boismenu
authored
Merge pull request #5 from fboismenu/unified_lib
Support for Isolator interface, with unique Ruby source script for hook and isolator
2 parents 749ad22 + 73806fd commit 7595efd

17 files changed

+643
-166
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
build/
2+
CMakeFiles/

CMakeLists.txt

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,43 @@
11
cmake_minimum_required(VERSION 2.8)
2-
project(ruby_hook)
32

4-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fpermissive -std=c++11")
3+
set(PROJECT_NAME mesos_modules_ruby)
4+
project(${PROJECT_NAME})
5+
6+
set(CMAKE_MACOSX_RPATH 1)
7+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -Wno-macro-redefined -std=c++11")
58

69
find_package(PkgConfig REQUIRED)
710

811
pkg_check_modules(RUBY REQUIRED ruby-2.4)
912

1013
if(DEFINED ENV{MESOS_BUILD_DIR})
11-
set(MESOS_BUILD_DIR "$ENV{MESOS_BUILD_DIR}")
14+
set(MESOS_BUILD_DIR $ENV{MESOS_BUILD_DIR})
1215
else()
1316
set(MESOS_BUILD_DIR "${PWD}")
1417
endif()
1518

19+
# Required include path lookup in Build dir
20+
file(GLOB BOOST_SRCH_PATH "${MESOS_BUILD_DIR}/3rdparty/boost-*")
21+
find_path(BOOST_INCLUDE_DIR NAMES "boost/version.hpp" PATHS ${BOOST_SRCH_PATH} NO_DEFAULT_PATH)
22+
23+
file(GLOB PROTO_SRCH_PATH "${MESOS_BUILD_DIR}/3rdparty/protobuf-*/src")
24+
find_path(PROTOBUF_INCLUDE_DIR NAMES "google/protobuf/stubs/common.h" PATHS ${PROTO_SRCH_PATH} NO_DEFAULT_PATH)
25+
26+
file(GLOB GLOG_SRCH_PATH "${MESOS_BUILD_DIR}/3rdparty/glog-*/src")
27+
find_path(GLOG_INCLUDE_DIR NAMES "glog/logging.h" PATHS ${GLOG_SRCH_PATH} NO_DEFAULT_PATH)
28+
29+
file(GLOB PICOJSON_SRCH_PATH "${MESOS_BUILD_DIR}/3rdparty/picojson-*")
30+
find_path(PICOJSON_INCLUDE_DIR NAMES "picojson.h" PATHS ${PICOJSON_SRCH_PATH} NO_DEFAULT_PATH)
31+
1632
include_directories(
17-
"${MESOS_BUILD_DIR}/3rdparty/boost-1.53.0"
18-
"${MESOS_BUILD_DIR}/3rdparty/glog-0.3.3/src"
19-
"${MESOS_BUILD_DIR}/3rdparty/protobuf-3.3.0/src"
20-
)
33+
./include
34+
${BOOST_INCLUDE_DIR}
35+
${GLOG_INCLUDE_DIR}
36+
${PROTOBUF_INCLUDE_DIR}
37+
${PICOJSON_INCLUDE_DIR}
38+
${MESOS_BUILD_DIR}/3rdparty/stout/include
39+
${MESOS_BUILD_DIR}/3rdparty/libprocess/include
40+
)
2141

2242
pkg_check_modules(MESOS QUIET mesos)
2343

@@ -26,28 +46,29 @@ if (NOT MESOS_FOUND) # Assume build tree has not been installed yet
2646
set(MESOS_INCLUDE_DIRS ${MESOS_BUILD_DIR}/include)
2747
set(MESOS_LIBRARY_DIRS ${MESOS_BUILD_DIR}/src/.libs)
2848
set(MESOS_LIBRARIES mesos)
29-
include_directories(
30-
"${MESOS_BUILD_DIR}/3rdparty/libprocess/include"
31-
"${MESOS_BUILD_DIR}/3rdparty/picojson-1.3.0"
32-
"${MESOS_BUILD_DIR}/3rdparty/stout/include"
33-
)
3449
endif()
3550

36-
set(SOURCES
37-
RubyEngine.cpp
38-
RubyEngine.hpp
39-
RubyHook.cpp
40-
RubyHook.hpp
51+
set(MODULES_SOURCES
52+
src/RubyEngine.cpp
53+
src/RubyHook.cpp
54+
src/RubyIsolator.cpp
4155
)
4256

4357
add_compile_options(${MESOS_CFLAGS} ${RUBY_CFLAGS})
4458
include_directories(${MESOS_INCLUDE_DIRS} ${RUBY_INCLUDE_DIRS})
4559
link_directories(${MESOS_LIBRARY_DIRS} ${RUBY_LIBRARY_DIRS})
4660
link_libraries(${MESOS_LIBRARIES} ${RUBY_LIBRARIES})
4761

48-
add_library(rubyhook SHARED ${SOURCES})
62+
add_library(${PROJECT_NAME} SHARED ${MODULES_SOURCES})
63+
64+
# Unit Tests building & execution
65+
66+
add_executable(test_rmodules tests/main.cpp)
67+
68+
file(GLOB GTEST_SRCH_PATH "${MESOS_BUILD_DIR}/3rdparty/googletest-*/googletest/include")
69+
find_path(GTEST_INCLUDE_DIR NAMES "gtest/gtest.h" PATHS ${GTEST_SRCH_PATH} NO_DEFAULT_PATH)
70+
71+
target_include_directories(test_rmodules PUBLIC ${GTEST_INCLUDE_DIR} tests)
72+
target_link_libraries(test_rmodules ${PROJECT_NAME} ${MESOS_BUILD_DIR}/3rdparty/.libs/libgmock.a)
73+
add_custom_target(check COMMAND test_rmodules ${CMAKE_SOURCE_DIR}/tests/mesos_modules.rb)
4974

50-
add_executable(test_hook main.cpp)
51-
target_include_directories(test_hook PUBLIC ${MESOS_BUILD_DIR}/3rdparty/googletest-release-1.8.0/googletest/include)
52-
target_link_libraries(test_hook rubyhook ${MESOS_BUILD_DIR}/3rdparty/.libs/libgmock.a)
53-
add_custom_target(check COMMAND test_hook ${CMAKE_SOURCE_DIR}/hook.rb)

README.md

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
Mesos Modules Ruby
2-
------------------
1+
## Mesos Modules Ruby
32

43
This repository is a collection of **experimental** mesos modules to call ruby scripts implementing hooks.
54

65
This project is not battle-tested, use it at your own risk.
76

8-
Build Instructions
9-
------------------
7+
## Build Instructions
108

119
```shell
1210
$ export MESOS_BUILD_DIR=[ directory where Mesos was BUILT, e.g. ~/repos/mesos/build ]
13-
$ export PKG_CONFIG_PATH=[ probably the same directory or ${installdir}/lib/pkgconfig ]
1411
$ mkdir build
1512
$ cd build
1613
$ cmake ..
1714
$ make
18-
$ ./test_hook ../hook.rb
15+
$ ./test_rmodules ../tests/mesos_modules.rb
1916
```
2017

21-
Scripting Documentation
22-
-----------------------
18+
## Scripting Documentation
19+
20+
Example available in ./tests/mesos_modules.rb
21+
22+
### Ruby based Mesos Hook
2323

2424
RubyHook is currently expecting the following API from the attached Ruby script:
2525

@@ -38,4 +38,36 @@ RubyHook is currently expecting the following API from the attached Ruby script:
3838

3939
Both `taskinfo` and `execinfo` are hashes filled from equivalent C++ classes.
4040
For instance, you can get the name of a task with `taskinfo["name"]` and access
41-
the labels kv-pairs through `taskinfo["labels"]` as a string-string hash.
41+
the labels kv-pairs through `taskinfo["labels"]` as a string-string hash.
42+
43+
### Ruby based Mesos Isolator
44+
45+
Current implemenation supports the prepare and cleanup callback of the mesos isolator
46+
For details on the Isolator interface, please check directly the source code:
47+
https://github.com/apache/mesos/blob/master/include/mesos/slave/isolator.hpp
48+
49+
```ruby
50+
def isolator_prepare params
51+
# Params contains the container_id, user,
52+
# and environment (pre-launch w/o for example the actual mesos-slave inject environment!)
53+
54+
return {"pre_exec_commands" => [{"value" => "touch /tmp/rb_isolator"}]} # pre exec commands
55+
end
56+
57+
def isolator_cleanup params
58+
# params: hash containing a container_id
59+
end
60+
```
61+
62+
## Deployement & Configuration
63+
64+
This library supports a single source Ruby script, for both supported module interfaces: hook and isolation.
65+
The hook and isolation modules both supports the `script_path` parameter, and shall be pointing to the same script.
66+
The library will, on purpose, only take into account the first `script_path` processed.
67+
68+
This is done on purpose to make it explicit that all modules (hook and isolator) will run in the same
69+
ruby context (single vm/process).
70+
71+
Example of json configuration file in examples sub-folder.
72+
Don't forget to add `com_criteo_mesos_RubyIsolator` to the list of slave's activated isolators
73+
(--isolation="...,com_criteo_mesos_RubyIsolator")

RubyEngine.cpp

Lines changed: 0 additions & 37 deletions
This file was deleted.

RubyEngine.hpp

Lines changed: 0 additions & 22 deletions
This file was deleted.

hook.rb

Lines changed: 0 additions & 18 deletions
This file was deleted.

include/RubyEngine.hpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#ifndef __RUBYENGINE_HPP__
2+
#define __RUBYENGINE_HPP__
3+
4+
#include <ruby.h>
5+
#include <string>
6+
#include <mutex>
7+
#include <mesos/mesos.hpp>
8+
9+
const std::string PARAM_SCRIPT_PATH = "script_path";
10+
11+
namespace criteo {
12+
namespace mesos {
13+
14+
namespace helpers{
15+
void hash_set(VALUE hash, const std::string& key, const std::string& value);
16+
void hash_set(VALUE hash, const std::string& key, VALUE value);
17+
bool is_non_empty_hash(VALUE hash);
18+
bool is_non_empty_array(VALUE array);
19+
}
20+
21+
// Manages basic initialization of Ruby's engine
22+
class RubyEngine
23+
{
24+
int state = 0;
25+
26+
public:
27+
RubyEngine(const std::string& name);
28+
~RubyEngine();
29+
30+
bool load_script(const std::string& scriptname);
31+
bool is_callback_defined(const std::string& symbol) const;
32+
33+
std::string handle_exception();
34+
};
35+
36+
// This class is a singleton,
37+
// to avoid multiple initializations
38+
// from multiple modules.
39+
//
40+
// Goal is too enforce a single common ruby source script
41+
// for all support modules declared here.
42+
// TODO: Support Ruby based declaration of the modules
43+
//
44+
// /!\ First set of mesos module paramater wins!
45+
//
46+
class RubyEngineSingleton : public RubyEngine
47+
{
48+
bool started = false;
49+
std::mutex m_mutex;
50+
51+
public:
52+
static RubyEngineSingleton& getInstance();
53+
void start(const ::mesos::Parameters& parameters);
54+
std::mutex* mutex();
55+
56+
private:
57+
RubyEngineSingleton();
58+
59+
public:
60+
// remove capability to copy (by mistake) the object
61+
RubyEngineSingleton(RubyEngineSingleton const&) = delete;
62+
void operator=(RubyEngineSingleton const&) = delete;
63+
};
64+
65+
} // criteo.mesos
66+
}
67+
#endif // __RUBYENGINE_HPP__

RubyHook.hpp renamed to include/RubyHook.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@
1010

1111
class RubyHook : public mesos::Hook
1212
{
13-
std::mutex mutex;
14-
RubyEngine ruby;
15-
1613
public:
1714
RubyHook(const mesos::Parameters& parameters);
1815

include/RubyIsolator.hpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#ifndef __RUBY_ISOLATOR_MODULE_HPP__
2+
#define __RUBY_ISOLATOR_MODULE_HPP__
3+
4+
#include "RubyEngine.hpp"
5+
6+
#include <ruby.h>
7+
#include <mesos/mesos.hpp>
8+
#include <mesos/slave/isolator.hpp>
9+
#include <mesos/module/isolator.hpp>
10+
11+
using ::mesos::slave::ContainerLaunchInfo;
12+
13+
namespace criteo {
14+
namespace mesos {
15+
16+
constexpr const char * PrepareCallbackName = "isolator_prepare";
17+
constexpr const char * CleanupCallbackName = "isolator_cleanup";
18+
19+
class RubyIsolator : public ::mesos::slave::Isolator
20+
{
21+
public:
22+
23+
RubyIsolator(const ::mesos::Parameters& parameters);
24+
25+
virtual process::Future<Option<ContainerLaunchInfo>> prepare(
26+
const ::mesos::ContainerID& containerId,
27+
const ::mesos::slave::ContainerConfig& containerConfig) override;
28+
29+
virtual process::Future<Nothing> cleanup(
30+
const ::mesos::ContainerID& containerId) override;
31+
32+
private:
33+
34+
bool rb2ContainerLaunchInfo(ContainerLaunchInfo& cli, VALUE ruby_value) const;
35+
36+
};
37+
}
38+
}
39+
40+
// Callback used by registration below...
41+
static mesos::slave::Isolator* createRubyIsolator(const ::mesos::Parameters& parameters)
42+
{
43+
try {
44+
return new criteo::mesos::RubyIsolator(parameters);
45+
} catch (const std::exception& e) {
46+
LOG(ERROR) << "RubyHook error: " << e.what();
47+
return nullptr; // already wrapped in a Try<> at calling site
48+
}
49+
}
50+
51+
// Declaration of the isolator
52+
mesos::modules::Module<mesos::slave::Isolator> com_criteo_mesos_RubyIsolator(
53+
MESOS_MODULE_API_VERSION,
54+
MESOS_VERSION,
55+
"Criteo Mesos",
56+
57+
"Ruby isolator module",
58+
nullptr,
59+
createRubyIsolator);
60+
61+
#endif //__RUBY_ISOLATOR_MODULE_HPP__

0 commit comments

Comments
 (0)