mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-04 20:05:51 +00:00
@@ -47,6 +47,7 @@ CliArgs::parse(int argc, char const* argv[])
|
|||||||
("conf,c", po::value<std::string>()->default_value(kDEFAULT_CONFIG_PATH), "configuration file")
|
("conf,c", po::value<std::string>()->default_value(kDEFAULT_CONFIG_PATH), "configuration file")
|
||||||
("ng-web-server,w", "Use ng-web-server")
|
("ng-web-server,w", "Use ng-web-server")
|
||||||
("migrate", po::value<std::string>(), "start migration helper")
|
("migrate", po::value<std::string>(), "start migration helper")
|
||||||
|
("verify", "Checks the validity of config values")
|
||||||
;
|
;
|
||||||
// clang-format on
|
// clang-format on
|
||||||
po::positional_options_description positional;
|
po::positional_options_description positional;
|
||||||
@@ -75,6 +76,9 @@ CliArgs::parse(int argc, char const* argv[])
|
|||||||
return Action{Action::Migrate{.configPath = std::move(configPath), .subCmd = MigrateSubCmd::migration(opt)}};
|
return Action{Action::Migrate{.configPath = std::move(configPath), .subCmd = MigrateSubCmd::migration(opt)}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parsed.count("verify") != 0u)
|
||||||
|
return Action{Action::VerifyConfig{.configPath = std::move(configPath)}};
|
||||||
|
|
||||||
return Action{Action::Run{.configPath = std::move(configPath), .useNgWebServer = parsed.count("ng-web-server") != 0}
|
return Action{Action::Run{.configPath = std::move(configPath), .useNgWebServer = parsed.count("ng-web-server") != 0}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ public:
|
|||||||
MigrateSubCmd subCmd;
|
MigrateSubCmd subCmd;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @brief Verify Config action. */
|
||||||
|
struct VerifyConfig {
|
||||||
|
std::string configPath;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct an action from a Run.
|
* @brief Construct an action from a Run.
|
||||||
*
|
*
|
||||||
@@ -66,7 +71,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
template <typename ActionType>
|
template <typename ActionType>
|
||||||
requires std::is_same_v<ActionType, Run> or std::is_same_v<ActionType, Exit> or
|
requires std::is_same_v<ActionType, Run> or std::is_same_v<ActionType, Exit> or
|
||||||
std::is_same_v<ActionType, Migrate>
|
std::is_same_v<ActionType, Migrate> or std::is_same_v<ActionType, VerifyConfig>
|
||||||
explicit Action(ActionType&& action) : action_(std::forward<ActionType>(action))
|
explicit Action(ActionType&& action) : action_(std::forward<ActionType>(action))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -86,7 +91,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::variant<Run, Exit, Migrate> action_;
|
std::variant<Run, Exit, Migrate, VerifyConfig> action_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
55
src/app/VerifyConfig.hpp
Normal file
55
src/app/VerifyConfig.hpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2025, the clio developers.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "util/newconfig/ConfigDefinition.hpp"
|
||||||
|
#include "util/newconfig/ConfigFileJson.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verifies user's config values are correct
|
||||||
|
*
|
||||||
|
* @param configPath The path to config
|
||||||
|
* @return true if config values are all correct, false otherwise
|
||||||
|
*/
|
||||||
|
inline bool
|
||||||
|
verifyConfig(std::string_view configPath)
|
||||||
|
{
|
||||||
|
using namespace util::config;
|
||||||
|
|
||||||
|
auto const json = ConfigFileJson::makeConfigFileJson(configPath);
|
||||||
|
if (!json.has_value()) {
|
||||||
|
std::cerr << json.error().error << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto const errors = gClioConfig.parse(json.value());
|
||||||
|
if (errors.has_value()) {
|
||||||
|
for (auto const& err : errors.value())
|
||||||
|
std::cerr << err.error << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace app
|
||||||
@@ -19,12 +19,12 @@
|
|||||||
|
|
||||||
#include "app/CliArgs.hpp"
|
#include "app/CliArgs.hpp"
|
||||||
#include "app/ClioApplication.hpp"
|
#include "app/ClioApplication.hpp"
|
||||||
|
#include "app/VerifyConfig.hpp"
|
||||||
#include "migration/MigrationApplication.hpp"
|
#include "migration/MigrationApplication.hpp"
|
||||||
#include "rpc/common/impl/HandlerProvider.hpp"
|
#include "rpc/common/impl/HandlerProvider.hpp"
|
||||||
#include "util/TerminationHandler.hpp"
|
#include "util/TerminationHandler.hpp"
|
||||||
#include "util/log/Logger.hpp"
|
#include "util/log/Logger.hpp"
|
||||||
#include "util/newconfig/ConfigDefinition.hpp"
|
#include "util/newconfig/ConfigDefinition.hpp"
|
||||||
#include "util/newconfig/ConfigFileJson.hpp"
|
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
@@ -40,34 +40,27 @@ try {
|
|||||||
auto const action = app::CliArgs::parse(argc, argv);
|
auto const action = app::CliArgs::parse(argc, argv);
|
||||||
return action.apply(
|
return action.apply(
|
||||||
[](app::CliArgs::Action::Exit const& exit) { return exit.exitCode; },
|
[](app::CliArgs::Action::Exit const& exit) { return exit.exitCode; },
|
||||||
|
[](app::CliArgs::Action::VerifyConfig const& verify) {
|
||||||
|
if (app::verifyConfig(verify.configPath)) {
|
||||||
|
std::cout << "Config is correct" << "\n";
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
},
|
||||||
[](app::CliArgs::Action::Run const& run) {
|
[](app::CliArgs::Action::Run const& run) {
|
||||||
auto const json = ConfigFileJson::makeConfigFileJson(run.configPath);
|
auto const res = app::verifyConfig(run.configPath);
|
||||||
if (!json.has_value()) {
|
if (res != EXIT_SUCCESS)
|
||||||
std::cerr << json.error().error << std::endl;
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
|
||||||
auto const errors = gClioConfig.parse(json.value());
|
|
||||||
if (errors.has_value()) {
|
|
||||||
for (auto const& err : errors.value())
|
|
||||||
std::cerr << err.error << std::endl;
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
util::LogService::init(gClioConfig);
|
util::LogService::init(gClioConfig);
|
||||||
app::ClioApplication clio{gClioConfig};
|
app::ClioApplication clio{gClioConfig};
|
||||||
return clio.run(run.useNgWebServer);
|
return clio.run(run.useNgWebServer);
|
||||||
},
|
},
|
||||||
[](app::CliArgs::Action::Migrate const& migrate) {
|
[](app::CliArgs::Action::Migrate const& migrate) {
|
||||||
auto const json = ConfigFileJson::makeConfigFileJson(migrate.configPath);
|
auto const res = app::verifyConfig(migrate.configPath);
|
||||||
if (!json.has_value()) {
|
if (res != EXIT_SUCCESS)
|
||||||
std::cerr << json.error().error << std::endl;
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
|
||||||
auto const errors = gClioConfig.parse(json.value());
|
|
||||||
if (errors.has_value()) {
|
|
||||||
for (auto const& err : errors.value())
|
|
||||||
std::cerr << err.error << std::endl;
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
util::LogService::init(gClioConfig);
|
util::LogService::init(gClioConfig);
|
||||||
app::MigratorApplication migrator{gClioConfig, migrate.subCmd};
|
app::MigratorApplication migrator{gClioConfig, migrate.subCmd};
|
||||||
return migrator.run();
|
return migrator.run();
|
||||||
|
|||||||
@@ -416,6 +416,7 @@ static ClioConfigDefinition gClioConfig = ClioConfigDefinition{
|
|||||||
ConfigValue{ConfigType::Integer}.defaultValue(rpc::kAPI_VERSION_MIN).withConstraint(gValidateApiVersion)},
|
ConfigValue{ConfigType::Integer}.defaultValue(rpc::kAPI_VERSION_MIN).withConstraint(gValidateApiVersion)},
|
||||||
{"api_version.max",
|
{"api_version.max",
|
||||||
ConfigValue{ConfigType::Integer}.defaultValue(rpc::kAPI_VERSION_MAX).withConstraint(gValidateApiVersion)},
|
ConfigValue{ConfigType::Integer}.defaultValue(rpc::kAPI_VERSION_MAX).withConstraint(gValidateApiVersion)},
|
||||||
|
|
||||||
{"migration.full_scan_threads", ConfigValue{ConfigType::Integer}.defaultValue(2).withConstraint(gValidateUint32)},
|
{"migration.full_scan_threads", ConfigValue{ConfigType::Integer}.defaultValue(2).withConstraint(gValidateUint32)},
|
||||||
{"migration.full_scan_jobs", ConfigValue{ConfigType::Integer}.defaultValue(4).withConstraint(gValidateUint32)},
|
{"migration.full_scan_jobs", ConfigValue{ConfigType::Integer}.defaultValue(4).withConstraint(gValidateUint32)},
|
||||||
{"migration.cursors_per_job", ConfigValue{ConfigType::Integer}.defaultValue(100).withConstraint(gValidateUint32)}},
|
{"migration.cursors_per_job", ConfigValue{ConfigType::Integer}.defaultValue(100).withConstraint(gValidateUint32)}},
|
||||||
|
|||||||
@@ -208,3 +208,11 @@ static constexpr auto kINVALID_JSON_DATA = R"JSON({
|
|||||||
"withDefault" : "0.0"
|
"withDefault" : "0.0"
|
||||||
}
|
}
|
||||||
})JSON";
|
})JSON";
|
||||||
|
|
||||||
|
// used to Verify Config test
|
||||||
|
static constexpr auto kVALID_JSON_DATA = R"JSON({
|
||||||
|
"server": {
|
||||||
|
"ip": "0.0.0.0",
|
||||||
|
"port": 51233
|
||||||
|
}
|
||||||
|
})JSON";
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ target_sources(
|
|||||||
PRIVATE # Common
|
PRIVATE # Common
|
||||||
ConfigTests.cpp
|
ConfigTests.cpp
|
||||||
app/CliArgsTests.cpp
|
app/CliArgsTests.cpp
|
||||||
|
app/VerifyConfigTests.cpp
|
||||||
app/WebHandlersTests.cpp
|
app/WebHandlersTests.cpp
|
||||||
data/AmendmentCenterTests.cpp
|
data/AmendmentCenterTests.cpp
|
||||||
data/BackendCountersTests.cpp
|
data/BackendCountersTests.cpp
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ struct CliArgsTests : testing::Test {
|
|||||||
testing::StrictMock<testing::MockFunction<int(CliArgs::Action::Run)>> onRunMock;
|
testing::StrictMock<testing::MockFunction<int(CliArgs::Action::Run)>> onRunMock;
|
||||||
testing::StrictMock<testing::MockFunction<int(CliArgs::Action::Exit)>> onExitMock;
|
testing::StrictMock<testing::MockFunction<int(CliArgs::Action::Exit)>> onExitMock;
|
||||||
testing::StrictMock<testing::MockFunction<int(CliArgs::Action::Migrate)>> onMigrateMock;
|
testing::StrictMock<testing::MockFunction<int(CliArgs::Action::Migrate)>> onMigrateMock;
|
||||||
|
testing::StrictMock<testing::MockFunction<int(CliArgs::Action::VerifyConfig)>> onVerifyMock;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(CliArgsTests, Parse_NoArgs)
|
TEST_F(CliArgsTests, Parse_NoArgs)
|
||||||
@@ -46,7 +47,13 @@ TEST_F(CliArgsTests, Parse_NoArgs)
|
|||||||
return returnCode;
|
return returnCode;
|
||||||
});
|
});
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
action.apply(onRunMock.AsStdFunction(), onExitMock.AsStdFunction(), onMigrateMock.AsStdFunction()), returnCode
|
action.apply(
|
||||||
|
onRunMock.AsStdFunction(),
|
||||||
|
onExitMock.AsStdFunction(),
|
||||||
|
onMigrateMock.AsStdFunction(),
|
||||||
|
onVerifyMock.AsStdFunction()
|
||||||
|
),
|
||||||
|
returnCode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +69,12 @@ TEST_F(CliArgsTests, Parse_NgWebServer)
|
|||||||
return returnCode;
|
return returnCode;
|
||||||
});
|
});
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
action.apply(onRunMock.AsStdFunction(), onExitMock.AsStdFunction(), onMigrateMock.AsStdFunction()),
|
action.apply(
|
||||||
|
onRunMock.AsStdFunction(),
|
||||||
|
onExitMock.AsStdFunction(),
|
||||||
|
onMigrateMock.AsStdFunction(),
|
||||||
|
onVerifyMock.AsStdFunction()
|
||||||
|
),
|
||||||
returnCode
|
returnCode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -79,7 +91,12 @@ TEST_F(CliArgsTests, Parse_VersionHelp)
|
|||||||
|
|
||||||
EXPECT_CALL(onExitMock, Call).WillOnce([](CliArgs::Action::Exit const& exit) { return exit.exitCode; });
|
EXPECT_CALL(onExitMock, Call).WillOnce([](CliArgs::Action::Exit const& exit) { return exit.exitCode; });
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
action.apply(onRunMock.AsStdFunction(), onExitMock.AsStdFunction(), onMigrateMock.AsStdFunction()),
|
action.apply(
|
||||||
|
onRunMock.AsStdFunction(),
|
||||||
|
onExitMock.AsStdFunction(),
|
||||||
|
onMigrateMock.AsStdFunction(),
|
||||||
|
onVerifyMock.AsStdFunction()
|
||||||
|
),
|
||||||
EXIT_SUCCESS
|
EXIT_SUCCESS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -97,6 +114,34 @@ TEST_F(CliArgsTests, Parse_Config)
|
|||||||
return returnCode;
|
return returnCode;
|
||||||
});
|
});
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
action.apply(onRunMock.AsStdFunction(), onExitMock.AsStdFunction(), onMigrateMock.AsStdFunction()), returnCode
|
action.apply(
|
||||||
|
onRunMock.AsStdFunction(),
|
||||||
|
onExitMock.AsStdFunction(),
|
||||||
|
onMigrateMock.AsStdFunction(),
|
||||||
|
onVerifyMock.AsStdFunction()
|
||||||
|
),
|
||||||
|
returnCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CliArgsTests, Parse_VerifyConfig)
|
||||||
|
{
|
||||||
|
std::string_view configPath = "some_config_path";
|
||||||
|
std::array argv{"clio_server", configPath.data(), "--verify"}; // NOLINT(bugprone-suspicious-stringview-data-usage)
|
||||||
|
auto const action = CliArgs::parse(argv.size(), argv.data());
|
||||||
|
|
||||||
|
int const returnCode = 123;
|
||||||
|
EXPECT_CALL(onVerifyMock, Call).WillOnce([&configPath](CliArgs::Action::VerifyConfig const& verify) {
|
||||||
|
EXPECT_EQ(verify.configPath, configPath);
|
||||||
|
return returnCode;
|
||||||
|
});
|
||||||
|
EXPECT_EQ(
|
||||||
|
action.apply(
|
||||||
|
onRunMock.AsStdFunction(),
|
||||||
|
onExitMock.AsStdFunction(),
|
||||||
|
onMigrateMock.AsStdFunction(),
|
||||||
|
onVerifyMock.AsStdFunction()
|
||||||
|
),
|
||||||
|
returnCode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
62
tests/unit/app/VerifyConfigTests.cpp
Normal file
62
tests/unit/app/VerifyConfigTests.cpp
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2025, the clio developers.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include "app/VerifyConfig.hpp"
|
||||||
|
#include "util/TmpFile.hpp"
|
||||||
|
#include "util/newconfig/FakeConfigData.hpp"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace app;
|
||||||
|
using namespace util::config;
|
||||||
|
|
||||||
|
TEST(VerifyConfigTest, InvalidConfig)
|
||||||
|
{
|
||||||
|
auto const tmpConfigFile = TmpFile(kJSON_DATA);
|
||||||
|
|
||||||
|
// false because json data(kJSON_DATA) is not compatible with current configDefintion
|
||||||
|
EXPECT_FALSE(verifyConfig(tmpConfigFile.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VerifyConfigTest, ValidConfig)
|
||||||
|
{
|
||||||
|
auto const tmpConfigFile = TmpFile(kVALID_JSON_DATA);
|
||||||
|
|
||||||
|
// current example config should always be compatible with configDefinition
|
||||||
|
EXPECT_TRUE(verifyConfig(tmpConfigFile.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VerifyConfigTest, ConfigFileNotExist)
|
||||||
|
{
|
||||||
|
EXPECT_FALSE(verifyConfig("doesn't exist Config File"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(VerifyConfigTest, InvalidJsonFile)
|
||||||
|
{
|
||||||
|
// invalid json because extra "," after 51233
|
||||||
|
static constexpr auto kINVALID_JSON = R"({
|
||||||
|
"server": {
|
||||||
|
"ip": "0.0.0.0",
|
||||||
|
"port": 51233,
|
||||||
|
}
|
||||||
|
})";
|
||||||
|
auto const tmpConfigFile = TmpFile(kINVALID_JSON);
|
||||||
|
|
||||||
|
EXPECT_FALSE(verifyConfig(tmpConfigFile.path));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user