From: Waldemar Kozaczuk <jwkozac...@gmail.com>
Committer: Nadav Har'El <n...@scylladb.com>
Branch: master
Modularized httpserver REST API
Broke down OSv REST API provides by httpserver-api into number of
fine-grained plugins
that implement specific subset of API and thus makes it possible to compose
REST API
in "a la carte" fashion.
Deprecated file_mappings.cc that hard-coded serving specific static content
(swagger-ui,osv-gui)
in favor of more generic logic in global_server.cc that allows to configure
redirects and file/directory
mappings rules via /tmp/httpserver.conf file.
Please see JSON example below that demonstrates two sections - redirects
and file_mappings -
but YAML will obviously work as well.
{
"ipaddress": "0.0.0.0",
"port": "8000",
"redirects": [
{
"path": "/",
"target_path": "/dashboard/"
}
],
"file_mappings": [
{
"path": "/api-gui",
"file": "/usr/mgmt/swagger-ui/dist/index.html",
"exact_match": true // path should be matched exactly otherwise treat
path like a rule
},
{
"path": "/api",
"directory": "/usr/mgmt/api",
"content_replace": "json"
},
{
"path": "/dashboard_static",
"directory": "/usr/mgmt/gui/dashboard_static"
},
{
"path": "/dashboard",
"file": "/usr/mgmt/gui/dashboard/index.html"
},
{
"path": "/api-gui",
"directory": "/usr/mgmt/swagger-ui/dist"
}
]
}
Created default httpserver.conf that configures httpserver to serve same
static content same way for backwards compatibility.
Added integration test to httpserver module to verify it serves static
content
per configuration in supplied /tmp/httpserver.conf.
Fixes #885
Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>
Message-Id: <1503345255-22147-1-git-send-email-jwkozac...@gmail.com>
---
diff --git a/modules/httpserver-api/.gitignore
b/modules/httpserver-api/.gitignore
--- a/modules/httpserver-api/.gitignore
+++ b/modules/httpserver-api/.gitignore
@@ -1,3 +1,4 @@
-libhttpserver.so
+*.so
autogen/*
obj/*
+*.manifest
diff --git a/modules/httpserver-api/Makefile
b/modules/httpserver-api/Makefile
--- a/modules/httpserver-api/Makefile
+++ b/modules/httpserver-api/Makefile
@@ -24,10 +24,10 @@ libs-dir = $(miscbase)/usr/lib64
boost-libs := -lboost_system -lboost_filesystem -lboost_regex
# the build target executable:
-TARGET = httpserver
+TARGET = httpserver-api
JSON_FILES := $(wildcard api-doc/listings/*.json)
JSON_CC_FILES := $(subst .json,.json.cc,$(subst
api-doc/listings/,autogen/,$(JSON_FILES)))
-CPP_FILES := $(JSON_CC_FILES) $(wildcard *.cc) $(wildcard json/*.cc)
$(wildcard api/*.cc)
+CPP_FILES := $(JSON_CC_FILES) $(wildcard *.cc) $(wildcard json/*.cc)
STUB_CPP_FILES := $(wildcard stub/*.cc)
OBJ_FILES := $(addprefix obj/,$(CPP_FILES:.cc=.o))
STUB_FILES := $(addprefix obj/,$(STUB_CPP_FILES:.cc=.o))
@@ -50,16 +50,18 @@ DEPS := $(OBJ_FILES:.o=.d)
module: all
-all: init lib$(TARGET).so
+all: lib$(TARGET).so api_api api_app api_env api_file api_fs api_hardware
api_network api_os api_trace
+ $(call quiet, cat _usr_*.manifest | sort | uniq > usr.manifest,
CREATE_MANIFEST)
-init:
- $(call very-quiet, mkdir -p obj)
- $(call very-quiet, mkdir -p obj/stub)
- $(call very-quiet, mkdir -p obj/json)
- $(call very-quiet, mkdir -p obj/api)
- $(call very-quiet, mkdir -p obj/autogen)
- $(call very-quiet, mkdir -p autogen)
-.PHONY: init
+add_api_to_manifest = \
+ echo "/usr/mgmt/plugins/libhttpserver-$(1).so: `realpath
libhttpserver-$(1).so`" > _usr_$(1).manifest
+
+add_boost_dependencies_to_manifest = \
+ ldd libhttpserver-$(1).so | grep boost | sed 's/ *[^ ] *\(.*\) =>
\(.*\) .*/\/usr\/lib\/\1: \2/' > _usr_$(1)_boost.manifest
+
+api_%: libhttpserver-api_%.so
+ $(call quiet, $(call add_api_to_manifest,$@), APPEND_TO_MANIFEST $@)
+ $(call very-quiet, $(call add_boost_dependencies_to_manifest,$@))
stub-lib: $(STUB_FILES)
$(call quiet, $(CXX) $(CXXFLAGS) -shared -o $(TARGET)-stub.so $^, LINK
$@)
@@ -69,37 +71,49 @@ $(TARGET): $(OBJ_FILES) $(STUB_FILES)
lib$(TARGET).so: $(OBJ_FILES)
$(call quiet, $(CXX) $(CXXFLAGS) -shared $(STATIC_LIBS) -o $@ $^
$(DYN_LIBS), LINK $@)
- ldd $@ | grep boost | sed 's/ *[^ ] *\(.*\) => \(.*\) .*/\/\1: \2/' >
usr.manifest
+ $(call very-quiet, ldd $@ | grep boost | sed 's/ *[^ ] *\(.*\) =>
\(.*\) .*/\usr\/lib\/\1: \2/' > _usr_$(TARGET).manifest)
+
+libhttpserver-api_%.so: obj/autogen/%.json.o obj/api/%.o
+ $(call quiet, $(CXX) $(CXXFLAGS) -shared $(STATIC_LIBS) -o $@ $^
$(DYN_LIBS), LINK $@)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
autogen/%.cc: api-doc/listings/% json2code.py
+ $(call very-quiet, mkdir -p autogen)
$(call quiet,./json2code.py -f $< -ns json, GEN $@)
+init_obj = \
+ $(call very-quiet, mkdir -p obj/stub) \
+ $(call very-quiet, mkdir -p obj/json) \
+ $(call very-quiet, mkdir -p obj/api) \
+ $(call very-quiet, mkdir -p obj/autogen)
+
obj/%.o: %.cc
+ $(call init_obj)
$(call quiet, $(CXX) $(CXXFLAGS) -c -MMD -o $@ $<, CXX $@)
clean:
$(call quiet, $(RM) $(TARGET), CLEAN)
- $(call very-quiet, $(RM) lib$(TARGET).so)
- $(call very-quiet, $(RM) -rf obj/*)
- $(call very-quiet, $(RM) -rf autogen/*)
+ $(call very-quiet, $(RM) lib*.so)
+ $(call very-quiet, $(RM) -rf obj)
+ $(call very-quiet, $(RM) -rf autogen)
+ $(call very-quiet, $(RM) -f *usr*.manifest)
check: check-http check-ssl
check-http:
# Test plain HTTP
cd $(src) && \
make image=httpserver-api,jetty && \
- PYTHONPATH=$(src)/scripts modules/httpserver-api/tests/testhttpserver.py
+ PYTHONPATH=$(src)/scripts
modules/httpserver-api/tests/testhttpserver-api.py
check-ssl:
# Test SSL
cd $(src) && \
make image=httpserver-api.fg_ssl,certs,jetty && \
- PYTHONPATH=$(src)/scripts
modules/httpserver-api/tests/testhttpserver.py \
+ PYTHONPATH=$(src)/scripts
modules/httpserver-api/tests/testhttpserver-api.py \
--cert modules/certs/build/client.pem \
--key modules/certs/build/client.key \
--cacert modules/certs/build/cacert.pem
diff --git a/modules/httpserver-api/api/api.cc
b/modules/httpserver-api/api/api.cc
--- a/modules/httpserver-api/api/api.cc
+++ b/modules/httpserver-api/api/api.cc
@@ -240,6 +240,10 @@ class api_param_handler : public handler_base {
routes& _routes;
};
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::api::init(*routes);
+}
+
void init(routes& routes)
{
api_json_init_path("Advanced API options");
diff --git a/modules/httpserver-api/api/app.cc
b/modules/httpserver-api/api/app.cc
--- a/modules/httpserver-api/api/app.cc
+++ b/modules/httpserver-api/api/app.cc
@@ -49,6 +49,10 @@ static std::string exec_app(const std::string&
cmnd_line, bool new_program) {
return app_ids;
}
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::app::init(*routes);
+}
+
void init(routes& routes)
{
app_json_init_path("app API");
diff --git a/modules/httpserver-api/api/env.cc
b/modules/httpserver-api/api/env.cc
--- a/modules/httpserver-api/api/env.cc
+++ b/modules/httpserver-api/api/env.cc
@@ -19,6 +19,10 @@ using namespace json;
using namespace std;
using namespace env_json;
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::env::init(*routes);
+}
+
void init(routes& routes)
{
env_json_init_path("Environment variables API");
diff --git a/modules/httpserver-api/api/file.cc
b/modules/httpserver-api/api/file.cc
--- a/modules/httpserver-api/api/file.cc
+++ b/modules/httpserver-api/api/file.cc
@@ -443,6 +443,10 @@ class put_file_handler : public handler_base {
}
};
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::file::init(*routes);
+}
+
void init(routes& routes)
{
file_json_init_path("file API");
diff --git a/modules/httpserver-api/api/files_mapping.cc
b/modules/httpserver-api/api/files_mapping.cc
--- a/modules/httpserver-api/api/files_mapping.cc
+++ b/modules/httpserver-api/api/files_mapping.cc
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2014 Cloudius Systems, Ltd.
- *
- * This work is open source software, licensed under the terms of the
- * BSD license as described in the LICENSE file in the top-level directory.
- */
-
-#include "files_mapping.hh"
-#include "routes.hh"
-#include "transformers.hh"
-namespace httpserver {
-
-namespace api {
-
-namespace files_mapping {
-
-void init(routes& routes)
-{
- function_handler* redirect =
- new function_handler(
- [](const_req req) {
- throw redirect_exception(req.get_protocol_name() + "://" +
- req.get_header("Host") + "/dashboard/");
- // The return is required so the lambda expression would have
- // the right signature
- return "";
- });
- routes.put(GET, "/", redirect);
- file_handler* index = new file_handler(
- "/usr/mgmt/swagger-ui/dist/index.html", nullptr, true);
- routes.put(GET, "/api-gui", index);
-
- directory_handler* api = new directory_handler("/usr/mgmt/api",
- new content_replace("json"));
- routes.add(GET, url("/api").remainder("path"), api);
- routes.add(GET, url("/dashboard_static").remainder("path"),
- new directory_handler("/usr/mgmt/gui/dashboard_static"));
- routes.add(GET, url("/dashboard").remainder("path"),
- new file_handler("/usr/mgmt/gui/dashboard/index.html"));
- routes.add(GET, url("/api-gui").remainder("path"),
- new directory_handler("/usr/mgmt/swagger-ui/dist"));
-}
-
-}
-}
-}
diff --git a/modules/httpserver-api/api/files_mapping.hh
b/modules/httpserver-api/api/files_mapping.hh
--- a/modules/httpserver-api/api/files_mapping.hh
+++ b/modules/httpserver-api/api/files_mapping.hh
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2014 Cloudius Systems, Ltd.
- *
- * This work is open source software, licensed under the terms of the
- * BSD license as described in the LICENSE file in the top-level directory.
- */
-
-#ifndef FILES_MAPPING_HH_
-#define FILES_MAPPING_HH_
-
-#include "routes.hh"
-
-namespace httpserver {
-
-namespace api {
-
-namespace files_mapping {
-
-/**
- * Initialize the routes object with specific routes mapping
- * @param routes - the routes object to fill
- */
-void init(routes& routes);
-
-}
-}
-}
-
-#endif /* FILES_MAPPING_HH_ */
diff --git a/modules/httpserver-api/api/fs.cc
b/modules/httpserver-api/api/fs.cc
--- a/modules/httpserver-api/api/fs.cc
+++ b/modules/httpserver-api/api/fs.cc
@@ -32,6 +32,10 @@ static void fill_dfstat(DFStat& dfstat, const
osv::mount_desc& mount, const stru
dfstat.ffree = st.f_ffree;
}
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::fs::init(*routes);
+}
+
void init(routes& routes) {
fs_json_init_path("FS core API");
diff --git a/modules/httpserver-api/api/hardware.cc
b/modules/httpserver-api/api/hardware.cc
--- a/modules/httpserver-api/api/hardware.cc
+++ b/modules/httpserver-api/api/hardware.cc
@@ -24,6 +24,10 @@ using namespace std;
using namespace json;
using namespace hardware_json;
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::hardware::init(*routes);
+}
+
void init(routes& routes)
{
hardware_json_init_path("Hardware management API");
diff --git a/modules/httpserver-api/api/network.cc
b/modules/httpserver-api/api/network.cc
--- a/modules/httpserver-api/api/network.cc
+++ b/modules/httpserver-api/api/network.cc
@@ -43,6 +43,10 @@ static Interface get_interface(const string& name,
ifnet* ifp, long time)
return f;
}
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::network::init(*routes);
+}
+
/**
* Initialize the routes object with specific routes mapping
* @param routes - the routes object to fill
diff --git a/modules/httpserver-api/api/os.cc
b/modules/httpserver-api/api/os.cc
--- a/modules/httpserver-api/api/os.cc
+++ b/modules/httpserver-api/api/os.cc
@@ -33,6 +33,10 @@ using namespace std;
using namespace json;
using namespace os_json;
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::os::init(*routes);
+}
+
void init(routes& routes)
{
os_json_init_path("OS core API");
diff --git a/modules/httpserver-api/api/trace.cc
b/modules/httpserver-api/api/trace.cc
--- a/modules/httpserver-api/api/trace.cc
+++ b/modules/httpserver-api/api/trace.cc
@@ -25,6 +25,10 @@ using namespace httpserver::json::trace_json;
static std::unordered_map<tracepoint_base*,
std::unique_ptr<tracepoint_counter>> counters;
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::trace::init(*routes);
+}
+
/**
* Initialize the routes object with specific routes mapping
* @param routes - the routes object to fill
diff --git a/modules/httpserver-api/global_server.cc
b/modules/httpserver-api/global_server.cc
--- a/modules/httpserver-api/global_server.cc
+++ b/modules/httpserver-api/global_server.cc
@@ -6,17 +6,7 @@
*/
#include "global_server.hh"
-#include "api/os.hh"
-#include "api/fs.hh"
-#include "api/files_mapping.hh"
-#include "api/file.hh"
-#include "api/trace.hh"
-#include "api/env.hh"
-#include "api/hardware.hh"
-#include "api/api.hh"
#include "path_holder.hh"
-#include "api/network.hh"
-#include "api/app.hh"
#include <iostream>
#include <osv/app.hh>
#include <fstream>
@@ -25,6 +15,8 @@
#include <boost/foreach.hpp>
#include "yaml-cpp/yaml.h"
#include "json/api_docs.hh"
+#include <osv/debug.h>
+#include "transformers.hh"
namespace httpserver {
@@ -43,32 +35,43 @@ bool global_server::run(po::variables_map& _config)
if (get().s != nullptr) {
return false;
}
+ //TODO: It would be nice to add config option that specifies location
+ //of httpserver.conf. Also maybe the default location should be
/etc/httpserver.conf
+ //but it seems that cloud-init module has some dependency on it -->
look at
+ // modules/cloud-init/server-module.hh
std::ifstream f("/tmp/httpserver.conf");
if (f.is_open()) {
try {
YAML::Node doc = YAML::Load(f);
for (auto node : doc) {
- std::function<std::string(const YAML::Node&)> to_string;
-
- to_string = [&](const YAML::Node & n) -> std::string {
- std::string s;
- if (n.IsSequence()) {
- for (auto & sn : n) {
- if (!s.empty()) {
- s += ',';
+ auto key = node.first.as<std::string>();
+ if (key == "redirects" && node.second.IsSequence()) {
+ get().setup_redirects(node.second);
+ }
+ else if (key == "file_mappings" &&
node.second.IsSequence()) {
+ get().setup_file_mappings(node.second);
+ }
+ else {
+ std::function<std::string(const YAML::Node&)>
to_string;
+
+ to_string = [&](const YAML::Node & n) -> std::string {
+ std::string s;
+ if (n.IsSequence()) {
+ for (auto & sn : n) {
+ if (!s.empty()) {
+ s += ',';
+ }
+ s += to_string(sn);
}
- s += to_string(sn);
+ } else {
+ s = n.as<std::string>();
}
- } else {
- s = n.as<std::string>();
- }
- return s;
- };
+ return s;
+ };
- auto key = node.first.as<std::string>();
- auto val = to_string(node.second);
-
- get().set(key, val);
+ auto val = to_string(node.second);
+ get().set(key, val);
+ }
}
} catch (const std::exception& e) {
std::cout << "httpserver Failed reading the configuration
file " << e.what() << std::endl;
@@ -87,18 +90,69 @@ bool global_server::run(po::variables_map& _config)
osv::this_application::on_termination_request([&] {
get().s->close();
+ for( auto plugin : get().plugins) {
+ dlclose(plugin);
+ }
});
get().s->run();
return true;
}
+void global_server::setup_file_mappings(const YAML::Node&
file_mappings_node) {
+ for (auto node : file_mappings_node) {
+ const YAML::Node path = node["path"];
+ if (path && node["directory"]) {
+ const std::string directory =
node["directory"].as<std::string>();
+ const YAML::Node content_replace_node =
node["content_replace"];
+ directory_handler* handler = content_replace_node ?
+ new directory_handler(directory,
new content_replace(content_replace_node.as<std::string>())) :
+ new directory_handler(directory);
+ _routes.add(GET,
url(path.as<std::string>()).remainder("path"), handler);
+ debug("httpserver: setup directory mapping: [%s] -> [%s]\n",
path.as<std::string>().c_str(), directory.c_str());
+ }
+ else if (path && node["file"]) {
+ const std::string file = node["file"].as<std::string>();
+ const YAML::Node exact_match_node = node["exact_match"];
+ file_handler* handler = new file_handler(file, nullptr, true);
+ if (exact_match_node && exact_match_node.as<bool>()) {
+ _routes.put(GET, path.as<std::string>(), handler);
+ }
+ else {
+ _routes.add(GET,
url(path.as<std::string>()).remainder("path"), handler);
+ }
+ debug("httpserver: setup file mapping: [%s] -> [%s]\n",
path.as<std::string>().c_str(), file.c_str());
+ }
+ }
+}
+
+void global_server::setup_redirects(const YAML::Node& redirects_node) {
+ for (auto node : redirects_node) {
+ const YAML::Node path_node = node["path"];
+ const YAML::Node target_path_node = node["target_path"];
+ if (path_node && target_path_node) {
+ const std::string path = path_node.as<std::string>();
+ const std::string target_path =
target_path_node.as<std::string>();
+
+ function_handler* redirect =
+ new function_handler(
+ [target_path](const_req req) {
+ throw
redirect_exception(req.get_protocol_name() + "://" +
+
req.get_header("Host") + target_path);
+ // The return is required so the lambda
expression would have
+ // the right signature
+ return "";
+ });
+ _routes.put(GET, path, redirect);
+ debug("httpserver: setup redirect: [%s] -> [%s]\n",
path.c_str(), target_path.c_str());
+ }
+ }
+}
global_server::global_server()
: s(nullptr)
{
set_routes();
-
}
global_server& global_server::set(po::variables_map& _config)
@@ -116,47 +170,48 @@ global_server& global_server::set(const std::string&
key,
boost::program_options::variable_value v(val, false);
config.insert(std::make_pair(std::string(key), v));
return *this;
-
}
void global_server::set_routes()
{
-
path_holder::set_routes(&_routes);
json::api_doc_init(_routes);
- api::network::init(_routes);
- api::os::init(_routes);
- api::fs::init(_routes);
- api::file::init(_routes);
- api::trace::init(_routes);
- api::env::init(_routes);
- api::api::init(_routes);
- api::app::init(_routes);
- api::files_mapping::init(_routes);
- api::hardware::init(_routes);
{
namespace fs = boost::filesystem;
- using init_func_t = void(void*);
- void* plugin;
- init_func_t* plugin_init;
fs::path plugin_path("/usr/mgmt/plugins/");
if (!fs::exists(plugin_path) && !fs::is_directory(plugin_path))
return;
- BOOST_FOREACH(const fs::path& p,
std::make_pair(fs::directory_iterator(plugin_path),
+ BOOST_FOREACH(const fs::path& path,
std::make_pair(fs::directory_iterator(plugin_path),
fs::directory_iterator())) {
- if (fs::extension(p)==".so") {
- plugin = dlopen(p.c_str(), RTLD_LAZY);
- if ( plugin == nullptr ) {
- continue;
- }
- plugin_init =
reinterpret_cast<init_func_t*>(dlsym(plugin, "init"));
- if ( plugin_init == nullptr ) {
- continue;
- }
- plugin_init(reinterpret_cast<void*>(&_routes));
+ if (fs::extension(path)==".so") {
+ load_plugin(path.string());
}
}
}
}
+void global_server::load_plugin(const std::string& path)
+{
+ void* plugin = dlopen(path.c_str(), RTLD_LAZY);
+ if ( plugin == nullptr ) {
+ return;
+ }
+ //
+ // The httpserver plugin needs to export proper initialization
function intended to register
+ // any new routes. The function should be
named "httpserver_plugin_register_routes" and
+ // defined to follow signature like so:
+ // extern "C" void
httpserver_plugin_register_routes(httpserver::routes* routes) {
+ // ....
+ // }
+ // This is necessary to avoid C++ symbol mangling so that dlsym() can
find the symbol
+ using init_func_t = void(routes*);
+ init_func_t* httpserver_plugin_register_routes =
reinterpret_cast<init_func_t*>(dlsym(plugin, "httpserver_plugin_register_routes"));
+ if ( httpserver_plugin_register_routes == nullptr ) {
+ dlclose(plugin);
+ return;
+ }
+ plugins.push_back(plugin);
+ httpserver_plugin_register_routes(&_routes);
+ debug("httpserver: loaded plugin from path: %s\n",path.c_str());
+}
}
diff --git a/modules/httpserver-api/global_server.hh
b/modules/httpserver-api/global_server.hh
--- a/modules/httpserver-api/global_server.hh
+++ b/modules/httpserver-api/global_server.hh
@@ -9,9 +9,11 @@
#define GLOBAL_SERVER_HH_
#include "routes.hh"
#include "server.hh"
+#include <vector>
#include <boost/program_options/variables_map.hpp>
#include <mutex>
#include <condition_variable>
+#include <external/x64/misc.bin/usr/include/yaml-cpp/node/iterator.h>
namespace po = boost::program_options;
@@ -65,10 +67,14 @@ private:
global_server();
void set_routes();
+ void setup_redirects(const YAML::Node& node);
+ void setup_file_mappings(const YAML::Node& node);
+ void load_plugin(const std::string& path);
static global_server* instance;
routes _routes;
http::server::server* s;
po::variables_map config;
+ std::vector<void*> plugins;
/**
* set configuration based on command line.
diff --git a/modules/httpserver-api/module.py
b/modules/httpserver-api/module.py
--- a/modules/httpserver-api/module.py
+++ b/modules/httpserver-api/module.py
@@ -8,7 +8,7 @@
_exe = '/libhttpserver.so'
usr_files = FileMap()
-usr_files.add(os.path.join(_module, 'libhttpserver.so')).to(_exe)
+usr_files.add(os.path.join(_module, 'libhttpserver-api.so')).to(_exe)
usr_files.add(os.path.join(_module, 'api-doc')).to('/usr/mgmt/api')
api.require('openssl')
diff --git a/modules/httpserver-api/tests/basetest.py
b/modules/httpserver-api/tests/basetest.py
--- a/modules/httpserver-api/tests/basetest.py
+++ b/modules/httpserver-api/tests/basetest.py
@@ -66,7 +66,10 @@ def is_reachable(cls):
try:
s.connect((cls._client.get_host(), cls._client.get_port()))
s.close()
- return cls.is_jvm_up()
+ if cls.config.check_jvm:
+ return cls.is_jvm_up()
+ else:
+ return True
except socket.error:
return False
@@ -149,8 +152,10 @@ def shutdown(cls):
@classmethod
def start_image(cls):
- jvm_plugin_api_listings_path =
os.path.join(os.path.realpath(os.path.dirname(__file__)),'../../httpserver-jvm-plugin/api-doc/listings')
- cls.jvm_api =
cls.get_json_api_from_directory(jvm_plugin_api_listings_path,"jvm.json")
+ if cls.config.check_jvm:
+ jvm_plugin_api_listings_path = \
+
os.path.join(os.path.realpath(os.path.dirname(__file__)),'../../httpserver-jvm-plugin/api-doc/listings')
+ cls.jvm_api =
cls.get_json_api_from_directory(jvm_plugin_api_listings_path,"jvm.json")
cls.os_api = cls.get_json_api("os.json")
if not cls.config.connect:
cls.os_process = cls.exec_os()
diff --git a/modules/httpserver-api/tests/testhttpserver-api.py
b/modules/httpserver-api/tests/testhttpserver-api.py
--- a/modules/httpserver-api/tests/testhttpserver-api.py
+++ b/modules/httpserver-api/tests/testhttpserver-api.py
@@ -30,6 +30,7 @@ def tearDownClass(cls):
if __name__ == '__main__':
basetest.Basetest.set_config(parser)
+ basetest.Basetest.config.check_jvm = True
basetest.Basetest.start_image()
del sys.argv[1:]
api_tests =
unittest.TestLoader().discover(os.path.join(module_base, 'tests', 'api'),
pattern='*.py')
diff --git a/modules/httpserver-html5-gui/Makefile
b/modules/httpserver-html5-gui/Makefile
--- a/modules/httpserver-html5-gui/Makefile
+++ b/modules/httpserver-html5-gui/Makefile
@@ -19,4 +19,12 @@ gui: osv-gui/.git
osv-gui/.git:
cd $(src) && git submodule update --init -f
modules/httpserver-html5-gui/osv-gui
+check:
+ # Test if HTTP servers serves properly static content
+ cd $(src) && \
+ scripts/build image=httpserver-html5-gui.fg && \
+ PYTHONPATH=$(src)/scripts:$(src)/modules/httpserver-api/tests
modules/httpserver-html5-gui/tests/testhttpserver.py
+
+.PHONY: check
+
.SECONDARY:
diff --git a/modules/httpserver-html5-gui/httpserver.conf
b/modules/httpserver-html5-gui/httpserver.conf
--- a/modules/httpserver-html5-gui/httpserver.conf
+++ b/modules/httpserver-html5-gui/httpserver.conf
@@ -0,0 +1,34 @@
+{
+ "ipaddress": "0.0.0.0",
+ "port": "8000",
+ "redirects": [
+ {
+ "path": "/",
+ "target_path": "/dashboard/"
+ }
+ ],
+ "file_mappings": [
+ {
+ "path": "/api-gui",
+ "file": "/usr/mgmt/swagger-ui/dist/index.html",
+ "exact_match": true
+ },
+ {
+ "path": "/api",
+ "directory": "/usr/mgmt/api",
+ "content_replace": "json"
+ },
+ {
+ "path": "/dashboard_static",
+ "directory": "/usr/mgmt/gui/dashboard_static"
+ },
+ {
+ "path": "/dashboard",
+ "file": "/usr/mgmt/gui/dashboard/index.html"
+ },
+ {
+ "path": "/api-gui",
+ "directory": "/usr/mgmt/swagger-ui/dist"
+ }
+ ]
+}
diff --git a/modules/httpserver-html5-gui/module.py
b/modules/httpserver-html5-gui/module.py
--- a/modules/httpserver-html5-gui/module.py
+++ b/modules/httpserver-html5-gui/module.py
@@ -8,6 +8,7 @@
usr_files = FileMap()
usr_files.add(os.path.join(_module, 'swagger-ui', 'dist')).to('/usr/mgmt/swagger-ui/dist')
usr_files.add(os.path.join(_module, 'osv-gui/public')).to('/usr/mgmt/gui')
+usr_files.add(os.path.join(_module,
'httpserver.conf')).to('/tmp/httpserver.conf')
api.require('httpserver-api')
@@ -21,4 +22,4 @@
fg_ssl = api.run(_exe + ' --ssl')
fg_cors = api.run(_exe + ' --access-allow=true')
-default = daemon
+default = daemon
\ No newline at end of file
diff --git a/modules/httpserver-html5-gui/tests/static/teststatic.py
b/modules/httpserver-html5-gui/tests/static/teststatic.py
--- a/modules/httpserver-html5-gui/tests/static/teststatic.py
+++ b/modules/httpserver-html5-gui/tests/static/teststatic.py
@@ -0,0 +1,31 @@
+import requests
+import basetest
+
+class teststatic(basetest.Basetest):
+ def testredirect(self):
+ r = self.get("/")
+ self.assertEqual(r.status_code, 301)
+ self.assertEqual(r.url, self.get_url("/"))
+
+ def testapiguifile(self):
+ r = self.get("/api-gui/")
+ self.assertEqual(r.status_code, 200)
+
+ def testapi(self):
+ r = self.get("/api/listings/api.json")
+ self.assertEqual(r.status_code, 200)
+
+ def testdashboard_static(self):
+ r = self.get("/dashboard_static/smoothie.js")
+ self.assertEqual(r.status_code, 200)
+
+ def testdashboard(self):
+ r = self.get("/dashboard/")
+ self.assertEqual(r.status_code, 200)
+
+ def testapigui(self):
+ r = self.get("/api-gui/css/screen.css")
+ self.assertEqual(r.status_code, 200)
+
+ def get(self,path):
+ return requests.get(self.get_url(path), allow_redirects=False)
diff --git a/modules/httpserver-html5-gui/tests/testhttpserver.py
b/modules/httpserver-html5-gui/tests/testhttpserver.py
--- a/modules/httpserver-html5-gui/tests/testhttpserver.py
+++ b/modules/httpserver-html5-gui/tests/testhttpserver.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+import sys
+import argparse
+import os
+import unittest
+import basetest
+
+from osv import client
+
+parser = argparse.ArgumentParser(description="""Testing the Httpserver""")
+
+module_base =
os.path.join(os.path.realpath(os.path.dirname(__file__)), '..')
+osv_base = os.path.join(module_base, '..', '..')
+
+parser.add_argument('--connect', help='Connect to an existing image',
action='store_true')
+parser.add_argument('--run_script', help='path to the run image script',
default=os.path.join(osv_base, 'scripts', 'run.py'))
+parser.add_argument('--cmd', help='the command to execute')
+parser.add_argument('--use_sudo', help='Use sudo with -n option instead of
port forwarding', action='store_true')
+parser.add_argument('--jsondir', help='location of the json files',
default=os.path.join(module_base, '../httpserver-api/api-doc/listings/'))
+client.Client.add_arguments(parser)
+
+if __name__ == '__main__':
+ basetest.Basetest.set_config(parser)
+ basetest.Basetest.config.check_jvm = False
+ basetest.Basetest.start_image()
+ del sys.argv[1:]
+ tests =
unittest.TestLoader().discover(os.path.join(module_base, 'tests', 'static'),
pattern='*.py')
+ test_suite = unittest.TestSuite(tests)
+ unittest.TextTestRunner(verbosity=2).run(test_suite)
+ basetest.Basetest.shutdown()
diff --git a/modules/httpserver-jolokia-plugin/jolokia.cc
b/modules/httpserver-jolokia-plugin/jolokia.cc
--- a/modules/httpserver-jolokia-plugin/jolokia.cc
+++ b/modules/httpserver-jolokia-plugin/jolokia.cc
@@ -26,9 +26,8 @@ static void verify_jvm() {
throw httpserver::not_found_exception("JVM not running");
}
}
-extern "C" void init(void* arg) {
- auto r = reinterpret_cast<httpserver::routes*>(arg);
- httpserver::api::jolokia::init(*r);
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::jolokia::init(*routes);
}
/**
* Initialize the routes object with specific routes mapping
diff --git a/modules/httpserver-jvm-plugin/jvm.cc
b/modules/httpserver-jvm-plugin/jvm.cc
--- a/modules/httpserver-jvm-plugin/jvm.cc
+++ b/modules/httpserver-jvm-plugin/jvm.cc
@@ -61,9 +61,8 @@ class set_jmx_handler : public handler_base {
}
};
-extern "C" void init(void* arg) {
- auto r = reinterpret_cast<httpserver::routes*>(arg);
- httpserver::api::jvm::init(*r);
+extern "C" void httpserver_plugin_register_routes(httpserver::routes*
routes) {
+ httpserver::api::jvm::init(*routes);
}
/**
* Initialize the routes object with specific routes mapping
--
You received this message because you are subscribed to the Google Groups "OSv
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.