LCOV - code coverage report
Current view: top level - irohad/main - iroha_conf_loader.cpp (source / functions) Hit Total Coverage
Test: cleared_cor.info Lines: 0 121 0.0 %
Date: 2019-03-07 14:46:43 Functions: 0 34 0.0 %

          Line data    Source code
       1             : /**
       2             :  * Copyright Soramitsu Co., Ltd. All Rights Reserved.
       3             :  * SPDX-License-Identifier: Apache-2.0
       4             :  */
       5             : 
       6             : #include "main/iroha_conf_loader.hpp"
       7             : 
       8             : #include <fstream>
       9             : #include <limits>
      10             : #include <sstream>
      11             : 
      12             : #include <rapidjson/document.h>
      13             : #include <rapidjson/error/en.h>
      14             : #include <rapidjson/istreamwrapper.h>
      15             : #include <rapidjson/rapidjson.h>
      16             : #include <boost/algorithm/string/join.hpp>
      17             : #include <boost/range/adaptor/map.hpp>
      18             : #include "main/iroha_conf_literals.hpp"
      19             : 
      20             : /// The length of the string around the error place to print in case of JSON
      21             : /// syntax error.
      22             : static constexpr size_t kBadJsonPrintLength = 15;
      23             : 
      24             : /// The offset of printed chunk towards file start from the error position.
      25             : static constexpr size_t kBadJsonPrintOffsset = 5;
      26             : 
      27             : static_assert(kBadJsonPrintOffsset <= kBadJsonPrintLength,
      28             :               "The place of error is out of the printed string boundaries!");
      29             : 
      30             : /**
      31             :  * Adds the children logger configs from parent logger JSON object to parent
      32             :  * logger config. The parent logger JSON object is searched for the children
      33             :  * config section, and the children configs are parsed and created if the
      34             :  * section is present.
      35             :  * @param path - current config node path used to denote the possible error
      36             :  *    place.
      37             :  * @param parent_config - the parent logger config
      38             :  * @param parent_obj - the parent logger json configuration
      39             :  */
      40             : void addChildrenLoggerConfigs(const std::string &path,
      41             :                               const logger::LoggerManagerTreePtr &parent_config,
      42             :                               const rapidjson::Value::ConstObject &parent_obj);
      43             : 
      44             : /**
      45             :  * Overrides the logger configuration with the values from JSON object.
      46             :  * @param path - current config node path used to denote the possible error
      47             :  *    place.
      48             :  * @param cfg - the configuration to use as base
      49             :  * @param obj - the JSON object to take overrides from
      50             :  */
      51             : void updateLoggerConfig(const std::string &path,
      52             :                         logger::LoggerConfig &cfg,
      53             :                         const rapidjson::Value::ConstObject &obj);
      54             : 
      55             : /**
      56             :  * Gets a value by a key from a JSON object, if present.
      57             :  * @param path - current config node path used to denote the possible error
      58             :  *    place.
      59             :  * @param dest - the variable to store the value
      60             :  * @param obj - the source JSON object
      61             :  * @param key - the key for the requested value
      62             :  * @return true if the value was loaded, otherwise false.
      63             :  */
      64             : template <typename TDest, typename TKey>
      65             : bool tryGetValByKey(const std::string &path,
      66             :                     TDest &dest,
      67             :                     const rapidjson::Value::ConstObject &obj,
      68             :                     const TKey &key);
      69             : 
      70             : /**
      71             :  * Gets an optional value by a key from a JSON object.
      72             :  * @param path - current config node path used to denote the possible error
      73             :  *    place.
      74             :  * @param obj - the source JSON object
      75             :  * @param key - the key for the requested value
      76             :  * @return the value if present in the JSON object, otherwise boost::none.
      77             :  */
      78             : template <typename TDest, typename TKey>
      79             : boost::optional<TDest> getOptValByKey(const std::string &path,
      80             :                                       const rapidjson::Value::ConstObject &obj,
      81             :                                       const TKey &key);
      82             : 
      83             : /**
      84             :  * Adds one sublevel to the path denoting a place in config tree.
      85             :  * @param parent - the location of the sublevel
      86             :  * @param child - the name of sublevel
      87             :  * @return the path to the sublevel
      88             :  */
      89             : template <typename TChild>
      90             : inline std::string sublevelPath(std::string parent, TChild child) {
      91           0 :   std::stringstream child_sstream;
      92           0 :   child_sstream << child;
      93           0 :   return std::move(parent) + "/" + child_sstream.str();
      94           0 : }
      95             : 
      96             : /**
      97             :  * Gets a value by a key from a JSON object. Throws an exception if the value
      98             :  * was not loaded.
      99             :  * @param path - current config node path used to denote the possible error
     100             :  *    place.
     101             :  * @param dest - the variable to store the value
     102             :  * @param obj - the source JSON object
     103             :  * @param key - the key for the requested value
     104             :  */
     105             : template <typename TDest, typename TKey>
     106             : void getValByKey(const std::string &path,
     107             :                  TDest &dest,
     108             :                  const rapidjson::Value::ConstObject &obj,
     109             :                  const TKey &key);
     110             : 
     111             : /**
     112             :  * Throws a runtime exception if the given condition is false.
     113             :  * @param condition
     114             :  * @param error - error message
     115             :  */
     116             : inline void assert_fatal(bool condition, std::string error) {
     117           0 :   if (!condition) {
     118           0 :     throw std::runtime_error(error);
     119             :   }
     120           0 : }
     121             : 
     122             : // ------------ getVal(path, dst, src) ------------
     123             : // getVal is a set of functions that load the value from rapidjson::Value to a
     124             : // given destination variable. They check the JSON type and throw exception if
     125             : // it is wrong. The path argument is used to denote the possible error place.
     126             : 
     127             : template <typename TDest>
     128             : typename std::enable_if<not std::numeric_limits<TDest>::is_integer>::type
     129             : getVal(const std::string &path, TDest &, const rapidjson::Value &) {
     130             :   BOOST_THROW_EXCEPTION(
     131             :       std::runtime_error("Wrong type. Should never reach here."));
     132             : }
     133             : 
     134             : template <typename TDest>
     135             : typename std::enable_if<std::numeric_limits<TDest>::is_integer>::type getVal(
     136             :     const std::string &path, TDest &dest, const rapidjson::Value &src) {
     137           0 :   assert_fatal(src.IsInt64(), path + " must be an integer");
     138           0 :   const int64_t val = src.GetInt64();
     139           0 :   assert_fatal(val >= std::numeric_limits<TDest>::min()
     140           0 :                    && val <= std::numeric_limits<TDest>::max(),
     141           0 :                path + ": integer value out of range");
     142           0 :   dest = val;
     143           0 : }
     144             : 
     145             : template <>
     146             : void getVal<bool>(const std::string &path,
     147             :                   bool &dest,
     148             :                   const rapidjson::Value &src) {
     149           0 :   assert_fatal(src.IsBool(), path + " must be a boolean");
     150           0 :   dest = src.GetBool();
     151           0 : }
     152             : 
     153             : template <>
     154             : void getVal<std::string>(const std::string &path,
     155             :                          std::string &dest,
     156             :                          const rapidjson::Value &src) {
     157           0 :   assert_fatal(src.IsString(), path + " must be a string");
     158           0 :   dest = src.GetString();
     159           0 : }
     160             : 
     161             : template <>
     162             : void getVal<logger::LogLevel>(const std::string &path,
     163             :                               logger::LogLevel &dest,
     164             :                               const rapidjson::Value &src) {
     165           0 :   std::string level_str;
     166           0 :   getVal(path, level_str, src);
     167           0 :   const auto it = config_members::LogLevels.find(level_str);
     168           0 :   if (it == config_members::LogLevels.end()) {
     169           0 :     BOOST_THROW_EXCEPTION(std::runtime_error(
     170             :         "Wrong log level at " + path + ": must be one of '"
     171             :         + boost::algorithm::join(
     172             :               config_members::LogLevels | boost::adaptors::map_keys, "', '")
     173             :         + "'."));
     174             :   }
     175           0 :   dest = it->second;
     176           0 : }
     177             : 
     178             : template <>
     179             : void getVal<logger::LogPatterns>(const std::string &path,
     180             :                                  logger::LogPatterns &dest,
     181             :                                  const rapidjson::Value &src) {
     182           0 :   assert_fatal(src.IsObject(),
     183           0 :                path + " must be a map from log level to pattern");
     184           0 :   for (const auto &pattern_entry : src.GetObject()) {
     185             :     logger::LogLevel level;
     186           0 :     std::string pattern_str;
     187           0 :     getVal(sublevelPath(path, "(level name)"), level, pattern_entry.name);
     188           0 :     getVal(sublevelPath(path, "(pattern)"), pattern_str, pattern_entry.value);
     189           0 :     dest.setPattern(level, pattern_str);
     190           0 :   }
     191           0 : }
     192             : 
     193             : template <>
     194             : void getVal<logger::LoggerManagerTreePtr>(const std::string &path,
     195             :                                           logger::LoggerManagerTreePtr &dest,
     196             :                                           const rapidjson::Value &src) {
     197           0 :   assert_fatal(src.IsObject(), path + " must be a logger tree config");
     198           0 :   logger::LoggerConfig root_config{logger::kDefaultLogLevel,
     199           0 :                                    logger::getDefaultLogPatterns()};
     200           0 :   updateLoggerConfig(path, root_config, src.GetObject());
     201           0 :   dest = std::make_shared<logger::LoggerManagerTree>(
     202           0 :       std::make_shared<const logger::LoggerConfig>(std::move(root_config)));
     203           0 :   addChildrenLoggerConfigs(path, dest, src.GetObject());
     204           0 : }
     205             : 
     206             : template <>
     207             : void getVal<IrohadConfig>(const std::string &path,
     208             :                           IrohadConfig &dest,
     209             :                           const rapidjson::Value &src) {
     210           0 :   assert_fatal(src.IsObject(),
     211           0 :                path + " Irohad config top element must be an object.");
     212           0 :   const auto obj = src.GetObject();
     213           0 :   getValByKey(path, dest.block_store_path, obj, config_members::BlockStorePath);
     214           0 :   getValByKey(path, dest.torii_port, obj, config_members::ToriiPort);
     215           0 :   getValByKey(path, dest.internal_port, obj, config_members::InternalPort);
     216           0 :   getValByKey(path, dest.pg_opt, obj, config_members::PgOpt);
     217           0 :   getValByKey(
     218           0 :       path, dest.max_proposal_size, obj, config_members::MaxProposalSize);
     219           0 :   getValByKey(path, dest.proposal_delay, obj, config_members::ProposalDelay);
     220           0 :   getValByKey(path, dest.vote_delay, obj, config_members::VoteDelay);
     221           0 :   getValByKey(path, dest.mst_support, obj, config_members::MstSupport);
     222           0 :   getValByKey(
     223           0 :       path, dest.mst_expiration_time, obj, config_members::MstExpirationTime);
     224           0 :   getValByKey(
     225           0 :       path, dest.max_round_delay_ms, obj, config_members::MaxRoundsDelay);
     226           0 :   getValByKey(path,
     227           0 :               dest.stale_stream_max_rounds,
     228             :               obj,
     229             :               config_members::StaleStreamMaxRounds);
     230           0 :   getValByKey(path, dest.logger_manager, obj, config_members::LogSection);
     231           0 : }
     232             : 
     233             : // ------------ end of getVal(path, dst, src) ------------
     234             : 
     235             : void updateLoggerConfig(const std::string &path,
     236             :                         logger::LoggerConfig &cfg,
     237             :                         const rapidjson::Value::ConstObject &obj) {
     238           0 :   tryGetValByKey(path, cfg.log_level, obj, config_members::LogLevel);
     239           0 :   tryGetValByKey(path, cfg.patterns, obj, config_members::LogPatternsSection);
     240           0 : }
     241             : 
     242             : template <typename TDest, typename TKey>
     243             : bool tryGetValByKey(const std::string &path,
     244             :                     TDest &dest,
     245             :                     const rapidjson::Value::ConstObject &obj,
     246             :                     const TKey &key) {
     247           0 :   const auto it = obj.FindMember(key);
     248           0 :   if (it == obj.MemberEnd()) {
     249           0 :     return false;
     250             :   } else {
     251           0 :     getVal(sublevelPath(path, key), dest, it->value);
     252           0 :     return true;
     253             :   }
     254           0 : }
     255             : 
     256             : template <typename TDest, typename TKey>
     257             : bool tryGetValByKey(const std::string &path,
     258             :                     boost::optional<TDest> &dest,
     259             :                     const rapidjson::Value::ConstObject &obj,
     260             :                     const TKey &key) {
     261           0 :   dest = getOptValByKey<TDest>(path, obj, key);
     262           0 :   return true; // value loaded any way, either from file or boost::none
     263             : }
     264             : 
     265             : template <typename TDest, typename TKey>
     266             : boost::optional<TDest> getOptValByKey(const std::string &path,
     267             :                                       const rapidjson::Value::ConstObject &obj,
     268             :                                       const TKey &key) {
     269           0 :   TDest val;
     270           0 :   return boost::make_optional(tryGetValByKey(path, val, obj, key), val);
     271           0 : }
     272             : 
     273             : /**
     274             :  * Gets a value by a key from a JSON object. Throws an exception if the value
     275             :  * was not loaded.
     276             :  * @param path - current config node path used to denote the possible error
     277             :  *    place.
     278             :  * @param dest - the variable to store the value
     279             :  * @param obj - the source JSON object
     280             :  * @param key - the key for the requested value
     281             :  */
     282             : template <typename TDest, typename TKey>
     283             : void getValByKey(const std::string &path,
     284             :                  TDest &dest,
     285             :                  const rapidjson::Value::ConstObject &obj,
     286             :                  const TKey &key) {
     287           0 :   assert_fatal(tryGetValByKey(path, dest, obj, key),
     288           0 :                path + " has no key '" + key + "'.");
     289           0 : }
     290             : 
     291             : void addChildrenLoggerConfigs(const std::string &path,
     292             :                               const logger::LoggerManagerTreePtr &parent_config,
     293             :                               const rapidjson::Value::ConstObject &parent_obj) {
     294           0 :   const auto it = parent_obj.FindMember(config_members::LogChildrenSection);
     295           0 :   if (it != parent_obj.MemberEnd()) {
     296             :     auto children_section_path =
     297           0 :         sublevelPath(path, config_members::LogChildrenSection);
     298           0 :     for (const auto &child_json : it->value.GetObject()) {
     299           0 :       assert_fatal(child_json.name.IsString(),
     300           0 :                    "Child logger key must be a string holding its tag.");
     301           0 :       assert_fatal(child_json.value.IsObject(),
     302           0 :                    "Child logger value must be a JSON object.");
     303           0 :       auto child_tag = child_json.name.GetString();
     304           0 :       const auto child_obj = child_json.value.GetObject();
     305           0 :       auto child_path = sublevelPath(children_section_path, child_tag);
     306           0 :       auto child_conf = parent_config->registerChild(
     307           0 :           std::move(child_tag),
     308           0 :           getOptValByKey<logger::LogLevel>(
     309             :               child_path, child_obj, config_members::LogLevel),
     310           0 :           getOptValByKey<logger::LogPatterns>(
     311             :               child_path, child_obj, config_members::LogPatternsSection));
     312           0 :       addChildrenLoggerConfigs(std::move(child_path), child_conf, child_obj);
     313           0 :     }
     314           0 :   }
     315           0 : }
     316             : 
     317             : std::string reportJsonParsingError(const rapidjson::Document &doc,
     318             :                                    const std::string &conf_path,
     319             :                                    std::istream &input) {
     320           0 :   const size_t error_offset = doc.GetErrorOffset();
     321             :   // This ensures the unsigned string beginning position does not cross zero:
     322           0 :   const size_t print_offset =
     323           0 :       std::max(error_offset, kBadJsonPrintOffsset) - kBadJsonPrintOffsset;
     324           0 :   input.seekg(print_offset);
     325           0 :   std::string json_error_buf(kBadJsonPrintLength, 0);
     326           0 :   input.readsome(&json_error_buf[0], kBadJsonPrintLength);
     327           0 :   return "JSON parse error [" + conf_path + "] " + "(near `" + json_error_buf
     328           0 :       + "'): " + std::string(rapidjson::GetParseError_En(doc.GetParseError()));
     329           0 : }
     330             : 
     331             : IrohadConfig parse_iroha_config(const std::string &conf_path) {
     332             :   const rapidjson::Document doc{[&conf_path] {
     333           0 :     rapidjson::Document doc;
     334           0 :     std::ifstream ifs_iroha(conf_path);
     335           0 :     rapidjson::IStreamWrapper isw(ifs_iroha);
     336           0 :     doc.ParseStream(isw);
     337           0 :     assert_fatal(not doc.HasParseError(),
     338           0 :                  reportJsonParsingError(doc, conf_path, ifs_iroha));
     339           0 :     return doc;
     340           0 :   }()};
     341             : 
     342           0 :   IrohadConfig config;
     343           0 :   getVal("", config, doc);
     344           0 :   return config;
     345           0 : }

Generated by: LCOV version 1.13