diff --git a/arangod/Pregel/Algos/LabelPropagation.cpp b/arangod/Pregel/Algos/LabelPropagation.cpp index 9c5788039c..bc68c6e760 100644 --- a/arangod/Pregel/Algos/LabelPropagation.cpp +++ b/arangod/Pregel/Algos/LabelPropagation.cpp @@ -81,8 +81,7 @@ struct LPComputation : public VertexComputation { uint64_t newCommunity = mutableVertexData()->currentCommunity; if (messages.size() == 1) { newCommunity = std::min(**messages, newCommunity); - } - if (messages.size() > 1) { + } else if (messages.size() > 1) { newCommunity = mostFrequent(messages); } diff --git a/arangod/RestServer/InitDatabaseFeature.cpp b/arangod/RestServer/InitDatabaseFeature.cpp index 89a4762ed4..ed7d8005ac 100644 --- a/arangod/RestServer/InitDatabaseFeature.cpp +++ b/arangod/RestServer/InitDatabaseFeature.cpp @@ -22,16 +22,17 @@ #include "InitDatabaseFeature.h" +#include + #include "Basics/FileUtils.h" #include "Basics/terminal-utils.h" +#include "Cluster/ServerState.h" #include "Logger/Logger.h" #include "Logger/LoggerFeature.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" #include "RestServer/DatabasePathFeature.h" -#include - using namespace arangodb; using namespace arangodb::application_features; using namespace arangodb::basics; @@ -71,6 +72,7 @@ void InitDatabaseFeature::validateOptions( if (_initDatabase || _restoreAdmin) { ApplicationServer::forceDisableFeatures(_nonServerFeatures); + ServerState::instance()->setRole(ServerState::ROLE_SINGLE); } } @@ -140,7 +142,7 @@ std::string InitDatabaseFeature::readPassword(std::string const& message) { void InitDatabaseFeature::checkEmptyDatabase() { auto database = ApplicationServer::getFeature("DatabasePath"); std::string path = database->directory(); - std::string journals = database->subdirectoryName("journals"); + std::string serverFile = database->subdirectoryName("SERVER"); bool empty = false; std::string message; @@ -153,10 +155,10 @@ void InitDatabaseFeature::checkEmptyDatabase() { goto doexit; } - if (FileUtils::exists(journals)) { - if (!FileUtils::isDirectory(journals)) { + if (FileUtils::exists(serverFile)) { + if (FileUtils::isDirectory(serverFile)) { message = - "database journals path '" + journals + "' is not a directory"; + "database SERVER '" + serverFile + "' is not a file"; code = EXIT_FAILURE; goto doexit; } @@ -168,7 +170,7 @@ void InitDatabaseFeature::checkEmptyDatabase() { } if (!empty) { - message = "database already initialized, refusing to change root user"; + message = "database already initialized, refusing to initialize it again"; goto doexit; } diff --git a/arangod/RestServer/UpgradeFeature.cpp b/arangod/RestServer/UpgradeFeature.cpp index fdcedd45df..3162b6a39b 100644 --- a/arangod/RestServer/UpgradeFeature.cpp +++ b/arangod/RestServer/UpgradeFeature.cpp @@ -23,6 +23,7 @@ #include "UpgradeFeature.h" #include "Cluster/ClusterFeature.h" +#include "GeneralServer/AuthenticationFeature.h" #include "Logger/Logger.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" @@ -101,15 +102,46 @@ void UpgradeFeature::validateOptions(std::shared_ptr options) { void UpgradeFeature::start() { auto init = ApplicationServer::getFeature("InitDatabase"); + AuthInfo *ai = AuthenticationFeature::INSTANCE->authInfo(); // upgrade the database if (_upgradeCheck) { - upgradeDatabase(init->defaultPassword()); + upgradeDatabase(); + + if (!init->restoreAdmin() && !init->defaultPassword().empty()) { + ai->updateUser("root", [&](AuthUserEntry& entry) { + entry.updatePassword(init->defaultPassword()); + }); + } } // change admin user if (init->restoreAdmin()) { - changeAdminPassword(init->defaultPassword()); + Result res; + + res = ai->removeAllUsers(); + + if (res.fail()) { + LOG_TOPIC(WARN, arangodb::Logger::FIXME) << "failed to create clear users: " + << res.errorMessage(); + *_result = EXIT_FAILURE; + return; + } + + res = ai->storeUser(true, "root", init->defaultPassword(), true); + + if (res.fail() && res.errorNumber() == TRI_ERROR_USER_NOT_FOUND) { + res = ai->storeUser(false, "root", init->defaultPassword(), true); + } + + if (res.fail()) { + LOG_TOPIC(WARN, arangodb::Logger::FIXME) << "failed to create root user: " + << res.errorMessage(); + *_result = EXIT_FAILURE; + return; + } + + *_result = EXIT_SUCCESS; } // and force shutdown @@ -117,71 +149,12 @@ void UpgradeFeature::start() { if (init->isInitDatabase()) { *_result = EXIT_SUCCESS; } - + server()->beginShutdown(); } } -void UpgradeFeature::changeAdminPassword(std::string const& defaultPassword) { - LOG_TOPIC(TRACE, arangodb::Logger::FIXME) << "starting to restore admin user"; - - auto* systemVocbase = DatabaseFeature::DATABASE->systemDatabase(); - - // enter context and isolate - { - V8Context* context = - V8DealerFeature::DEALER->enterContext(systemVocbase, true, 0); - - if (context == nullptr) { - LOG_TOPIC(FATAL, arangodb::Logger::FIXME) << "could not enter context #0"; - FATAL_ERROR_EXIT(); - } - - TRI_DEFER(V8DealerFeature::DEALER->exitContext(context)); - - { - v8::HandleScope scope(context->_isolate); - auto localContext = - v8::Local::New(context->_isolate, context->_context); - localContext->Enter(); - - { - v8::Context::Scope contextScope(localContext); - - // run upgrade script - LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "running admin recreation script"; - - // special check script to be run just once in first thread (not in - // all) but for all databases - v8::HandleScope scope(context->_isolate); - - v8::Handle args = v8::Object::New(context->_isolate); - - args->Set(TRI_V8_ASCII_STRING2(context->_isolate, "password"), - TRI_V8_STD_STRING2(context->_isolate, defaultPassword)); - - localContext->Global()->Set( - TRI_V8_ASCII_STRING2(context->_isolate, "UPGRADE_ARGS"), args); - - auto startupLoader = V8DealerFeature::DEALER->startupLoader(); - - startupLoader->executeGlobalScript(context->_isolate, localContext, - "server/restore-admin-user.js"); - } - - // finally leave the context. otherwise v8 will crash with assertion - // failure when we delete the context locker below - localContext->Exit(); - } - } - - // and return from the context - LOG_TOPIC(TRACE, arangodb::Logger::FIXME) << "finished to restore admin user"; - - *_result = EXIT_SUCCESS; -} - -void UpgradeFeature::upgradeDatabase(std::string const& defaultPassword) { +void UpgradeFeature::upgradeDatabase() { LOG_TOPIC(TRACE, arangodb::Logger::FIXME) << "starting database init/upgrade"; DatabaseFeature* databaseFeature = application_features::ApplicationServer::getFeature("Database"); @@ -224,8 +197,6 @@ void UpgradeFeature::upgradeDatabase(std::string const& defaultPassword) { args->Set(TRI_V8_ASCII_STRING2(context->_isolate, "upgrade"), v8::Boolean::New(context->_isolate, _upgrade)); - args->Set(TRI_V8_ASCII_STRING2(context->_isolate, "password"), - TRI_V8_STD_STRING2(context->_isolate, defaultPassword)); localContext->Global()->Set( TRI_V8_ASCII_STRING2(context->_isolate, "UPGRADE_ARGS"), args); diff --git a/arangod/RestServer/UpgradeFeature.h b/arangod/RestServer/UpgradeFeature.h index 9d7df0b9b3..3f519624c8 100644 --- a/arangod/RestServer/UpgradeFeature.h +++ b/arangod/RestServer/UpgradeFeature.h @@ -41,8 +41,7 @@ class UpgradeFeature final : public application_features::ApplicationFeature { bool _upgradeCheck; private: - void changeAdminPassword(std::string const& defaultPassword); - void upgradeDatabase(std::string const& defaultPassword); + void upgradeDatabase(); private: int* _result; diff --git a/arangod/V8Server/v8-collection.cpp b/arangod/V8Server/v8-collection.cpp index dbb64aee61..90bb978d7e 100644 --- a/arangod/V8Server/v8-collection.cpp +++ b/arangod/V8Server/v8-collection.cpp @@ -1026,7 +1026,8 @@ static void JS_DropVocbaseCol(v8::FunctionCallbackInfo const& args) { } } - if (auth->isActive()) { + if (auth->isActive() && (ServerState::instance()->isCoordinator() || + !ServerState::instance()->isRunningInCluster())) { auth->authInfo()->enumerateUsers([&](AuthUserEntry& entry) { entry.removeCollection(dbname, collName); }); @@ -2766,17 +2767,6 @@ static void JS_TruncateVocbaseCol( TRI_V8_THROW_EXCEPTION_INTERNAL("cannot extract collection"); } - AuthenticationFeature* auth = FeatureCacheFeature::instance()->authenticationFeature(); - if (auth->isActive() && ExecContext::CURRENT != nullptr) { - CollectionNameResolver resolver(collection->vocbase()); - std::string const cName = resolver.getCollectionNameCluster(collection->cid()); - AuthLevel level = auth->canUseCollection(ExecContext::CURRENT->user(), - collection->vocbase()->name(), cName); - if (level != AuthLevel::RW) { - TRI_V8_THROW_EXCEPTION(TRI_ERROR_FORBIDDEN); - } - } - // optionally specify non trx remove bool unsafeTruncate = false; if (args.Length() > 0) { diff --git a/arangod/V8Server/v8-vocindex.cpp b/arangod/V8Server/v8-vocindex.cpp index f89a679dfa..e2c7c7976c 100644 --- a/arangod/V8Server/v8-vocindex.cpp +++ b/arangod/V8Server/v8-vocindex.cpp @@ -304,8 +304,11 @@ static void CreateVocBase(v8::FunctionCallbackInfo const& args, if (result.IsEmpty()) { TRI_V8_THROW_EXCEPTION_MEMORY(); } + // in case of success we grant the creating user RW access - if (auth->isActive() && ExecContext::CURRENT != nullptr) { + if (auth->isActive() && ExecContext::CURRENT != nullptr && + (ServerState::instance()->isCoordinator() || + !ServerState::instance()->isRunningInCluster())) { // this should not fail, we can not get here without database RW access auth->authInfo()->updateUser(ExecContext::CURRENT->user(), [&](AuthUserEntry& entry) { diff --git a/arangod/VocBase/AuthInfo.cpp b/arangod/VocBase/AuthInfo.cpp index a68d37a6ea..90ddaa67e1 100644 --- a/arangod/VocBase/AuthInfo.cpp +++ b/arangod/VocBase/AuthInfo.cpp @@ -229,18 +229,21 @@ void AuthInfo::loadFromDB() { MUTEX_LOCKER(locker, _loadFromDBLock); + // double check to be sure after we got the lock if (!_outdated) { return; } auto role = ServerState::instance()->getRole(); + if (role != ServerState::ROLE_SINGLE && role != ServerState::ROLE_COORDINATOR) { + TRI_ASSERT(false); _outdated = false; return; } - { + if (_authInfo.empty()) {// should only work once WRITE_LOCKER(writeLocker, _authInfoLock); insertInitial(); } @@ -252,15 +255,16 @@ void AuthInfo::loadFromDB() { if (builder) { VPackSlice usersSlice = builder->slice(); - if (usersSlice.length() == 0) { - insertInitial(); - } else { + if (usersSlice.length() != 0) { parseUsers(usersSlice); } - _outdated = false; - } else { + } + + if (_authInfo.empty()) { insertInitial(); } + + _outdated = false; } // private, must be called with _authInfoLock in write mode @@ -461,6 +465,7 @@ static Result UpdateUser(VPackSlice const& user) { Result AuthInfo::enumerateUsers( std::function const& func) { + loadFromDB(); // we require an consisten view on the user object { WRITE_LOCKER(guard, _authInfoLock); @@ -484,6 +489,7 @@ Result AuthInfo::updateUser(std::string const& user, if (user.empty()) { return TRI_ERROR_USER_NOT_FOUND; } + loadFromDB(); VPackBuilder data; { // we require an consisten view on the user object WRITE_LOCKER(guard, _authInfoLock); @@ -506,6 +512,7 @@ Result AuthInfo::updateUser(std::string const& user, Result AuthInfo::accessUser(std::string const& user, std::function const& func) { + loadFromDB(); READ_LOCKER(guard, _authInfoLock); auto it = _authInfo.find(user); if (it != _authInfo.end()) { @@ -516,6 +523,7 @@ Result AuthInfo::accessUser(std::string const& user, } VPackBuilder AuthInfo::serializeUser(std::string const& user) { + loadFromDB(); VPackBuilder doc = QueryUser(_queryRegistry, user); VPackBuilder result; if (!doc.isEmpty()) { @@ -524,6 +532,42 @@ VPackBuilder AuthInfo::serializeUser(std::string const& user) { return result; } +static Result removeUserInternal(AuthUserEntry const& entry) { + TRI_ASSERT(!entry.key().empty()); + TRI_vocbase_t* vocbase = DatabaseFeature::DATABASE->systemDatabase(); + if (vocbase == nullptr) { + return Result(TRI_ERROR_INTERNAL); + } + + // we cannot set this execution context, otherwise the transaction + // will ask us again for permissions and we get a deadlock + ExecContext* oldExe = ExecContext::CURRENT; + ExecContext::CURRENT = nullptr; + TRI_DEFER(ExecContext::CURRENT = oldExe); + + std::shared_ptr ctx( + new transaction::StandaloneContext(vocbase)); + SingleCollectionTransaction trx(ctx, TRI_COL_NAME_USERS, + AccessMode::Type::WRITE); + + trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); + + Result res = trx.begin(); + if (res.ok()) { + VPackBuilder builder; + { + VPackObjectBuilder guard(&builder); + builder.add(StaticStrings::KeyString, VPackValue(entry.key())); + // TODO maybe protect with a revision ID? + } + + OperationResult result = + trx.remove(TRI_COL_NAME_USERS, builder.slice(), OperationOptions()); + res = trx.finish(result.code); + } + return res; +} + Result AuthInfo::removeUser(std::string const& user) { if (user.empty()) { return TRI_ERROR_USER_NOT_FOUND; @@ -534,57 +578,51 @@ Result AuthInfo::removeUser(std::string const& user) { loadFromDB(); WRITE_LOCKER(guard, _authInfoLock); - auto it = _authInfo.find(user); + auto const& it = _authInfo.find(user); if (it == _authInfo.end()) { - return Result(TRI_ERROR_USER_NOT_FOUND); + return TRI_ERROR_USER_NOT_FOUND; } - TRI_ASSERT(!it->second.key().empty()); - - TRI_vocbase_t* vocbase = DatabaseFeature::DATABASE->systemDatabase(); - if (vocbase == nullptr) { - return Result(TRI_ERROR_INTERNAL); - } - - // we cannot set this execution context, otherwise the transaction - // will ask us again for permissions and we get a deadlock - ExecContext* oldExe = ExecContext::CURRENT; - ExecContext::CURRENT = nullptr; - TRI_DEFER(ExecContext::CURRENT = oldExe); - - std::shared_ptr ctx( - new transaction::StandaloneContext(vocbase)); - SingleCollectionTransaction trx(ctx, TRI_COL_NAME_USERS, - AccessMode::Type::WRITE); - - trx.addHint(transaction::Hints::Hint::SINGLE_OPERATION); - - Result res = trx.begin(); + + Result res = removeUserInternal(it->second); if (res.ok()) { - VPackBuilder builder; - { - VPackObjectBuilder guard(&builder); - builder.add(StaticStrings::KeyString, VPackValue(it->second.key())); - // TODO maybe protect with a revision ID? - } - - OperationResult result = - trx.remove(TRI_COL_NAME_USERS, builder.slice(), OperationOptions()); - res = trx.finish(result.code); - if (res.ok()) { - _authInfo.erase(it); - reloadAllUsers(); - } + _authInfo.erase(it); + reloadAllUsers(); } return res; } +Result AuthInfo::removeAllUsers() { + loadFromDB(); + + WRITE_LOCKER(guard, _authInfoLock); + Result res; + for (auto const& pair : _authInfo) { + res = removeUserInternal(pair.second); + if (!res.ok()) { + break; + } + } + + {// do not get into race conditions with loadFromDB + MUTEX_LOCKER(locker, _loadFromDBLock); + _authInfo.clear(); + _outdated = true; + } + reloadAllUsers(); + return res; +} + VPackBuilder AuthInfo::getConfigData(std::string const& username) { + loadFromDB(); VPackBuilder bb = QueryUser(_queryRegistry, username); return VPackBuilder(bb.slice().get("configData")); } Result AuthInfo::setConfigData(std::string const& user, velocypack::Slice const& data) { + loadFromDB(); + + READ_LOCKER(guard, _authInfoLock); auto it = _authInfo.find(user); if (it == _authInfo.end()) { return Result(TRI_ERROR_USER_NOT_FOUND); @@ -601,12 +639,16 @@ Result AuthInfo::setConfigData(std::string const& user, } VPackBuilder AuthInfo::getUserData(std::string const& username) { + loadFromDB(); VPackBuilder bb = QueryUser(_queryRegistry, username); return VPackBuilder(bb.slice().get("userData")); } Result AuthInfo::setUserData(std::string const& user, velocypack::Slice const& data) { + loadFromDB(); + + READ_LOCKER(guard, _authInfoLock); auto it = _authInfo.find(user); if (it == _authInfo.end()) { return Result(TRI_ERROR_USER_NOT_FOUND); @@ -702,8 +744,6 @@ AuthLevel AuthInfo::canUseCollection(std::string const& username, // public called from HttpCommTask.cpp and VstCommTask.cpp AuthResult AuthInfo::checkAuthentication(AuthenticationMethod authType, std::string const& secret) { - loadFromDB(); - switch (authType) { case AuthenticationMethod::BASIC: return checkAuthenticationBasic(secret); @@ -718,10 +758,15 @@ AuthResult AuthInfo::checkAuthentication(AuthenticationMethod authType, // private AuthResult AuthInfo::checkAuthenticationBasic(std::string const& secret) { + auto role = ServerState::instance()->getRole(); + if (role != ServerState::ROLE_SINGLE && + role != ServerState::ROLE_COORDINATOR) { + return AuthResult(); + } + { READ_LOCKER(guard, _authInfoLock); auto const& it = _authBasicCache.find(secret); - if (it != _authBasicCache.end()) { return it->second; } @@ -729,9 +774,8 @@ AuthResult AuthInfo::checkAuthenticationBasic(std::string const& secret) { std::string const up = StringUtils::decodeBase64(secret); std::string::size_type n = up.find(':', 0); - if (n == std::string::npos || n == 0 || n + 1 > up.size()) { - LOG_TOPIC(TRACE, arangodb::Logger::FIXME) + LOG_TOPIC(TRACE, arangodb::Logger::AUTHENTICATION) << "invalid authentication data found, cannot extract " "username/password"; return AuthResult(); @@ -741,7 +785,6 @@ AuthResult AuthInfo::checkAuthenticationBasic(std::string const& secret) { std::string password = up.substr(n + 1); AuthResult result = checkPassword(username, password); - { WRITE_LOCKER(guard, _authInfoLock); diff --git a/arangod/VocBase/AuthInfo.h b/arangod/VocBase/AuthInfo.h index 9007c98e6a..e7fa704ead 100644 --- a/arangod/VocBase/AuthInfo.h +++ b/arangod/VocBase/AuthInfo.h @@ -90,6 +90,7 @@ class AuthInfo { std::function const&); velocypack::Builder serializeUser(std::string const& user); Result removeUser(std::string const& user); + Result removeAllUsers(); velocypack::Builder getConfigData(std::string const& user); Result setConfigData(std::string const& user, velocypack::Slice const& data); diff --git a/arangod/VocBase/Methods/Databases.cpp b/arangod/VocBase/Methods/Databases.cpp index ccaef9e59b..c0bd961fd4 100644 --- a/arangod/VocBase/Methods/Databases.cpp +++ b/arangod/VocBase/Methods/Databases.cpp @@ -463,7 +463,8 @@ arangodb::Result Databases::drop(TRI_vocbase_t* systemVocbase, } auto auth = FeatureCacheFeature::instance()->authenticationFeature(); - if (auth->isActive()) { + if (auth->isActive() && (ServerState::instance()->isCoordinator() || + !ServerState::instance()->isRunningInCluster())) { auth->authInfo()->enumerateUsers( [&](AuthUserEntry& entry) { entry.removeDatabase(dbName); }); } diff --git a/arangosh/Import/ImportFeature.cpp b/arangosh/Import/ImportFeature.cpp index dc0e8b910c..29331b5899 100644 --- a/arangosh/Import/ImportFeature.cpp +++ b/arangosh/Import/ImportFeature.cpp @@ -462,8 +462,10 @@ void ImportFeature::start() { } } else { - LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "error message: " - << ih.getErrorMessage(); + LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "error message:"; + for (std::string const& msg : ih.getErrorMessages()) { + LOG_TOPIC(ERR, arangodb::Logger::FIXME) << msg; + } } } catch (std::exception const& ex) { LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "Caught exception " << ex.what() diff --git a/arangosh/Import/ImportHelper.cpp b/arangosh/Import/ImportHelper.cpp index 0dc1ada99f..5163ec2303 100644 --- a/arangosh/Import/ImportHelper.cpp +++ b/arangosh/Import/ImportHelper.cpp @@ -173,7 +173,7 @@ ImportHelper::ImportHelper(ClientFeature const* client, while (true) { uint32_t numReady = 0; for (auto const& t : _senderThreads) { - if (t->isReady()) { + if (t->isIdle()) { numReady++; } } @@ -200,7 +200,7 @@ bool ImportHelper::importDelimited(std::string const& collectionName, _firstLine = ""; _outputBuffer.clear(); _lineBuffer.clear(); - _errorMessage = ""; + _errorMessages.clear(); _hasError = false; // read and convert @@ -217,7 +217,7 @@ bool ImportHelper::importDelimited(std::string const& collectionName, fd = TRI_TRACKED_OPEN_FILE(fileName.c_str(), O_RDONLY | TRI_O_CLOEXEC); if (fd < 0) { - _errorMessage = TRI_LAST_ERROR_STR; + _errorMessages.push_back(TRI_LAST_ERROR_STR); return false; } } @@ -235,8 +235,7 @@ bool ImportHelper::importDelimited(std::string const& collectionName, if (fd != STDIN_FILENO) { TRI_TRACKED_CLOSE_FILE(fd); } - - _errorMessage = "out of memory"; + _errorMessages.push_back("out of memory"); return false; } @@ -270,7 +269,7 @@ bool ImportHelper::importDelimited(std::string const& collectionName, if (fd != STDIN_FILENO) { TRI_TRACKED_CLOSE_FILE(fd); } - _errorMessage = TRI_LAST_ERROR_STR; + _errorMessages.push_back(TRI_LAST_ERROR_STR); return false; } else if (n == 0) { // we have read the entire file @@ -311,7 +310,7 @@ bool ImportHelper::importJson(std::string const& collectionName, _collectionName = collectionName; _firstLine = ""; _outputBuffer.clear(); - _errorMessage = ""; + _errorMessages.clear(); _hasError = false; // read and convert @@ -328,7 +327,7 @@ bool ImportHelper::importJson(std::string const& collectionName, fd = TRI_TRACKED_OPEN_FILE(fileName.c_str(), O_RDONLY | TRI_O_CLOEXEC); if (fd < 0) { - _errorMessage = TRI_LAST_ERROR_STR; + _errorMessages.push_back(TRI_LAST_ERROR_STR); return false; } } @@ -350,7 +349,7 @@ bool ImportHelper::importJson(std::string const& collectionName, while (!_hasError) { // reserve enough room to read more data if (_outputBuffer.reserve(BUFFER_SIZE) == TRI_ERROR_OUT_OF_MEMORY) { - _errorMessage = TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY); + _errorMessages.push_back(TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY)); if (fd != STDIN_FILENO) { TRI_TRACKED_CLOSE_FILE(fd); @@ -362,7 +361,7 @@ bool ImportHelper::importJson(std::string const& collectionName, ssize_t n = TRI_READ(fd, _outputBuffer.end(), BUFFER_SIZE - 1); if (n < 0) { - _errorMessage = TRI_LAST_ERROR_STR; + _errorMessages.push_back(TRI_LAST_ERROR_STR); if (fd != STDIN_FILENO) { TRI_TRACKED_CLOSE_FILE(fd); } @@ -398,10 +397,9 @@ bool ImportHelper::importJson(std::string const& collectionName, if (fd != STDIN_FILENO) { TRI_TRACKED_CLOSE_FILE(fd); } - _errorMessage = - "import file is too big. please increase the value of --batch-size " - "(currently " + - StringUtils::itoa(_maxUploadSize) + ")"; + _errorMessages.push_back("import file is too big. please increase the value of --batch-size " + "(currently " + + StringUtils::itoa(_maxUploadSize) + ")"); return false; } @@ -750,7 +748,7 @@ bool ImportHelper::truncateCollection() { << "unable to truncate collection '" << _collectionName << "', server returned status code: " << static_cast(code); _hasError = true; - _errorMessage = "Unable to overwrite collection"; + _errorMessages.push_back("Unable to overwrite collection"); return false; } @@ -832,7 +830,7 @@ SenderThread* ImportHelper::findSender() { for (auto const& t : _senderThreads) { if (t->hasError()) { _hasError = true; - _errorMessage = t->errorMessage(); + _errorMessages.push_back(t->errorMessage()); return nullptr; } else if (t->isIdle()) { return t.get(); diff --git a/arangosh/Import/ImportHelper.h b/arangosh/Import/ImportHelper.h index 3a5453a201..84e9dd77d8 100644 --- a/arangosh/Import/ImportHelper.h +++ b/arangosh/Import/ImportHelper.h @@ -233,7 +233,7 @@ class ImportHelper { /// @return string get the error message ////////////////////////////////////////////////////////////////////////////// - std::string getErrorMessage() { return _errorMessage; } + std::vector getErrorMessages() { return _errorMessages; } private: static void ProcessCsvBegin(TRI_csv_parser_t*, size_t); @@ -293,7 +293,7 @@ class ImportHelper { std::unordered_map _translations; bool _hasError; - std::string _errorMessage; + std::vector _errorMessages; static double const ProgressStep; }; diff --git a/arangosh/Import/SenderThread.h b/arangosh/Import/SenderThread.h index 2f094f54d8..0e558b8790 100644 --- a/arangosh/Import/SenderThread.h +++ b/arangosh/Import/SenderThread.h @@ -58,7 +58,9 @@ class SenderThread : public arangodb::Thread { void sendData(std::string const& url, basics::StringBuffer* sender); bool hasError(); + /// Ready to start sending bool isReady(); + /// Currently not sending data bool isIdle(); bool isDone(); diff --git a/arangosh/Shell/V8ClientConnection.cpp b/arangosh/Shell/V8ClientConnection.cpp index dbccfa2f2e..5fd8a97198 100644 --- a/arangosh/Shell/V8ClientConnection.cpp +++ b/arangosh/Shell/V8ClientConnection.cpp @@ -982,8 +982,12 @@ static void ClientConnection_importCsv( TRI_V8_RETURN(result); } - TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FAILED, - ih.getErrorMessage().c_str()); + std::string error = "error messages:"; + for (std::string const& msg : ih.getErrorMessages()) { + error.append(msg + ";\t"); + } + + TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FAILED, error.c_str()); TRI_V8_TRY_CATCH_END } @@ -1046,9 +1050,13 @@ static void ClientConnection_importJson( TRI_V8_RETURN(result); } + + std::string error = "error messages:"; + for (std::string const& msg : ih.getErrorMessages()) { + error.append(msg + ";\t"); + } - TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FAILED, - ih.getErrorMessage().c_str()); + TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FAILED, error.c_str()); TRI_V8_TRY_CATCH_END } diff --git a/js/client/modules/@arangodb/process-utils.js b/js/client/modules/@arangodb/process-utils.js index 50b246fecf..60d1becebb 100644 --- a/js/client/modules/@arangodb/process-utils.js +++ b/js/client/modules/@arangodb/process-utils.js @@ -971,6 +971,7 @@ function startInstanceCluster (instanceInfo, protocol, options, throw new Error("startup timed out!"); } } + arango.reconnect(instanceInfo.endpoint, '_system', 'root', ''); return true; } diff --git a/js/server/restore-admin-user.js b/js/server/restore-admin-user.js deleted file mode 100644 index 8e7939bc15..0000000000 --- a/js/server/restore-admin-user.js +++ /dev/null @@ -1,37 +0,0 @@ -/*jshint -W051:true, -W069:true */ -'use strict'; - -//////////////////////////////////////////////////////////////////////////////// -/// DISCLAIMER -/// -/// Copyright 2016 ArangoDB GmbH, Cologne, Germany -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// -/// Copyright holder is ArangoDB GmbH, Cologne, Germany -/// -/// @author Frank Celler -/// @author Copyright 2016, ArangoDB GmbH, Cologne, Germany -//////////////////////////////////////////////////////////////////////////////// - -(function() { - var args = global.UPGRADE_ARGS; - delete global.UPGRADE_ARGS; - - require("internal").db._users.truncate(); - - const users = require("@arangodb/users"); - - users.save("root", args.password, true); - users.grantDatabase("root", "*", "rw"); -}()); diff --git a/js/server/upgrade-database.js b/js/server/upgrade-database.js index 40708d810e..b05a5fe1e0 100644 --- a/js/server/upgrade-database.js +++ b/js/server/upgrade-database.js @@ -44,9 +44,7 @@ const db = internal.db; const shallowCopy = require('@arangodb/util').shallowCopy; - const defaultRootPW = args.password || ''; - - function upgrade () { + function upgrade () { // default replication factor for system collections const DEFAULT_REPLICATION_FACTOR_SYSTEM = internal.DEFAULT_REPLICATION_FACTOR_SYSTEM;