mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Load validator list from file:
* Adds local file:// URL support to the [validator_list_sites] stanza. The file:// URL must not contain a hostname. Allows a rippled node operator to "sideload" a new list if their node is unable to reach a validator list's web site before an old list expires. Lists loaded from a file will be validated in the same way a downloaded list is validated. * Generalize file/dir "guards" from Config test so they can be reused in other tests. * Check for error when reading validators.txt. Saves some parsing and checking of an empty string, and will give a more meaningful error. * Completes RIPD-1674.
This commit is contained in:
committed by
Nik Bougalis
parent
e7a69cce65
commit
c1a02440dc
File diff suppressed because one or more lines are too long
76
src/test/basics/FileUtilities_test.cpp
Normal file
76
src/test/basics/FileUtilities_test.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2018 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or 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 <ripple/basics/ByteUtilities.h>
|
||||
#include <ripple/basics/FileUtilities.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <test/unit_test/FileDirGuard.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class FileUtilities_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void testGetFileContents()
|
||||
{
|
||||
using namespace ripple::test::detail;
|
||||
using namespace boost::system;
|
||||
|
||||
constexpr const char* expectedContents =
|
||||
"This file is very short. That's all we need.";
|
||||
|
||||
FileDirGuard file(*this, "test_file", "test.txt",
|
||||
expectedContents);
|
||||
|
||||
error_code ec;
|
||||
auto const path = file.file();
|
||||
|
||||
{
|
||||
// Test with no max
|
||||
auto const good = getFileContents(ec, path);
|
||||
BEAST_EXPECT(!ec);
|
||||
BEAST_EXPECT(good == expectedContents);
|
||||
}
|
||||
|
||||
{
|
||||
// Test with large max
|
||||
auto const good = getFileContents(ec, path, kilobytes(1));
|
||||
BEAST_EXPECT(!ec);
|
||||
BEAST_EXPECT(good == expectedContents);
|
||||
}
|
||||
|
||||
{
|
||||
// Test with small max
|
||||
auto const bad = getFileContents(ec, path, 16);
|
||||
BEAST_EXPECT(ec && ec.value() ==
|
||||
boost::system::errc::file_too_large);
|
||||
BEAST_EXPECT(bad.empty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void run () override
|
||||
{
|
||||
testGetFileContents();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(FileUtilities, ripple_basics, ripple);
|
||||
|
||||
} // ripple
|
||||
@@ -80,6 +80,10 @@ public:
|
||||
BEAST_EXPECT(parseUrl (pUrl, "scheme://[::1]:123/path"));
|
||||
BEAST_EXPECT(*pUrl.port == 123);
|
||||
BEAST_EXPECT(pUrl.domain == "::1");
|
||||
BEAST_EXPECT(parseUrl(pUrl, "nodomain:///path/path/path"));
|
||||
BEAST_EXPECT(pUrl.scheme == "nodomain");
|
||||
BEAST_EXPECT(pUrl.domain.empty());
|
||||
BEAST_EXPECT(pUrl.path == "/path/path/path");
|
||||
}
|
||||
|
||||
void testToString ()
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <ripple/core/ConfigSections.h>
|
||||
#include <ripple/server/Port.h>
|
||||
#include <test/jtx/TestSuite.h>
|
||||
#include <test/unit_test/FileDirGuard.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <fstream>
|
||||
@@ -121,88 +122,12 @@ backend=sqlite
|
||||
configContentsTemplate % dbPathSection % valFileSection);
|
||||
}
|
||||
|
||||
/**
|
||||
Write a config file and remove when done.
|
||||
*/
|
||||
class ConfigGuard
|
||||
{
|
||||
private:
|
||||
bool rmSubDir_{false};
|
||||
|
||||
protected:
|
||||
using path = boost::filesystem::path;
|
||||
path subDir_;
|
||||
beast::unit_test::suite& test_;
|
||||
|
||||
auto rmDir (path const& toRm)
|
||||
{
|
||||
if (is_directory (toRm) && is_empty (toRm))
|
||||
remove (toRm);
|
||||
else
|
||||
test_.log << "Expected " << toRm.string ()
|
||||
<< " to be an empty existing directory." << std::endl;
|
||||
}
|
||||
|
||||
public:
|
||||
ConfigGuard (beast::unit_test::suite& test, path subDir,
|
||||
bool useCounter = true)
|
||||
: subDir_ (std::move (subDir))
|
||||
, test_ (test)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
{
|
||||
static auto subDirCounter = 0;
|
||||
if (useCounter)
|
||||
subDir_ += std::to_string(++subDirCounter);
|
||||
if (!exists (subDir_))
|
||||
{
|
||||
create_directory (subDir_);
|
||||
rmSubDir_ = true;
|
||||
}
|
||||
else if (is_directory (subDir_))
|
||||
rmSubDir_ = false;
|
||||
else
|
||||
{
|
||||
// Cannot run the test. Someone created a file where we want to
|
||||
// put our directory
|
||||
Throw<std::runtime_error> (
|
||||
"Cannot create directory: " + subDir_.string ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~ConfigGuard ()
|
||||
{
|
||||
try
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
|
||||
if (rmSubDir_)
|
||||
rmDir (subDir_);
|
||||
else
|
||||
test_.log << "Skipping rm dir: "
|
||||
<< subDir_.string () << std::endl;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
// if we throw here, just let it die.
|
||||
test_.log << "Error in ~ConfigGuard: " << e.what () << std::endl;
|
||||
};
|
||||
}
|
||||
|
||||
path const& subdir() const
|
||||
{
|
||||
return subDir_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Write a rippled config file and remove when done.
|
||||
*/
|
||||
class RippledCfgGuard : public ConfigGuard
|
||||
class RippledCfgGuard : public ripple::test::detail::FileDirGuard
|
||||
{
|
||||
private:
|
||||
path configFile_;
|
||||
path dataDir_;
|
||||
|
||||
bool rmDataDir_{false};
|
||||
@@ -214,28 +139,17 @@ public:
|
||||
path subDir, path const& dbPath,
|
||||
path const& validatorsFile,
|
||||
bool useCounter = true)
|
||||
: ConfigGuard (test, std::move (subDir), useCounter)
|
||||
: FileDirGuard(test, std::move (subDir),
|
||||
path (Config::configFileName),
|
||||
configContents (dbPath.string (), validatorsFile.string ()),
|
||||
useCounter)
|
||||
, dataDir_ (dbPath)
|
||||
{
|
||||
if (dbPath.empty ())
|
||||
dataDir_ = subDir_ / path (Config::databaseDirName);
|
||||
|
||||
configFile_ = subDir_ / path (Config::configFileName);
|
||||
|
||||
if (!exists (configFile_))
|
||||
{
|
||||
std::ofstream o (configFile_.string ());
|
||||
o << configContents (dbPath.string (), validatorsFile.string ());
|
||||
}
|
||||
else
|
||||
{
|
||||
Throw<std::runtime_error> (
|
||||
"Refusing to overwrite existing config file: " +
|
||||
configFile_.string ());
|
||||
}
|
||||
dataDir_ = subdir() / path (Config::databaseDirName);
|
||||
|
||||
rmDataDir_ = !exists (dataDir_);
|
||||
config_.setup (configFile_.string (), /*bQuiet*/ true,
|
||||
config_.setup (file_.string (), /*bQuiet*/ true,
|
||||
/* bSilent */ false, /* bStandalone */ false);
|
||||
}
|
||||
|
||||
@@ -246,7 +160,7 @@ public:
|
||||
|
||||
std::string configFile() const
|
||||
{
|
||||
return configFile_.string();
|
||||
return file().string();
|
||||
}
|
||||
|
||||
bool dataDirExists () const
|
||||
@@ -256,7 +170,7 @@ public:
|
||||
|
||||
bool configFileExists () const
|
||||
{
|
||||
return boost::filesystem::exists (configFile_);
|
||||
return fileExists();
|
||||
}
|
||||
|
||||
~RippledCfgGuard ()
|
||||
@@ -264,12 +178,6 @@ public:
|
||||
try
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
if (!boost::filesystem::exists (configFile_))
|
||||
test_.log << "Expected " << configFile_.string ()
|
||||
<< " to be an existing file." << std::endl;
|
||||
else
|
||||
remove (configFile_);
|
||||
|
||||
if (rmDataDir_)
|
||||
rmDir (dataDir_);
|
||||
else
|
||||
@@ -314,65 +222,37 @@ moreripplevalidators.net
|
||||
/**
|
||||
Write a validators.txt file and remove when done.
|
||||
*/
|
||||
class ValidatorsTxtGuard : public ConfigGuard
|
||||
class ValidatorsTxtGuard : public test::detail::FileDirGuard
|
||||
{
|
||||
private:
|
||||
path validatorsFile_;
|
||||
|
||||
public:
|
||||
ValidatorsTxtGuard (beast::unit_test::suite& test,
|
||||
path subDir, path const& validatorsFileName,
|
||||
bool useCounter = true)
|
||||
: ConfigGuard (test, std::move (subDir), useCounter)
|
||||
: FileDirGuard (test, std::move (subDir),
|
||||
path (
|
||||
validatorsFileName.empty () ? Config::validatorsFileName :
|
||||
validatorsFileName),
|
||||
valFileContents (),
|
||||
useCounter)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
validatorsFile_ = current_path () / subDir_ / path (
|
||||
validatorsFileName.empty () ? Config::validatorsFileName :
|
||||
validatorsFileName);
|
||||
|
||||
if (!exists (validatorsFile_))
|
||||
{
|
||||
std::ofstream o (validatorsFile_.string ());
|
||||
o << valFileContents ();
|
||||
}
|
||||
else
|
||||
{
|
||||
Throw<std::runtime_error> (
|
||||
"Refusing to overwrite existing config file: " +
|
||||
validatorsFile_.string ());
|
||||
}
|
||||
}
|
||||
|
||||
bool validatorsFileExists () const
|
||||
{
|
||||
return boost::filesystem::exists (validatorsFile_);
|
||||
return fileExists();
|
||||
}
|
||||
|
||||
std::string validatorsFile () const
|
||||
{
|
||||
return validatorsFile_.string ();
|
||||
return absolute(file()).string();
|
||||
}
|
||||
|
||||
~ValidatorsTxtGuard ()
|
||||
{
|
||||
try
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
if (!boost::filesystem::exists (validatorsFile_))
|
||||
test_.log << "Expected " << validatorsFile_.string ()
|
||||
<< " to be an existing file." << std::endl;
|
||||
else
|
||||
remove (validatorsFile_);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
// if we throw here, just let it die.
|
||||
test_.log << "Error in ~ValidatorsTxtGuard: "
|
||||
<< e.what () << std::endl;
|
||||
};
|
||||
}
|
||||
};
|
||||
} // detail
|
||||
|
||||
class Config_test final : public TestSuite
|
||||
{
|
||||
private:
|
||||
@@ -440,7 +320,7 @@ port_wss_admin
|
||||
{
|
||||
// read from file absolute path
|
||||
auto const cwd = current_path ();
|
||||
detail::ConfigGuard const g0(*this, "test_db");
|
||||
ripple::test::detail::DirGuard const g0(*this, "test_db");
|
||||
path const dataDirRel ("test_data_dir");
|
||||
path const dataDirAbs(cwd / g0.subdir () / dataDirRel);
|
||||
detail::RippledCfgGuard const g (*this, g0.subdir(),
|
||||
@@ -767,9 +647,9 @@ trustthesevalidators.gov
|
||||
{
|
||||
detail::RippledCfgGuard const cfg(*this, "testSetup",
|
||||
explicitPath ? "test_db" : "", "");
|
||||
/* ConfigGuard has a Config object that gets loaded on construction,
|
||||
but Config::setup is not reentrant, so we need a fresh config
|
||||
for every test case, so ignore it.
|
||||
/* RippledCfgGuard has a Config object that gets loaded on
|
||||
construction, but Config::setup is not reentrant, so we
|
||||
need a fresh config for every test case, so ignore it.
|
||||
*/
|
||||
{
|
||||
Config config;
|
||||
|
||||
169
src/test/unit_test/FileDirGuard.h
Normal file
169
src/test/unit_test/FileDirGuard.h
Normal file
@@ -0,0 +1,169 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2018 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef TEST_UNIT_TEST_DIRGUARD_H
|
||||
#define TEST_UNIT_TEST_DIRGUARD_H
|
||||
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <test/jtx/TestSuite.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
Create a directory and remove it when it's done
|
||||
*/
|
||||
class DirGuard
|
||||
{
|
||||
protected:
|
||||
using path = boost::filesystem::path;
|
||||
|
||||
private:
|
||||
path subDir_;
|
||||
bool rmSubDir_{false};
|
||||
|
||||
protected:
|
||||
beast::unit_test::suite& test_;
|
||||
|
||||
auto rmDir (path const& toRm)
|
||||
{
|
||||
if (is_directory (toRm) && is_empty (toRm))
|
||||
remove (toRm);
|
||||
else
|
||||
test_.log << "Expected " << toRm.string ()
|
||||
<< " to be an empty existing directory." << std::endl;
|
||||
}
|
||||
|
||||
public:
|
||||
DirGuard (beast::unit_test::suite& test, path subDir,
|
||||
bool useCounter = true)
|
||||
: subDir_ (std::move (subDir))
|
||||
, test_ (test)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
|
||||
static auto subDirCounter = 0;
|
||||
if (useCounter)
|
||||
subDir_ += std::to_string(++subDirCounter);
|
||||
if (!exists (subDir_))
|
||||
{
|
||||
create_directory (subDir_);
|
||||
rmSubDir_ = true;
|
||||
}
|
||||
else if (is_directory (subDir_))
|
||||
rmSubDir_ = false;
|
||||
else
|
||||
{
|
||||
// Cannot run the test. Someone created a file where we want to
|
||||
// put our directory
|
||||
Throw<std::runtime_error> (
|
||||
"Cannot create directory: " + subDir_.string ());
|
||||
}
|
||||
}
|
||||
|
||||
~DirGuard ()
|
||||
{
|
||||
try
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
|
||||
if (rmSubDir_)
|
||||
rmDir (subDir_);
|
||||
else
|
||||
test_.log << "Skipping rm dir: "
|
||||
<< subDir_.string () << std::endl;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
// if we throw here, just let it die.
|
||||
test_.log << "Error in ~DirGuard: " << e.what () << std::endl;
|
||||
};
|
||||
}
|
||||
|
||||
path const& subdir() const
|
||||
{
|
||||
return subDir_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Write a file in a directory and remove when done
|
||||
*/
|
||||
class FileDirGuard : public DirGuard
|
||||
{
|
||||
protected:
|
||||
path const file_;
|
||||
|
||||
public:
|
||||
FileDirGuard(beast::unit_test::suite& test,
|
||||
path subDir, path file, std::string const& contents,
|
||||
bool useCounter = true)
|
||||
: DirGuard(test, subDir, useCounter)
|
||||
, file_(file.is_absolute() ? file : subdir() / file)
|
||||
{
|
||||
if (!exists (file_))
|
||||
{
|
||||
std::ofstream o (file_.string ());
|
||||
o << contents;
|
||||
}
|
||||
else
|
||||
{
|
||||
Throw<std::runtime_error> (
|
||||
"Refusing to overwrite existing file: " +
|
||||
file_.string ());
|
||||
}
|
||||
}
|
||||
|
||||
~FileDirGuard ()
|
||||
{
|
||||
try
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
if (!exists (file_))
|
||||
test_.log << "Expected " << file_.string ()
|
||||
<< " to be an existing file." << std::endl;
|
||||
else
|
||||
remove (file_);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
// if we throw here, just let it die.
|
||||
test_.log << "Error in ~FileGuard: "
|
||||
<< e.what () << std::endl;
|
||||
};
|
||||
}
|
||||
|
||||
path const& file() const
|
||||
{
|
||||
return file_;
|
||||
}
|
||||
|
||||
bool fileExists () const
|
||||
{
|
||||
return boost::filesystem::exists (file_);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TEST_UNIT_TEST_DIRGUARD_H
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <test/basics/Buffer_test.cpp>
|
||||
#include <test/basics/contract_test.cpp>
|
||||
#include <test/basics/DetectCrash_test.cpp>
|
||||
#include <test/basics/FileUtilities_test.cpp>
|
||||
#include <test/basics/hardened_hash_test.cpp>
|
||||
#include <test/basics/KeyCache_test.cpp>
|
||||
#include <test/basics/mulDiv_test.cpp>
|
||||
|
||||
Reference in New Issue
Block a user