TEST_F(ConfigTests, test_strip_comments) {
  std::string json_comments =
      "// Comment\n // Comment //\n  # Comment\n# Comment\n{\"options\":{}}";

  // Test support for stripping C++ and hash style comments from config JSON.
  auto actual = json_comments;
  stripConfigComments(actual);
  std::string expected = "{\"options\":{}}\n";
  EXPECT_EQ(actual, expected);

  // Make sure the config update source logic applies the stripping.
  EXPECT_TRUE(get().update({{"data", json_comments}}));
}
Status FilesystemConfigPlugin::genPack(const std::string& name,
                                       const std::string& value,
                                       std::string& pack) {
  if (name == "*") {
    // The config requested a multi-pack.
    std::vector<std::string> paths;
    resolveFilePattern(value, paths);

    pt::ptree multi_pack;
    for (const auto& path : paths) {
      std::string content;
      if (!readFile(path, content)) {
        LOG(WARNING) << "Cannot read multi-pack file: " << path;
        continue;
      }

      // Assemble an intermediate property tree for simplified parsing.
      pt::ptree single_pack;
      stripConfigComments(content);
      try {
        std::stringstream json_stream;
        json_stream << content;
        pt::read_json(json_stream, single_pack);
      } catch (const pt::json_parser::json_parser_error& /* e */) {
        LOG(WARNING) << "Cannot read multi-pack JSON: " << path;
        continue;
      }

      multi_pack.put_child(fs::path(path).stem().string(), single_pack);
    }

    // We should have a property tree of pack content mimicking embedded
    // configuration packs, ready to parse as a string.
    std::ostringstream output;
    pt::write_json(output, multi_pack, false);
    pack = output.str();
    if (pack.empty()) {
      return Status(1, "Multi-pack content empty");
    }

    return Status(0);
  }

  boost::system::error_code ec;
  if (!fs::is_regular_file(value, ec) || ec.value() != errc::success) {
    return Status(1, value + " is not a valid path");
  }

  return readFile(value, pack);
}
Exemple #3
0
Status Config::updateSource(const std::string& source,
                            const std::string& json) {
  // Compute a 'synthesized' hash using the content before it is parsed.
  hashSource(source, json);

  // Remove all packs from this source.
  schedule_->removeAll(source);
  // Remove all files from this source.
  removeFiles(source);

  // load the config (source.second) into a pt::ptree
  pt::ptree tree;
  try {
    auto clone = json;
    stripConfigComments(clone);
    std::stringstream json_stream;
    json_stream << clone;
    pt::read_json(json_stream, tree);
  } catch (const pt::json_parser::json_parser_error& /* e */) {
    return Status(1, "Error parsing the config JSON");
  }

  // extract the "schedule" key and store it as the main pack
  if (tree.count("schedule") > 0 && !Registry::external()) {
    auto& schedule = tree.get_child("schedule");
    pt::ptree main_pack;
    main_pack.add_child("queries", schedule);
    addPack("main", source, main_pack);
  }

  if (tree.count("scheduledQueries") > 0 && !Registry::external()) {
    auto& scheduled_queries = tree.get_child("scheduledQueries");
    pt::ptree queries;
    for (const std::pair<std::string, pt::ptree>& query : scheduled_queries) {
      auto query_name = query.second.get<std::string>("name", "");
      if (query_name.empty()) {
        return Status(1, "Error getting name from legacy scheduled query");
      }
      queries.add_child(query_name, query.second);
    }
    pt::ptree legacy_pack;
    legacy_pack.add_child("queries", queries);
    addPack("legacy_main", source, legacy_pack);
  }

  // extract the "packs" key into additional pack objects
  if (tree.count("packs") > 0 && !Registry::external()) {
    auto& packs = tree.get_child("packs");
    for (const auto& pack : packs) {
      auto value = packs.get<std::string>(pack.first, "");
      if (value.empty()) {
        // The pack is a JSON object, treat the content as pack data.
        addPack(pack.first, source, pack.second);
      } else {
        genPack(pack.first, source, value);
      }
    }
  }

  applyParsers(source, tree, false);
  return Status(0, "OK");
}
Exemple #4
0
Status Config::updateSource(const std::string& name, const std::string& json) {
  // Compute a 'synthesized' hash using the content before it is parsed.
  hashSource(name, json);

  // load the config (source.second) into a pt::ptree
  pt::ptree tree;
  try {
    auto clone = json;
    stripConfigComments(clone);
    std::stringstream json_stream;
    json_stream << clone;
    pt::read_json(json_stream, tree);
  } catch (const pt::json_parser::json_parser_error& e) {
    return Status(1, "Error parsing the config JSON");
  }

  // extract the "schedule" key and store it as the main pack
  if (tree.count("schedule") > 0 && !Registry::external()) {
    auto& schedule = tree.get_child("schedule");
    pt::ptree main_pack;
    main_pack.add_child("queries", schedule);
    addPack("main", name, main_pack);
  }

  if (tree.count("scheduledQueries") > 0 && !Registry::external()) {
    auto& scheduled_queries = tree.get_child("scheduledQueries");
    pt::ptree queries;
    for (const std::pair<std::string, pt::ptree>& query : scheduled_queries) {
      auto query_name = query.second.get<std::string>("name", "");
      if (query_name.empty()) {
        return Status(1, "Error getting name from legacy scheduled query");
      }
      queries.add_child(query_name, query.second);
    }
    pt::ptree legacy_pack;
    legacy_pack.add_child("queries", queries);
    addPack("legacy_main", name, legacy_pack);
  }

  // extract the "packs" key into additional pack objects
  if (tree.count("packs") > 0 && !Registry::external()) {
    auto& packs = tree.get_child("packs");
    for (const auto& pack : packs) {
      auto value = packs.get<std::string>(pack.first, "");
      if (value.empty()) {
        addPack(pack.first, name, pack.second);
      } else {
        PluginResponse response;
        PluginRequest request = {
            {"action", "genPack"}, {"name", pack.first}, {"value", value}};
        Registry::call("config", request, response);

        if (response.size() == 0 || response[0].count(pack.first) == 0) {
          continue;
        }

        try {
          pt::ptree pack_tree;
          std::stringstream pack_stream;
          pack_stream << response[0][pack.first];
          pt::read_json(pack_stream, pack_tree);
          addPack(pack.first, name, pack_tree);
        } catch (const pt::json_parser::json_parser_error& e) {
          LOG(WARNING) << "Error parsing the pack JSON: " << pack.first;
        }
      }
    }
  }

  // Iterate each parser.
  for (const auto& plugin : Registry::all("config_parser")) {
    std::shared_ptr<ConfigParserPlugin> parser = nullptr;
    try {
      parser = std::dynamic_pointer_cast<ConfigParserPlugin>(plugin.second);
    } catch (const std::bad_cast& e) {
      LOG(ERROR) << "Error casting config parser plugin: " << plugin.first;
    }
    if (parser == nullptr || parser.get() == nullptr) {
      continue;
    }

    // For each key requested by the parser, add a property tree reference.
    std::map<std::string, pt::ptree> parser_config;
    for (const auto& key : parser->keys()) {
      if (tree.count(key) > 0) {
        parser_config[key] = tree.get_child(key);
      } else {
        parser_config[key] = pt::ptree();
      }
    }

    // The config parser plugin will receive a copy of each property tree for
    // each top-level-config key. The parser may choose to update the config's
    // internal state
    parser->update(name, parser_config);
  }
  return Status(0, "OK");
}
Exemple #5
0
Status Config::updateSource(const std::string& name, const std::string& json) {
  // Compute a 'synthesized' hash using the content before it is parsed.
  hashSource(name, json);

  // Remove all packs from this source.
  schedule_->removeAll(name);
  // Remove all files from this source.
  removeFiles(name);

  // load the config (source.second) into a pt::ptree
  pt::ptree tree;
  try {
    auto clone = json;
    stripConfigComments(clone);
    std::stringstream json_stream;
    json_stream << clone;
    pt::read_json(json_stream, tree);
  } catch (const pt::json_parser::json_parser_error& e) {
    return Status(1, "Error parsing the config JSON");
  }

  // extract the "schedule" key and store it as the main pack
  if (tree.count("schedule") > 0 && !Registry::external()) {
    auto& schedule = tree.get_child("schedule");
    pt::ptree main_pack;
    main_pack.add_child("queries", schedule);
    addPack("main", name, main_pack);
  }

  if (tree.count("scheduledQueries") > 0 && !Registry::external()) {
    auto& scheduled_queries = tree.get_child("scheduledQueries");
    pt::ptree queries;
    for (const std::pair<std::string, pt::ptree>& query : scheduled_queries) {
      auto query_name = query.second.get<std::string>("name", "");
      if (query_name.empty()) {
        return Status(1, "Error getting name from legacy scheduled query");
      }
      queries.add_child(query_name, query.second);
    }
    pt::ptree legacy_pack;
    legacy_pack.add_child("queries", queries);
    addPack("legacy_main", name, legacy_pack);
  }

  // extract the "packs" key into additional pack objects
  if (tree.count("packs") > 0 && !Registry::external()) {
    auto& packs = tree.get_child("packs");
    for (const auto& pack : packs) {
      auto value = packs.get<std::string>(pack.first, "");
      if (value.empty()) {
        // The pack is a JSON object, treat the content as pack data.
        addPack(pack.first, name, pack.second);
      } else {
        // If the pack value is a string (and not a JSON object) then it is a
        // resource to be handled by the config plugin.
        PluginResponse response;
        PluginRequest request = {
            {"action", "genPack"}, {"name", pack.first}, {"value", value}};
        Registry::call("config", request, response);

        if (response.size() == 0 || response[0].count(pack.first) == 0) {
          continue;
        }

        try {
          pt::ptree pack_tree;
          std::stringstream pack_stream;
          pack_stream << response[0][pack.first];
          pt::read_json(pack_stream, pack_tree);
          addPack(pack.first, name, pack_tree);
        } catch (const pt::json_parser::json_parser_error& e) {
          LOG(WARNING) << "Error parsing the pack JSON: " << pack.first;
        }
      }
    }
  }

  applyParsers(name, tree, false);
  return Status(0, "OK");
}