1
0
Fork 0

added database.init-database

This commit is contained in:
Frank Celler 2016-06-08 21:40:09 +02:00
parent 967d709f28
commit a92d566234
10 changed files with 330 additions and 60 deletions

View File

@ -242,6 +242,7 @@ add_executable(${BIN_ARANGOD}
RestServer/EndpointFeature.cpp
RestServer/FileDescriptorsFeature.cpp
RestServer/FrontendFeature.cpp
RestServer/InitDatabaseFeature.cpp
RestServer/QueryRegistryFeature.cpp
RestServer/RestServerFeature.cpp
RestServer/ScriptFeature.cpp

View File

@ -30,8 +30,8 @@
#include "ProgramOptions/Section.h"
#include "Rest/Version.h"
#include "RestServer/DatabaseServerFeature.h"
#include "RestServer/RestServerFeature.h"
#include "RestServer/QueryRegistryFeature.h"
#include "RestServer/RestServerFeature.h"
#include "V8Server/V8DealerFeature.h"
#include "V8Server/v8-query.h"
#include "V8Server/v8-vocbase.h"
@ -65,12 +65,14 @@ DatabaseFeature::DatabaseFeature(ApplicationServer* server)
requiresElevatedPrivileges(false);
startsAfter("DatabaseServer");
startsAfter("LogfileManager");
startsAfter("InitDatabase");
}
void DatabaseFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
options->addSection("database", "Configure the database");
options->addOldOption("server.disable-replication-applier", "database.replication-applier");
options->addOldOption("server.disable-replication-applier",
"database.replication-applier");
options->addOption("--database.directory", "path to the database directory",
new StringParameter(&_directory));
@ -197,9 +199,9 @@ void DatabaseFeature::updateContexts() {
auto vocbase = _vocbase;
V8DealerFeature* dealer =
V8DealerFeature* dealer =
ApplicationServer::getFeature<V8DealerFeature>("V8Dealer");
dealer->defineContextUpdate(
[queryRegistry, server, vocbase](
v8::Isolate* isolate, v8::Handle<v8::Context> context, size_t i) {
@ -243,16 +245,15 @@ void DatabaseFeature::openDatabases() {
defaults.forceSyncProperties = _forceSyncProperties;
// get authentication (if available)
RestServerFeature* rest =
RestServerFeature* rest =
ApplicationServer::getFeature<RestServerFeature>("RestServer");
defaults.requireAuthentication = rest->authentication();
defaults.requireAuthenticationUnixSockets =
rest->authenticationUnixSockets();
defaults.requireAuthenticationUnixSockets = rest->authenticationUnixSockets();
defaults.authenticateSystemOnly = rest->authenticationSystemOnly();
bool const iterateMarkersOnOpen =
!wal::LogfileManager::instance()->hasFoundLastTick();
!wal::LogfileManager::instance()->hasFoundLastTick();
int res = TRI_InitServer(
DatabaseServerFeature::SERVER, DatabaseServerFeature::INDEX_POOL,
@ -267,15 +268,20 @@ void DatabaseFeature::openDatabases() {
res = TRI_StartServer(DatabaseServerFeature::SERVER, _checkVersion, _upgrade);
if (res != TRI_ERROR_NO_ERROR) {
if (_checkVersion && res == TRI_ERROR_ARANGO_EMPTY_DATADIR) {
if (res == TRI_ERROR_ARANGO_EMPTY_DATADIR) {
LOG_TOPIC(TRACE, Logger::STARTUP) << "database is empty";
_isInitiallyEmpty = true;
} else {
}
if (! _checkVersion || ! _isInitiallyEmpty) {
LOG(FATAL) << "cannot start server: " << TRI_errno_string(res);
FATAL_ERROR_EXIT();
}
}
LOG_TOPIC(TRACE, Logger::STARTUP) << "found system database";
if (!_isInitiallyEmpty) {
LOG_TOPIC(TRACE, Logger::STARTUP) << "found system database";
}
}
void DatabaseFeature::closeDatabases() {

View File

@ -0,0 +1,175 @@
////////////////////////////////////////////////////////////////////////////////
/// 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 Dr. Frank Celler
////////////////////////////////////////////////////////////////////////////////
#include "InitDatabaseFeature.h"
#include "Basics/FileUtils.h"
#include "Logger/LoggerFeature.h"
#include "Logger/LoggerFeature.h"
#include "ProgramOptions/ProgramOptions.h"
#include "ProgramOptions/Section.h"
#include "RestServer/DatabaseFeature.h"
using namespace arangodb;
using namespace arangodb::application_features;
using namespace arangodb::basics;
using namespace arangodb::options;
InitDatabaseFeature::InitDatabaseFeature(ApplicationServer* server)
: ApplicationFeature(server, "InitDatabase") {
setOptional(false);
requiresElevatedPrivileges(false);
startsAfter("Logger");
}
void InitDatabaseFeature::collectOptions(
std::shared_ptr<ProgramOptions> options) {
options->addSection("database", "Configure the database");
options->addHiddenOption("--database.init-database",
"initializes an empty database",
new BooleanParameter(&_initDatabase));
options->addHiddenOption("--database.password",
"initial password of root user",
new StringParameter(&_password));
}
void InitDatabaseFeature::validateOptions(
std::shared_ptr<ProgramOptions> options) {
ProgramOptions::ProcessingResult const& result = options->processingResult();
_seenPassword = result.touched("database.password");
}
void InitDatabaseFeature::prepare() {
if (!_seenPassword) {
std::string env = "ARANGODB_DEFAULT_ROOT_PASSWORD";
char const* passworde = getenv(env.c_str());
if (passworde != nullptr && *passworde != '\0') {
env += "=";
putenv(const_cast<char*>(env.c_str()));
_password = passworde;
_seenPassword = true;
}
}
if (!_initDatabase) {
return;
}
checkEmptyDatabase();
if (!_seenPassword) {
while (true) {
std::string password1 =
readPassword("Please enter password for root user");
if (!password1.empty()) {
std::string password2 = readPassword("Repeat password");
if (password1 == password2) {
_password = password1;
break;
}
LOG(ERR) << "passwords do not match, please repeat";
} else {
LOG(FATAL) << "initialization aborted by user";
FATAL_ERROR_EXIT();
}
}
}
}
void InitDatabaseFeature::start() {
if (!_initDatabase) {
return;
}
}
std::string InitDatabaseFeature::readPassword(std::string const& message) {
std::string password;
arangodb::Logger::flush();
std::cout << message << ": " << std::flush;
#ifdef TRI_HAVE_TERMIOS_H
TRI_SetStdinVisibility(false);
std::getline(std::cin, password);
TRI_SetStdinVisibility(true);
#else
std::getline(std::cin, password);
#endif
std::cout << std::endl;
return password;
}
void InitDatabaseFeature::checkEmptyDatabase() {
auto database = ApplicationServer::getFeature<DatabaseFeature>("Database");
std::string path = database->directory();
std::string journals = FileUtils::buildFilename(path, "journals");
bool empty = false;
std::string message;
int code = 2;
if (FileUtils::exists(path)) {
if (!FileUtils::isDirectory(path)) {
message = "database path '" + path + "' is not a directory";
code = EXIT_FAILURE;
goto doexit;
}
if (FileUtils::exists(journals)) {
if (!FileUtils::isDirectory(journals)) {
message =
"database journals path '" + journals + "' is not a directory";
code = EXIT_FAILURE;
goto doexit;
}
} else {
empty = true;
}
} else {
empty = true;
}
if (!empty) {
message = "database already initialized, refusing to change root user";
goto doexit;
}
return;
doexit:
LOG(FATAL) << message;
auto logger = ApplicationServer::getFeature<LoggerFeature>("Logger");
logger->unprepare();
TRI_EXIT_FUNCTION(code, nullptr);
exit(code);
}

View File

@ -0,0 +1,57 @@
////////////////////////////////////////////////////////////////////////////////
/// 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 Dr. Frank Celler
////////////////////////////////////////////////////////////////////////////////
#ifndef APPLICATION_FEATURES_INIT_DATABASE_FEATURE_H
#define APPLICATION_FEATURES_INIT_DATABASE_FEATURE_H 1
#include "ApplicationFeatures/ApplicationFeature.h"
namespace arangodb {
class InitDatabaseFeature final
: public application_features::ApplicationFeature {
public:
explicit InitDatabaseFeature(application_features::ApplicationServer* server);
public:
std::string const& defaultPassword() const { return _password; }
bool isInitDatabase() const { return _initDatabase; }
private:
bool _initDatabase = false;
std::string _password;
public:
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void start() override final;
void prepare() override final;
private:
void checkEmptyDatabase();
std::string readPassword(std::string const&);
private:
bool _seenPassword = false;
};
}
#endif

View File

@ -27,6 +27,7 @@
#include "ProgramOptions/Section.h"
#include "RestServer/DatabaseFeature.h"
#include "RestServer/DatabaseServerFeature.h"
#include "RestServer/InitDatabaseFeature.h"
#include "V8/v8-globals.h"
#include "V8Server/V8Context.h"
#include "V8Server/V8DealerFeature.h"
@ -58,7 +59,7 @@ UpgradeFeature::UpgradeFeature(
void UpgradeFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
options->addSection("database", "Configure the database");
options->addOldOption("upgrade", "--database.auto-upgrade");
options->addOption("--database.auto-upgrade",
@ -86,12 +87,12 @@ void UpgradeFeature::validateOptions(std::shared_ptr<ProgramOptions> options) {
ApplicationServer::forceDisableFeatures(_nonServerFeatures);
DatabaseFeature* database =
DatabaseFeature* database =
ApplicationServer::getFeature<DatabaseFeature>("Database");
database->disableReplicationApplier();
database->enableUpgrade();
ClusterFeature* cluster =
ClusterFeature* cluster =
ApplicationServer::getFeature<ClusterFeature>("Cluster");
cluster->forceDisable();
}
@ -103,7 +104,9 @@ void UpgradeFeature::start() {
}
// and force shutdown
if (_upgrade) {
auto init = ApplicationServer::getFeature<InitDatabaseFeature>("InitDatabase");
if (_upgrade || init->isInitDatabase()) {
server()->beginShutdown();
}
}
@ -147,27 +150,38 @@ void UpgradeFeature::upgradeDatabase() {
v8::HandleScope scope(context->_isolate);
v8::Handle<v8::Object> args = v8::Object::New(context->_isolate);
args->Set(TRI_V8_ASCII_STRING2(context->_isolate, "upgrade"),
v8::Boolean::New(context->_isolate, _upgrade));
auto init = ApplicationServer::getFeature<InitDatabaseFeature>(
"InitDatabase");
if (init != nullptr) {
args->Set(
TRI_V8_ASCII_STRING2(context->_isolate, "password"),
TRI_V8_STD_STRING2(context->_isolate, init->defaultPassword()));
}
localContext->Global()->Set(
TRI_V8_ASCII_STRING2(context->_isolate, "UPGRADE_ARGS"), args);
bool ok = TRI_UpgradeDatabase(vocbase, localContext);
if (!ok) {
if (localContext->Global()->Has(
TRI_V8_ASCII_STRING2(context->_isolate, "UPGRADE_STARTED"))) {
if (localContext->Global()->Has(TRI_V8_ASCII_STRING2(
context->_isolate, "UPGRADE_STARTED"))) {
localContext->Exit();
if (_upgrade) {
LOG(FATAL) << "Database '" << vocbase->_name
<< "' upgrade failed. Please inspect the logs from "
<< "' upgrade failed. Please inspect the logs from "
"the upgrade procedure";
FATAL_ERROR_EXIT();
} else {
LOG(FATAL) << "Database '" << vocbase->_name
<< "' needs upgrade. Please start the server with the "
"--database.auto-upgrade option";
LOG(FATAL)
<< "Database '" << vocbase->_name
<< "' needs upgrade. Please start the server with the "
"--database.auto-upgrade option";
FATAL_ERROR_EXIT();
}
} else {
@ -175,12 +189,14 @@ void UpgradeFeature::upgradeDatabase() {
FATAL_ERROR_EXIT();
}
LOG(DEBUG) << "database '" << vocbase->_name << "' init/upgrade done";
LOG(DEBUG) << "database '" << vocbase->_name
<< "' init/upgrade done";
}
}
}
// finally leave the context. otherwise v8 will crash with assertion failure
// finally leave the context. otherwise v8 will crash with assertion
// failure
// when we delete
// the context locker below
localContext->Exit();

View File

@ -56,6 +56,7 @@
#include "RestServer/EndpointFeature.h"
#include "RestServer/FileDescriptorsFeature.h"
#include "RestServer/FrontendFeature.h"
#include "RestServer/InitDatabaseFeature.h"
#include "RestServer/QueryRegistryFeature.h"
#include "RestServer/RestServerFeature.h"
#include "RestServer/ScriptFeature.h"
@ -119,6 +120,7 @@ static int runServer(int argc, char** argv) {
server.addFeature(new FileDescriptorsFeature(&server));
server.addFeature(new FoxxQueuesFeature(&server));
server.addFeature(new FrontendFeature(&server));
server.addFeature(new InitDatabaseFeature(&server));
server.addFeature(new LanguageFeature(&server));
server.addFeature(new LogfileManager(&server));
server.addFeature(new LoggerBufferFeature(&server));

View File

@ -3050,8 +3050,10 @@ bool TRI_UpgradeDatabase(TRI_vocbase_t* vocbase,
v8g->_vocbase = vocbase;
auto startupLoader = V8DealerFeature::DEALER->startupLoader();
v8::Handle<v8::Value> result = startupLoader->executeGlobalScript(
isolate, isolate->GetCurrentContext(), "server/upgrade-database.js");
bool ok = TRI_ObjectToBoolean(result);
if (!ok) {

View File

@ -31,7 +31,10 @@
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
(function(args) {
(function() {
var args = global.UPGRADE_ARGS;
delete global.UPGRADE_ARGS;
const internal = require("internal");
const fs = require("fs");
const console = require("console");
@ -40,7 +43,7 @@
const db = internal.db;
const shallowCopy = require("@arangodb/util").shallowCopy;
const defaultRootPW = require("process").env.ARANGODB_DEFAULT_ROOT_PASSWORD || "";
const defaultRootPW = args.password || "";
function upgrade() {
@ -776,8 +779,6 @@
// set this global variable to inform the server we actually got until here...
global.UPGRADE_STARTED = true;
delete global.UPGRADE_ARGS;
// and run the upgrade
return upgrade();
}(global.UPGRADE_ARGS));
}());

View File

@ -25,8 +25,8 @@
#include "ApplicationFeatures/ApplicationFeature.h"
#include "ApplicationFeatures/PrivilegeFeature.h"
#include "Basics/StringUtils.h"
#include "ProgramOptions/ArgumentParser.h"
#include "Logger/Logger.h"
#include "ProgramOptions/ArgumentParser.h"
using namespace arangodb::application_features;
using namespace arangodb::basics;
@ -194,7 +194,6 @@ void ApplicationServer::run(int argc, char* argv[]) {
reportServerProgress(_state);
start();
// wait until we get signaled the shutdown request
_state = ServerState::IN_WAIT;
reportServerProgress(_state);
@ -268,11 +267,13 @@ void ApplicationServer::collectOptions() {
_options->addHiddenOption("--dump-dependencies", "dump dependency graph",
new BooleanParameter(&_dumpDependencies));
apply([this](ApplicationFeature* feature) {
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::loadOptions";
feature->collectOptions(_options);
reportFeatureProgress(_state, feature->name());
}, true);
apply(
[this](ApplicationFeature* feature) {
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::loadOptions";
feature->collectOptions(_options);
reportFeatureProgress(_state, feature->name());
},
true);
}
void ApplicationServer::parseOptions(int argc, char* argv[]) {
@ -366,18 +367,21 @@ void ApplicationServer::setupDependencies(bool failOnMissing) {
// first check if a feature references an unknown other feature
if (failOnMissing) {
apply([this](ApplicationFeature* feature) {
for (auto& other : feature->requires()) {
if (!this->exists(other)) {
fail("feature '" + feature->name() +
"' depends on unknown feature '" + other + "'");
}
if (!this->feature(other)->isEnabled()) {
fail("enabled feature '" + feature->name() +
"' depends on other feature '" + other + "', which is disabled");
}
}
}, true);
apply(
[this](ApplicationFeature* feature) {
for (auto& other : feature->requires()) {
if (!this->exists(other)) {
fail("feature '" + feature->name() +
"' depends on unknown feature '" + other + "'");
}
if (!this->feature(other)->isEnabled()) {
fail("enabled feature '" + feature->name() +
"' depends on other feature '" + other +
"', which is disabled");
}
}
},
true);
}
// first insert all features, even the inactive ones
@ -478,14 +482,16 @@ void ApplicationServer::prepare() {
feature->prepare();
feature->state(FeatureState::PREPARED);
} catch (std::exception const& ex) {
LOG(ERR) << "caught exception during prepare of feature " << feature->name() << ": " << ex.what();
LOG(ERR) << "caught exception during prepare of feature "
<< feature->name() << ": " << ex.what();
// restore original privileges
if (!privilegesElevated) {
raisePrivilegesTemporarily();
}
throw;
} catch (...) {
LOG(ERR) << "caught unknown exception during prepare of feature " << feature->name();
LOG(ERR) << "caught unknown exception during prepare of feature "
<< feature->name();
// restore original privileges
if (!privilegesElevated) {
raisePrivilegesTemporarily();
@ -511,16 +517,19 @@ void ApplicationServer::start() {
feature->state(FeatureState::STARTED);
reportFeatureProgress(_state, feature->name());
} catch (std::exception const& ex) {
LOG(ERR) << "caught exception during start of feature " << feature->name() << ": " << ex.what() << ". shutting down";
LOG(ERR) << "caught exception during start of feature " << feature->name()
<< ": " << ex.what() << ". shutting down";
abortStartup = true;
} catch (...) {
LOG(ERR) << "caught unknown exception during start of feature " << feature->name() << ". shutting down";
LOG(ERR) << "caught unknown exception during start of feature "
<< feature->name() << ". shutting down";
abortStartup = true;
}
if (abortStartup) {
// try to stop all feature that we just started
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend(); ++it) {
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend();
++it) {
auto feature = *it;
if (feature->state() == FeatureState::STARTED) {
LOG(TRACE) << "forcefully stopping feature " << feature->name();
@ -541,7 +550,8 @@ void ApplicationServer::start() {
void ApplicationServer::stop() {
LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::stop";
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend(); ++it) {
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend();
++it) {
auto feature = *it;
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::stop";
@ -554,7 +564,8 @@ void ApplicationServer::stop() {
void ApplicationServer::unprepare() {
LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::unprepare";
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend(); ++it) {
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend();
++it) {
auto feature = *it;
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::unprepare";

View File

@ -30,7 +30,8 @@ using namespace arangodb;
using namespace arangodb::options;
ShutdownFeature::ShutdownFeature(
application_features::ApplicationServer* server, std::vector<std::string> const& features)
application_features::ApplicationServer* server,
std::vector<std::string> const& features)
: ApplicationFeature(server, "Shutdown") {
setOptional(true);
requiresElevatedPrivileges(false);
@ -43,6 +44,4 @@ ShutdownFeature::ShutdownFeature(
}
}
void ShutdownFeature::start() {
server()->beginShutdown();
}
void ShutdownFeature::start() { server()->beginShutdown(); }