-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[OCONF-782] Add Apricot backend (#77)
- Loading branch information
Showing
6 changed files
with
289 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
// Copyright 2019-2020 CERN and copyright holders of ALICE O2. | ||
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. | ||
// All rights not expressly granted are reserved. | ||
// | ||
// This software is distributed under the terms of the GNU General Public | ||
// License v3 (GPL Version 3), copied verbatim in the file "COPYING". | ||
// | ||
// In applying this license CERN does not waive the privileges and immunities | ||
// granted to it by virtue of its status as an Intergovernmental Organization | ||
// or submit itself to any jurisdiction. | ||
|
||
/// \file ApricotBackend.h | ||
/// \brief Configuration interface to the Apricot key-value store | ||
/// | ||
/// \author Pascal Boeschoten, CERN | ||
|
||
#include "ApricotBackend.h" | ||
#include <boost/property_tree/json_parser.hpp> | ||
|
||
namespace o2 | ||
{ | ||
namespace configuration | ||
{ | ||
namespace backends | ||
{ | ||
|
||
std::size_t WriteData(const char* in, std::size_t size, std::size_t num, std::string* out) { | ||
const std::size_t totalBytes(size * num); | ||
out->append(in, totalBytes); | ||
return totalBytes; | ||
}; | ||
|
||
ApricotBackend::ApricotBackend(const std::string& host, int port) : | ||
mUrl(host + ":" + std::to_string(port)) | ||
{ | ||
mCurl = curl_easy_init(); | ||
curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, 0); | ||
curl_easy_setopt(mCurl, CURLOPT_CONNECTTIMEOUT, 3); | ||
curl_easy_setopt(mCurl, CURLOPT_TIMEOUT, 3); | ||
} | ||
|
||
ApricotBackend::~ApricotBackend() | ||
{ | ||
curl_easy_cleanup(mCurl); | ||
curl_global_cleanup(); | ||
} | ||
|
||
auto ApricotBackend::replaceDefaultWithSlash(const std::string& path) -> std::string | ||
{ | ||
auto p = path; | ||
std::replace(p.begin(), p.end(), getSeparator(), '/'); | ||
return p; | ||
} | ||
|
||
void ApricotBackend::putString(const std::string& /*path*/, const std::string& /*value*/) | ||
{ | ||
throw std::runtime_error("Apricot backend does not support putting values"); | ||
} | ||
|
||
boost::optional<std::string> ApricotBackend::getString(const std::string& path) | ||
{ | ||
return get(path); | ||
} | ||
|
||
|
||
std::string ApricotBackend::get(const std::string& path) { | ||
std::string response; | ||
std::string url = mUrl + "/" + replaceDefaultWithSlash(addApricotPrefix(path)) + mQueryParams; | ||
CURLcode res; | ||
long responseCode; | ||
curl_easy_setopt(mCurl, CURLOPT_URL, url.c_str()); | ||
curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, WriteData); | ||
curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, &response); | ||
|
||
res = curl_easy_perform(mCurl); | ||
curl_easy_getinfo(mCurl, CURLINFO_RESPONSE_CODE, &responseCode); | ||
|
||
if (res != CURLE_OK) { | ||
throw std::runtime_error(std::string(curl_easy_strerror(res)) + " " + url); | ||
return {}; | ||
} | ||
if (responseCode < 200 || responseCode > 206) { | ||
throw std::runtime_error("Wrong status code: " + std::to_string(responseCode)); | ||
} | ||
return response; | ||
} | ||
|
||
boost::property_tree::ptree ApricotBackend::getRecursive(const std::string& path) | ||
{ | ||
std::istringstream ss; | ||
ss.str(get(path)); | ||
boost::property_tree::ptree tree; | ||
boost::property_tree::read_json(ss, tree); | ||
return tree; | ||
} | ||
|
||
KeyValueMap ApricotBackend::getRecursiveMap(const std::string& path) | ||
{ | ||
KeyValueMap map; | ||
auto subTree = getRecursive(path); | ||
|
||
// define lambda to recursively interate tree | ||
using boost::property_tree::ptree; | ||
std::function<void(const ptree&, std::string)> parse = [&](const ptree& node, std::string key) { | ||
map[key] = std::move(node.data()); | ||
key = key.empty() ? "" : key + getSeparator(); | ||
for (auto const &it: node) { | ||
parse(it.second, key + it.first); | ||
} | ||
}; | ||
parse(subTree, std::string()); | ||
return map; | ||
} | ||
|
||
} // namespace backends | ||
} // namespace configuration | ||
} // namespace o2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// Copyright 2019-2020 CERN and copyright holders of ALICE O2. | ||
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. | ||
// All rights not expressly granted are reserved. | ||
// | ||
// This software is distributed under the terms of the GNU General Public | ||
// License v3 (GPL Version 3), copied verbatim in the file "COPYING". | ||
// | ||
// In applying this license CERN does not waive the privileges and immunities | ||
// granted to it by virtue of its status as an Intergovernmental Organization | ||
// or submit itself to any jurisdiction. | ||
|
||
/// \file ApricotBackend.h | ||
/// \brief Configuration interface to the Apricot key-value store | ||
/// | ||
/// \author Pascal Boeschoten, CERN | ||
/// \author Adam Wegrzynek, CERN | ||
|
||
#ifndef O2_CONFIGURATION_BACKENDS_APRICOTBACKEND_H_ | ||
#define O2_CONFIGURATION_BACKENDS_APRICOTBACKEND_H_ | ||
|
||
#include "../BackendBase.h" | ||
#include <curl/curl.h> | ||
#include <string> | ||
|
||
namespace o2 | ||
{ | ||
namespace configuration | ||
{ | ||
namespace backends | ||
{ | ||
|
||
/// Backend for Apricot | ||
class ApricotBackend final : public BackendBase | ||
{ | ||
public: | ||
/// Connects to Apricot backend | ||
ApricotBackend(const std::string& host, int port); | ||
|
||
virtual ~ApricotBackend(); | ||
virtual void putString(const std::string& path, const std::string& value) override; | ||
virtual boost::optional<std::string> getString(const std::string& path) override; | ||
virtual KeyValueMap getRecursiveMap(const std::string&) override; | ||
virtual boost::property_tree::ptree getRecursive(const std::string& path) override; | ||
|
||
void setBasePrefix(const std::string& path) | ||
{ | ||
mBasePrefix = path; | ||
} | ||
|
||
auto setParams(const std::string& params) | ||
{ | ||
mQueryParams = params; | ||
} | ||
|
||
private: | ||
/// Query params | ||
std::string mQueryParams; | ||
|
||
/// Base prefix | ||
std::string mBasePrefix; | ||
|
||
/// CURL handle | ||
CURL *mCurl; | ||
|
||
/// Apricot URL | ||
std::string mUrl; | ||
|
||
/// Replaces DEFAULT_SEPARATOR with '/', this is required by ppconsul | ||
/// \param path A path with DEFAULT_SEPARATOR | ||
/// \retrun A path with '/' separator | ||
std::string replaceDefaultWithSlash(const std::string& path); | ||
|
||
/// Replaces '/' with DEFAULT_SEPARATOR | ||
/// \param path A path with '/' separator | ||
/// \return A path with DEFAULT_SEPARATOR | ||
std::string replaceSlashWithDefault(const std::string& path); | ||
|
||
/// Runs request against Apricot server | ||
std::string get(const std::string& path); | ||
|
||
/// Adds base prefix to requested path | ||
auto addApricotPrefix(const std::string& path) | ||
{ | ||
return mBasePrefix.empty() ? addPrefix(path) : mBasePrefix + getSeparator() + addPrefix(path); | ||
} | ||
}; | ||
|
||
} // namespace backends | ||
} // namespace configuration | ||
} // namespace o2 | ||
|
||
#endif // O2_CONFIGURATION_BACKENDS_APRICOTBACKEND_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/// \file TestApricot.cxx | ||
/// \brief Unit tests for the Configuration. | ||
/// | ||
/// \author Adam Wegrzynek, CERN | ||
/// | ||
|
||
#include "Configuration/ConfigurationFactory.h" | ||
#include "Configuration/ConfigurationInterface.h" | ||
|
||
#define BOOST_TEST_MODULE ApricotBackend | ||
#define BOOST_TEST_MAIN | ||
#define BOOST_TEST_DYN_LINK | ||
#include <boost/test/unit_test.hpp> | ||
|
||
using namespace o2::configuration; | ||
|
||
namespace { | ||
|
||
const std::string APRICOT_ENDPOINT = "127.0.0.1:32188"; | ||
|
||
|
||
BOOST_AUTO_TEST_SUITE(optionalTest, * boost::unit_test::disabled()) | ||
|
||
|
||
BOOST_AUTO_TEST_CASE(simpleCheck) | ||
{ | ||
auto conf = ConfigurationFactory::getConfiguration("apricot://" + APRICOT_ENDPOINT); | ||
auto tree = conf->getRecursive("components.qc.ANY.any.tpc-full-qcmn"); | ||
BOOST_CHECK_EQUAL(tree.get<std::string>("qc.config.database.implementation"), "CCDB"); | ||
BOOST_CHECK_EQUAL(tree.get<std::string>("qc.tasks.RawDigits.moduleName"), "QcTPC"); | ||
|
||
} | ||
|
||
|
||
BOOST_AUTO_TEST_CASE(simpleWithProcess) | ||
{ | ||
auto conf = ConfigurationFactory::getConfiguration("apricot://" + APRICOT_ENDPOINT + "/components/qc/ANY/apricottest/Adam?run_type=PHYSICS"); | ||
auto tree = conf->getRecursive(""); | ||
BOOST_CHECK_EQUAL(tree.get<std::string>("bookkeeping.url"), "localhost:4001"); | ||
BOOST_CHECK_EQUAL(tree.get<std::string>("Barth"), "true"); | ||
} | ||
|
||
BOOST_AUTO_TEST_CASE(simpleWithoutProcess) | ||
{ | ||
auto conf = ConfigurationFactory::getConfiguration("apricot://" + APRICOT_ENDPOINT + "/components/qc/ANY/apricottest/Adam"); | ||
auto tree = conf->getRecursive(""); | ||
BOOST_CHECK_EQUAL(tree.get<std::string>("Barth"), "true"); | ||
BOOST_CHECK_THROW(tree.get<std::string>("bookkeeping.url"), boost::wrapexcept<boost::property_tree::ptree_bad_path>); | ||
} | ||
BOOST_AUTO_TEST_SUITE_END() | ||
|
||
BOOST_AUTO_TEST_CASE(Dummy) | ||
{ | ||
BOOST_CHECK(true); | ||
} | ||
|
||
} // Anonymous namespace |