From cb05777f32011dbfd39475d9304f0885c90de048 Mon Sep 17 00:00:00 2001 From: Frank Celler Date: Thu, 9 Jun 2016 14:19:54 +0200 Subject: [PATCH 1/3] added @include --- lib/ApplicationFeatures/ConfigFeature.cpp | 110 ++++++++++++++-------- lib/ApplicationFeatures/ConfigFeature.h | 3 +- lib/ProgramOptions/IniFileParser.h | 17 ++++ 3 files changed, 88 insertions(+), 42 deletions(-) diff --git a/lib/ApplicationFeatures/ConfigFeature.cpp b/lib/ApplicationFeatures/ConfigFeature.cpp index baa6b8f29b..20c09f97da 100644 --- a/lib/ApplicationFeatures/ConfigFeature.cpp +++ b/lib/ApplicationFeatures/ConfigFeature.cpp @@ -24,9 +24,9 @@ #include -#include "Logger/Logger.h" #include "Basics/FileUtils.h" #include "Basics/StringUtils.h" +#include "Logger/Logger.h" #include "ProgramOptions/IniFileParser.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" @@ -51,99 +51,127 @@ void ConfigFeature::collectOptions(std::shared_ptr options) { options->addOption("--configuration,-c", "the configuration file or 'none'", new StringParameter(&_file)); - // add --config as an alias for --configuration. both point to the same variable! + // add --config as an alias for --configuration. both point to the same + // variable! options->addHiddenOption("--config", "the configuration file or 'none'", - new StringParameter(&_file)); + new StringParameter(&_file)); - options->addOption("--check-configuration", "check the configuration and exists", + options->addOption("--check-configuration", + "check the configuration and exists", new BooleanParameter(&_checkConfiguration)); } void ConfigFeature::loadOptions(std::shared_ptr options) { - loadConfigFile(options); + loadConfigFile(options, _progname); if (_checkConfiguration) { exit(EXIT_SUCCESS); } } -void ConfigFeature::loadConfigFile(std::shared_ptr options) { +void ConfigFeature::loadConfigFile(std::shared_ptr options, + std::string const& progname) { if (StringUtils::tolower(_file) == "none") { LOG_TOPIC(DEBUG, Logger::CONFIG) << "use no config file at all"; return; } - IniFileParser parser(options.get()); + std::vector files; + std::set seen; // always prefer an explicitly given config file - if (!_file.empty()) { + if (_file.empty()) { + files.emplace_back(progname); + } else { LOG_TOPIC(DEBUG, Logger::CONFIG) << "using user supplied conifg file '" << _file << "'"; + IniFileParser parser(options.get()); + if (!parser.parse(_file)) { exit(EXIT_FAILURE); } - return; + auto includes = parser.includes(); + files.insert(files.end(), includes.begin(), includes.end()); + + LOG_TOPIC(DEBUG, Logger::CONFIG) << "seen @includes: " << includes; } - // clang-format off - // - // check in order: - // - // .conf - // ./etc/relative/.conf - // ${HOME}/.arangodb/.conf - // /etc/arangodb/.conf - // - // clang-format on + for (size_t i = 0; i < files.size(); ++i) { + auto name = files[i]; - std::string basename = _progname + ".conf"; - std::string filename = - FileUtils::buildFilename(FileUtils::currentDirectory(), basename); + if (seen.find(name) != seen.end()) { + LOG(FATAL) << "circluar includes, seen '" << name << "' twice"; + FATAL_ERROR_EXIT(); + } - LOG_TOPIC(DEBUG, Logger::CONFIG) << "checking '" << filename << "'"; + seen.insert(name); - if (!FileUtils::exists(filename)) { - filename = FileUtils::buildFilename(FileUtils::currentDirectory(), - "etc/relative/" + basename); + // clang-format off + // + // check in order: + // + // .conf + // ./etc/relative/.conf + // ${HOME}/.arangodb/.conf + // /etc/arangodb/.conf + // + // clang-format on + + std::string basename = name + ".conf"; + std::string filename = + FileUtils::buildFilename(FileUtils::currentDirectory(), basename); LOG_TOPIC(DEBUG, Logger::CONFIG) << "checking '" << filename << "'"; if (!FileUtils::exists(filename)) { - filename = FileUtils::buildFilename(FileUtils::homeDirectory(), basename); + filename = FileUtils::buildFilename(FileUtils::currentDirectory(), + "etc/relative/" + basename); LOG_TOPIC(DEBUG, Logger::CONFIG) << "checking '" << filename << "'"; if (!FileUtils::exists(filename)) { filename = - FileUtils::buildFilename(FileUtils::configDirectory(), basename); + FileUtils::buildFilename(FileUtils::homeDirectory(), basename); LOG_TOPIC(DEBUG, Logger::CONFIG) << "checking '" << filename << "'"; if (!FileUtils::exists(filename)) { - LOG_TOPIC(DEBUG, Logger::CONFIG) << "cannot find any config file"; - return; + filename = + FileUtils::buildFilename(FileUtils::configDirectory(), basename); + + LOG_TOPIC(DEBUG, Logger::CONFIG) << "checking '" << filename << "'"; + + if (!FileUtils::exists(filename)) { + LOG_TOPIC(DEBUG, Logger::CONFIG) << "cannot find any config file"; + return; + } } } } - } - std::string local = filename + ".local"; + IniFileParser parser(options.get()); - LOG_TOPIC(DEBUG, Logger::CONFIG) << "checking override '" << local << "'"; + std::string local = filename + ".local"; - if (FileUtils::exists(local)) { - LOG_TOPIC(DEBUG, Logger::CONFIG) << "loading '" << local << "'"; + LOG_TOPIC(DEBUG, Logger::CONFIG) << "checking override '" << local << "'"; - if (!parser.parse(local)) { + if (FileUtils::exists(local)) { + LOG_TOPIC(DEBUG, Logger::CONFIG) << "loading '" << local << "'"; + + if (!parser.parse(local)) { + exit(EXIT_FAILURE); + } + } + + LOG_TOPIC(DEBUG, Logger::CONFIG) << "loading '" << filename << "'"; + + if (!parser.parse(filename)) { exit(EXIT_FAILURE); } - } - LOG_TOPIC(DEBUG, Logger::CONFIG) << "loading '" << filename << "'"; - - if (!parser.parse(filename)) { - exit(EXIT_FAILURE); + auto includes = parser.includes(); + files.insert(files.end(), includes.begin(), includes.end()); } } diff --git a/lib/ApplicationFeatures/ConfigFeature.h b/lib/ApplicationFeatures/ConfigFeature.h index a3e04e1a0c..bcee29c06d 100644 --- a/lib/ApplicationFeatures/ConfigFeature.h +++ b/lib/ApplicationFeatures/ConfigFeature.h @@ -40,7 +40,8 @@ class ConfigFeature final : public application_features::ApplicationFeature { bool _checkConfiguration; private: - void loadConfigFile(std::shared_ptr); + void loadConfigFile(std::shared_ptr, + std::string const& progname); private: std::string _progname; diff --git a/lib/ProgramOptions/IniFileParser.h b/lib/ProgramOptions/IniFileParser.h index 5d5a2de156..05df9c010b 100644 --- a/lib/ProgramOptions/IniFileParser.h +++ b/lib/ProgramOptions/IniFileParser.h @@ -47,6 +47,10 @@ class IniFileParser { _matchers.assignment = std::regex( "^[ \t]*(([-_A-Za-z0-9]*\\.)?[-_A-Za-z0-9]*)[ \t]*=[ \t]*(.*?)?[ \t]*$", std::regex::ECMAScript); + // an include line + _matchers.include = std::regex( + "^[ \t]*@include[ \t]*([-_A-Za-z0-9]*)[ \t]*$", + std::regex::ECMAScript); } // parse a config file. returns true if all is well, false otherwise @@ -80,6 +84,12 @@ class IniFileParser { if (std::regex_match(line, match, _matchers.section)) { // found section currentSection = match[1].str(); + } else if (std::regex_match(line, match, _matchers.include)) { + // found include + std::string option; + std::string value(match[1].str()); + + _includes.emplace_back(value); } else if (std::regex_match(line, match, _matchers.assignment)) { // found assignment std::string option; @@ -107,13 +117,20 @@ class IniFileParser { return true; } + // seen includes + std::vector const& includes() const { + return _includes; + } + private: ProgramOptions* _options; + std::vector _includes; struct { std::regex comment; std::regex section; std::regex assignment; + std::regex include; } _matchers; }; } From f029808e4eb846ffbe9e52317a1af99d46f82e42 Mon Sep 17 00:00:00 2001 From: Frank Celler Date: Thu, 9 Jun 2016 14:20:04 +0200 Subject: [PATCH 2/3] added restore-admin --- arangod/RestServer/InitDatabaseFeature.cpp | 16 ++--- arangod/RestServer/InitDatabaseFeature.h | 3 +- arangod/RestServer/UpgradeFeature.cpp | 83 ++++++++++++++++++---- arangod/RestServer/UpgradeFeature.h | 3 +- js/server/restore-admin-user.js | 40 +++++++++++ 5 files changed, 122 insertions(+), 23 deletions(-) create mode 100644 js/server/restore-admin-user.js diff --git a/arangod/RestServer/InitDatabaseFeature.cpp b/arangod/RestServer/InitDatabaseFeature.cpp index 8ec25e94df..580b44e73e 100644 --- a/arangod/RestServer/InitDatabaseFeature.cpp +++ b/arangod/RestServer/InitDatabaseFeature.cpp @@ -49,6 +49,10 @@ void InitDatabaseFeature::collectOptions( "initializes an empty database", new BooleanParameter(&_initDatabase)); + options->addHiddenOption("--database.restore-admin", + "resets the admin users and sets a new password", + new BooleanParameter(&_restoreAdmin)); + options->addHiddenOption("--database.password", "initial password of root user", new StringParameter(&_password)); @@ -73,11 +77,13 @@ void InitDatabaseFeature::prepare() { } } - if (!_initDatabase) { + if (!_initDatabase && !_restoreAdmin) { return; } - checkEmptyDatabase(); + if (_initDatabase) { + checkEmptyDatabase(); + } if (!_seenPassword) { while (true) { @@ -101,12 +107,6 @@ void InitDatabaseFeature::prepare() { } } -void InitDatabaseFeature::start() { - if (!_initDatabase) { - return; - } -} - std::string InitDatabaseFeature::readPassword(std::string const& message) { std::string password; diff --git a/arangod/RestServer/InitDatabaseFeature.h b/arangod/RestServer/InitDatabaseFeature.h index 20a7c407ef..43fa119256 100644 --- a/arangod/RestServer/InitDatabaseFeature.h +++ b/arangod/RestServer/InitDatabaseFeature.h @@ -34,15 +34,16 @@ class InitDatabaseFeature final public: std::string const& defaultPassword() const { return _password; } bool isInitDatabase() const { return _initDatabase; } + bool restoreAdmin() const { return _restoreAdmin; } private: bool _initDatabase = false; + bool _restoreAdmin = false; std::string _password; public: void collectOptions(std::shared_ptr) override final; void validateOptions(std::shared_ptr) override final; - void start() override final; void prepare() override final; private: diff --git a/arangod/RestServer/UpgradeFeature.cpp b/arangod/RestServer/UpgradeFeature.cpp index 81634c6f9c..f7809d922d 100644 --- a/arangod/RestServer/UpgradeFeature.cpp +++ b/arangod/RestServer/UpgradeFeature.cpp @@ -98,20 +98,83 @@ void UpgradeFeature::validateOptions(std::shared_ptr options) { } void UpgradeFeature::start() { + auto init = + ApplicationServer::getFeature("InitDatabase"); + // upgrade the database if (_upgradeCheck) { - upgradeDatabase(); + upgradeDatabase(init->defaultPassword()); + } + + // change admin user + if (init->restoreAdmin()) { + changeAdminPassword(init->defaultPassword()); } // and force shutdown - auto init = ApplicationServer::getFeature("InitDatabase"); - - if (_upgrade || init->isInitDatabase()) { + if (_upgrade || init->isInitDatabase() || init->restoreAdmin()) { server()->beginShutdown(); } } -void UpgradeFeature::upgradeDatabase() { +void UpgradeFeature::changeAdminPassword(std::string const& defaultPassword) { + LOG(TRACE) << "starting to restore admin user"; + + auto* systemVocbase = DatabaseFeature::DATABASE->vocbase(); + + // enter context and isolate + { + V8Context* context = + V8DealerFeature::DEALER->enterContext(systemVocbase, true, 0); + + if (context == nullptr) { + LOG(FATAL) << "could not enter context #0"; + FATAL_ERROR_EXIT(); + } + + { + 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(DEBUG) << "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(); + } + + V8DealerFeature::DEALER->exitContext(context); + } + + // and return from the context + LOG(TRACE) << "finished to restore admin user"; +} + +void UpgradeFeature::upgradeDatabase(std::string const& defaultPassword) { LOG(TRACE) << "starting database init/upgrade"; auto* server = DatabaseServerFeature::SERVER; @@ -154,14 +217,8 @@ void UpgradeFeature::upgradeDatabase() { args->Set(TRI_V8_ASCII_STRING2(context->_isolate, "upgrade"), v8::Boolean::New(context->_isolate, _upgrade)); - auto init = ApplicationServer::getFeature( - "InitDatabase"); - - if (init != nullptr) { - args->Set( - TRI_V8_ASCII_STRING2(context->_isolate, "password"), - TRI_V8_STD_STRING2(context->_isolate, init->defaultPassword())); - } + 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 3f519624c8..9d7df0b9b3 100644 --- a/arangod/RestServer/UpgradeFeature.h +++ b/arangod/RestServer/UpgradeFeature.h @@ -41,7 +41,8 @@ class UpgradeFeature final : public application_features::ApplicationFeature { bool _upgradeCheck; private: - void upgradeDatabase(); + void changeAdminPassword(std::string const& defaultPassword); + void upgradeDatabase(std::string const& defaultPassword); private: int* _result; diff --git a/js/server/restore-admin-user.js b/js/server/restore-admin-user.js new file mode 100644 index 0000000000..d164c9e929 --- /dev/null +++ b/js/server/restore-admin-user.js @@ -0,0 +1,40 @@ +/*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; + + const users = require("@arangodb/users"); + + try { + users.remove("root"); + } catch (e) { + } + + users.save("root", args.password, true); + users.grantDatabase("root", "*", "rw"); +}()); From d954d05074b7806df09aa9928e140f02811136b4 Mon Sep 17 00:00:00 2001 From: Frank Celler Date: Thu, 9 Jun 2016 15:28:18 +0200 Subject: [PATCH 3/3] added default agency size 3 --- js/client/modules/@arangodb/testing.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/js/client/modules/@arangodb/testing.js b/js/client/modules/@arangodb/testing.js index 23dc8ba4f5..b15f91e2cb 100644 --- a/js/client/modules/@arangodb/testing.js +++ b/js/client/modules/@arangodb/testing.js @@ -132,6 +132,7 @@ const optionsDocumentation = [ ]; const optionsDefaults = { + "agencySize": 3, "build": "", "buildType": "", "cleanup": true, @@ -1888,6 +1889,7 @@ function splitBuckets(options, cases) { //////////////////////////////////////////////////////////////////////////////// let allTests = [ + "agency", "arangosh", "authentication", "authentication_parameters", @@ -3751,9 +3753,6 @@ testFuncs.agency = function(options) { options.agency = true; options.cluster = false; - if (options.agencySize === undefined) { - options.agencySize = 1; - } let instanceInfo = startInstance("tcp", options, {}, "agency");