1
0
Fork 0

issue #3504: export database name with dump for easier restore (#4216)

This commit is contained in:
Jan 2018-01-05 14:52:56 +01:00 committed by GitHub
parent 3b6e85f4a6
commit 956902a5a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 14 deletions

View File

@ -1,6 +1,15 @@
devel
-----
* issue #3504: added option `--force-same-database` for arangorestore
with this option set to true, it is possible to make any arangorestore attempt
fail if the specified target database does not match the database name
specified in the source dump's "dump.json" file. it can thus be used to
prevent restoring data into the "wrong" database
The option is set to `false` by default to ensure backwards-compatibility
* UI: updated dygraph js library to version 2.1.0
* added C++ implementation for AQL function `SHA512()`

View File

@ -41,6 +41,16 @@ target database, the username and passwords passed to _arangorestore_ (in option
*--server.username* and *--server.password*) will be used to create an initial user for the
new database.
The option `--force-same-database` allows restricting arangorestore operations to a
database with the same name as in the source dump's "dump.json" file. It can thus be used
to prevent restoring data into a "wrong" database by accident.
For example, if a dump was taken from database `a`, and the restore is attempted into
database `b`, then with the `--force-same-database` option set to `true`, arangorestore
will abort instantly.
The `--force-same-database` option is set to `false` by default to ensure backwards-compatibility.
Here's an example of reloading data to a non-standard endpoint, using a dedicated
[database name](../Appendix/Glossary.md#database-name):
@ -120,16 +130,12 @@ intentional (normally, every server should create its own *_rev* values) there m
situations when it is required to re-use the exact same *_rev* values for the reloaded data.
This can be achieved by setting the *--recycle-ids* parameter to *true*:
unix> arangorestore --collection myusers --collection myvalues --recycle-ids true --input-directory "dump"
unix> arangorestore --collection myusers --collection myvalues --input-directory "dump"
Note that setting *--recycle-ids* to *true* will also cause collections to be (re-)created in
the target database with the exact same collection id as in the input directory. Any potentially
existing collection in the target database with the same collection id will then be dropped.
Setting *--recycle-ids* to *false* or omitting it will only use the [collection name](../Appendix/Glossary.md#collection-name) from the
input directory and allow the target database to create the collection with a different id
(though with the same name) than in the input directory.
### Reloading Data into a different Collection
With some creativity you can use _arangodump_ and _arangorestore_ to transfer data from one

View File

@ -30,6 +30,7 @@
#include "ApplicationFeatures/ApplicationServer.h"
#include "Basics/FileUtils.h"
#include "Basics/OpenFilesTracker.h"
#include "Basics/Result.h"
#include "Basics/StringUtils.h"
#include "Basics/VelocyPackHelper.h"
#include "Basics/files.h"
@ -62,7 +63,7 @@ RestoreFeature::RestoreFeature(application_features::ApplicationServer* server,
_chunkSize(1024 * 1024 * 8),
_includeSystemCollections(false),
_createDatabase(false),
_inputDirectory(),
_forceSameDatabase(false),
_importData(true),
_importStructure(true),
_progress(true),
@ -109,6 +110,10 @@ void RestoreFeature::collectOptions(
options->addOption("--create-database",
"create the target database if it does not exist",
new BooleanParameter(&_createDatabase));
options->addOption("--force-same-database",
"force usage of the same database name as in the source dump.json file",
new BooleanParameter(&_forceSameDatabase));
options->addOption("--input-directory", "input directory",
new StringParameter(&_inputDirectory));
@ -385,7 +390,7 @@ static bool SortCollections(VPackBuilder const& l, VPackBuilder const& r) {
return strcasecmp(leftName.c_str(), rightName.c_str()) < 0;
}
int RestoreFeature::processInputDirectory(std::string& errorMsg) {
Result RestoreFeature::readEncryptionInfo() {
std::string encryptionType;
try {
std::string const encryptionFilename = FileUtils::buildFilename(_inputDirectory, "ENCRYPTION");
@ -395,11 +400,9 @@ int RestoreFeature::processInputDirectory(std::string& errorMsg) {
encryptionType = "none";
}
} catch (basics::Exception const& ex) {
errorMsg = ex.what();
return ex.code();
return Result(ex.code(), ex.what());
} catch (std::exception const& ex) {
errorMsg = ex.what();
return TRI_ERROR_INTERNAL;
return Result(TRI_ERROR_INTERNAL, ex.what());
} catch (...) {
// file not found etc.
}
@ -415,6 +418,46 @@ int RestoreFeature::processInputDirectory(std::string& errorMsg) {
#endif
}
return Result();
}
Result RestoreFeature::readDumpInfo() {
std::string databaseName;
try {
std::string const fqn = _inputDirectory + TRI_DIR_SEPARATOR_STR + "dump.json";
#ifdef USE_ENTERPRISE
VPackBuilder fileContentBuilder =
(_encryption != nullptr)
? _encryption->velocyPackFromFile(fqn)
: basics::VelocyPackHelper::velocyPackFromFile(fqn);
#else
VPackBuilder fileContentBuilder =
basics::VelocyPackHelper::velocyPackFromFile(fqn);
#endif
VPackSlice const fileContent = fileContentBuilder.slice();
databaseName = fileContent.get("database").copyString();
} catch (...) {
// the above may go wrong for several reasons
}
if (!databaseName.empty()) {
std::cout << "Database name in source dump is '" << databaseName << "'" << std::endl;
}
ClientFeature* client =
application_features::ApplicationServer::getFeature<ClientFeature>(
"Client");
if (_forceSameDatabase && databaseName != client->databaseName()) {
return Result(TRI_ERROR_BAD_PARAMETER, std::string("database name in dump.json ('") + databaseName + "') does not match specified database name ('" + client->databaseName() + "')");
}
return Result();
}
int RestoreFeature::processInputDirectory(std::string& errorMsg) {
// create a lookup table for collections
std::map<std::string, bool> restrictList;
for (size_t i = 0; i < _collections.size(); ++i) {
@ -733,6 +776,26 @@ void RestoreFeature::start() {
_httpClient->params().setUserNamePassword("/", client->username(),
client->password());
// read encryption info
{
Result r = readEncryptionInfo();
if (r.fail()) {
LOG_TOPIC(FATAL, arangodb::Logger::FIXME) << r.errorMessage();
FATAL_ERROR_EXIT();
}
}
// read dump info
{
Result r = readDumpInfo();
if (r.fail()) {
LOG_TOPIC(FATAL, arangodb::Logger::FIXME) << r.errorMessage();
FATAL_ERROR_EXIT();
}
}
int err = TRI_ERROR_NO_ERROR;
std::string versionString = _httpClient->getServerVersion(&err);
@ -793,9 +856,8 @@ void RestoreFeature::start() {
<< _httpClient->getEndpointSpecification() << "'" << std::endl;
}
std::string errorMsg = "";
int res;
std::string errorMsg;
try {
res = processInputDirectory(errorMsg);
} catch (std::exception const& ex) {

View File

@ -50,10 +50,11 @@ class RestoreFeature final : public application_features::ApplicationFeature,
private:
std::vector<std::string> _collections;
std::string _inputDirectory;
uint64_t _chunkSize;
bool _includeSystemCollections;
bool _createDatabase;
std::string _inputDirectory;
bool _forceSameDatabase;
bool _importData;
bool _importStructure;
bool _progress;
@ -71,6 +72,8 @@ class RestoreFeature final : public application_features::ApplicationFeature,
int sendRestoreIndexes(VPackSlice const& slice, std::string& errorMsg);
int sendRestoreData(std::string const& cname, char const* buffer,
size_t bufferSize, std::string& errorMsg);
Result readEncryptionInfo();
Result readDumpInfo();
int processInputDirectory(std::string& errorMsg);
ssize_t readData(int fd, char* data, size_t len);
void beginDecryption(int fd);