From c82e0c2ca024ec7150a59e24d640aea348d8fc5c Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 7 Mar 2019 13:05:54 +0100 Subject: [PATCH] add option `--console.history` to arangosh (#8327) --- CHANGELOG | 12 +++++++ .../Manual/ReleaseNotes/NewFeatures34.md | 12 +++++++ arangod/RestHandler/RestVersionHandler.cpp | 4 +++ arangosh/Shell/ConsoleFeature.cpp | 29 +++++++++++---- arangosh/Shell/ConsoleFeature.h | 2 ++ arangosh/Shell/V8ClientConnection.cpp | 36 +++++++++++++++++-- arangosh/Shell/V8ClientConnection.h | 2 ++ arangosh/Shell/V8ShellFeature.cpp | 7 ++-- lib/Utilities/LinenoiseShell.cpp | 8 +++-- lib/Utilities/ShellBase.cpp | 16 +++++---- 10 files changed, 109 insertions(+), 19 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 602d6ea7be..66e332d8b1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,18 @@ devel * added "random" masking to mask any data type, added wildcard masking +* added option `--console.history` to arangosh for controlling whether + the command-line history should be loaded from and persisted in a file. + + The default value for this option is `true`. Setting it to `false` + will make arangosh not load any command-line history from the history + file, and not store the current session's history when the shell is + exited. The command-line history will then only be available in the + current shell session. + +* display the server role when connecting arangosh against a server (e.g. + SINGLE, COORDINATOR) + * fixed overflow in Windows NowNanos in RocksDB * Allow MoveShard from leader to a follower, thus swapping the two diff --git a/Documentation/Books/Manual/ReleaseNotes/NewFeatures34.md b/Documentation/Books/Manual/ReleaseNotes/NewFeatures34.md index 9c7225549c..d0bdfe2850 100644 --- a/Documentation/Books/Manual/ReleaseNotes/NewFeatures34.md +++ b/Documentation/Books/Manual/ReleaseNotes/NewFeatures34.md @@ -1047,6 +1047,18 @@ versions on the same machine (e.g. for testing). Client tools ------------ +### Arangosh + +Starting with ArangoDB version 3.4.5, the ArangoShell (arangosh) provides the option +`--console.history` for controlling whether the shell's command-line history +should be loaded from and persisted in a file. + +The default value for this option is `true`. Setting it to `false` +will make arangosh not load any command-line history from the history +file, and not store the current session's history when the shell is +exited. The command-line history will then only be available in the +current shell session. + ### Arangodump Arangodump can now dump multiple collections in parallel. This can significantly diff --git a/arangod/RestHandler/RestVersionHandler.cpp b/arangod/RestHandler/RestVersionHandler.cpp index ffa8d28936..31f3c800b4 100644 --- a/arangod/RestHandler/RestVersionHandler.cpp +++ b/arangod/RestHandler/RestVersionHandler.cpp @@ -66,6 +66,10 @@ RestStatus RestVersionHandler::execute() { auto server = application_features::ApplicationServer::server->getFeature( "Server"); result.add("mode", VPackValue(server->operationModeString())); + auto serverState = ServerState::instance(); + if (serverState != nullptr) { + result.add("role", VPackValue(ServerState::roleToString(serverState->getRole()))); + } } std::string host = ServerState::instance()->getHost(); diff --git a/arangosh/Shell/ConsoleFeature.cpp b/arangosh/Shell/ConsoleFeature.cpp index c27aa5549c..c8d66558d3 100644 --- a/arangosh/Shell/ConsoleFeature.cpp +++ b/arangosh/Shell/ConsoleFeature.cpp @@ -59,6 +59,7 @@ ConsoleFeature::ConsoleFeature(application_features::ApplicationServer& server) #endif _quiet(false), _colors(true), + _useHistory(true), _autoComplete(true), _prettyPrint(true), _auditFile(), @@ -108,6 +109,12 @@ void ConsoleFeature::collectOptions(std::shared_ptr options) { options->addOption("--console.audit-file", "audit log file to save commands and results", new StringParameter(&_auditFile)); + + options->addOption("--console.history", + "whether or not to load and persist command-line history", + new BooleanParameter(&_useHistory)) + .setIntroducedIn(30405) + .setIntroducedIn(30500); options->addOption("--console.pager", "enable paging", new BooleanParameter(&_pager)); @@ -309,13 +316,23 @@ std::string ConsoleFeature::readPassword() { } void ConsoleFeature::printWelcomeInfo() { - if (!_quiet && _pager) { - std::ostringstream s; - - s << "Using pager '" << _pagerCommand << "' for output buffering."; - - printLine(s.str()); + if (_quiet) { + return; } + + std::ostringstream s; + + if (_pager) { + s << "Using pager '" << _pagerCommand << "' for output buffering. "; + } + + if (_useHistory) { + s << "Command-line history will be persisted when the shell is exited."; + } else { + s << "Command-line history is enabled for this session only and will *not* be persisted."; + } + + printLine(s.str()); } void ConsoleFeature::printByeBye() { diff --git a/arangosh/Shell/ConsoleFeature.h b/arangosh/Shell/ConsoleFeature.h index 92dd589f60..880c484c0f 100644 --- a/arangosh/Shell/ConsoleFeature.h +++ b/arangosh/Shell/ConsoleFeature.h @@ -41,6 +41,7 @@ class ConsoleFeature final : public application_features::ApplicationFeature { bool quiet() const { return _quiet; } void setQuiet(bool value) { _quiet = value; } bool colors() const { return _colors; } + bool useHistory() const { return _useHistory; } bool autoComplete() const { return _autoComplete; } bool prettyPrint() const { return _prettyPrint; } bool pager() const { return _pager; } @@ -54,6 +55,7 @@ class ConsoleFeature final : public application_features::ApplicationFeature { #endif bool _quiet; bool _colors; + bool _useHistory; bool _autoComplete; bool _prettyPrint; std::string _auditFile; diff --git a/arangosh/Shell/V8ClientConnection.cpp b/arangosh/Shell/V8ClientConnection.cpp index f9f9e62cd3..a6e673b507 100644 --- a/arangosh/Shell/V8ClientConnection.cpp +++ b/arangosh/Shell/V8ClientConnection.cpp @@ -60,6 +60,7 @@ V8ClientConnection::V8ClientConnection() _lastErrorMessage(""), _version("arango"), _mode("unknown mode"), + _role("UNKNOWN"), _loop(1), _vpackOptions(VPackOptions::Defaults) { _vpackOptions.buildUnindexedObjects = true; @@ -127,6 +128,10 @@ void V8ClientConnection::createConnection() { if (mode.isString()) { _mode = mode.copyString(); } + VPackSlice role = details.get("role"); + if (role.isString()) { + _role = role.copyString(); + } } std::string const versionString = VelocyPackHelper::getStringValue(body, "version", ""); @@ -229,7 +234,7 @@ void V8ClientConnection::reconnect(ClientFeature* client) { LOG_TOPIC(INFO, arangodb::Logger::FIXME) << "Connected to ArangoDB " << "'" << endpointSpecification() << "', " - << "version " << _version << " [" << _mode << "], " + << "version " << _version << " [" << _role << ", " << _mode << "], " << "database '" << _databaseName << "', " << "username: '" << client->username() << "'"; } else { @@ -355,7 +360,7 @@ static void ClientConnection_ConstructorCallback(v8::FunctionCallbackInfoendpointSpecification() << "', " - << "version " << v8connection->version() << " [" << v8connection->mode() << "], " + << "version " << v8connection->version() << " [" << v8connection->role() << ", " << v8connection->mode() << "], " << "database '" << v8connection->databaseName() << "', " << "username: '" << v8connection->username() << "'"; @@ -1279,6 +1284,30 @@ static void ClientConnection_getMode(v8::FunctionCallbackInfo const& TRI_V8_TRY_CATCH_END } +//////////////////////////////////////////////////////////////////////////////// +/// @brief ClientConnection method "getRole" +//////////////////////////////////////////////////////////////////////////////// + +static void ClientConnection_getRole(v8::FunctionCallbackInfo const& args) { + TRI_V8_TRY_CATCH_BEGIN(isolate); + v8::HandleScope scope(isolate); + + // get the connection + V8ClientConnection* v8connection = + TRI_UnwrapClass(args.Holder(), WRAP_TYPE_CONNECTION, TRI_IGETC); + + if (v8connection == nullptr) { + TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted"); + } + + if (args.Length() != 0) { + TRI_V8_THROW_EXCEPTION_USAGE("getRole()"); + } + + TRI_V8_RETURN_STD_STRING(v8connection->role()); + TRI_V8_TRY_CATCH_END +} + //////////////////////////////////////////////////////////////////////////////// /// @brief ClientConnection method "getDatabaseName" //////////////////////////////////////////////////////////////////////////////// @@ -1755,6 +1784,9 @@ void V8ClientConnection::initServer(v8::Isolate* isolate, v8::Local connection_proto->Set(isolate, "getMode", v8::FunctionTemplate::New(isolate, ClientConnection_getMode)); + + connection_proto->Set(isolate, "getRole", + v8::FunctionTemplate::New(isolate, ClientConnection_getRole)); connection_proto->Set(isolate, "getDatabaseName", v8::FunctionTemplate::New(isolate, ClientConnection_getDatabaseName)); diff --git a/arangosh/Shell/V8ClientConnection.h b/arangosh/Shell/V8ClientConnection.h index 313f324838..7c7df7fc91 100644 --- a/arangosh/Shell/V8ClientConnection.h +++ b/arangosh/Shell/V8ClientConnection.h @@ -78,6 +78,7 @@ class V8ClientConnection { std::string lastErrorMessage() const { return _lastErrorMessage; } std::string const& version() const { return _version; } std::string const& mode() const { return _mode; } + std::string const& role() const { return _role; } std::string endpointSpecification() const; v8::Handle getData(v8::Isolate* isolate, arangodb::velocypack::StringRef const& location, @@ -146,6 +147,7 @@ class V8ClientConnection { std::string _lastErrorMessage; std::string _version; std::string _mode; + std::string _role; fuerte::EventLoopService _loop; fuerte::ConnectionBuilder _builder; diff --git a/arangosh/Shell/V8ShellFeature.cpp b/arangosh/Shell/V8ShellFeature.cpp index 057ad269bb..f66e9920ad 100644 --- a/arangosh/Shell/V8ShellFeature.cpp +++ b/arangosh/Shell/V8ShellFeature.cpp @@ -343,7 +343,7 @@ bool V8ShellFeature::printHello(V8ClientConnection* v8connection) { std::ostringstream is; is << "Connected to ArangoDB '" << v8connection->endpointSpecification() - << "' version: " << v8connection->version() << " [" + << "' version: " << v8connection->version() << " [" << v8connection->role() << ", " << v8connection->mode() << "], database: '" << v8connection->databaseName() << "', username: '" << v8connection->username() << "'"; @@ -351,7 +351,8 @@ bool V8ShellFeature::printHello(V8ClientConnection* v8connection) { } else { std::ostringstream is; - is << "Could not connect to endpoint '" << v8connection->endpointSpecification() + auto client = server()->getFeature("Client"); + is << "Could not connect to endpoint '" << client->endpoint() << "', database: '" << v8connection->databaseName() << "', username: '" << v8connection->username() << "'"; @@ -424,7 +425,7 @@ int V8ShellFeature::runShell(std::vector const& positionals) { bool promptError; auto v8connection = setup(context, true, positionals, &promptError); - V8LineEditor v8LineEditor(_isolate, context, "." + _name + ".history"); + V8LineEditor v8LineEditor(_isolate, context, _console->useHistory() ? "." + _name + ".history" : ""); if (v8connection != nullptr) { v8LineEditor.setSignalFunction( diff --git a/lib/Utilities/LinenoiseShell.cpp b/lib/Utilities/LinenoiseShell.cpp index 15878f8d71..171a113543 100644 --- a/lib/Utilities/LinenoiseShell.cpp +++ b/lib/Utilities/LinenoiseShell.cpp @@ -63,7 +63,9 @@ LinenoiseShell::LinenoiseShell(std::string const& history, Completer* completer) LinenoiseShell::~LinenoiseShell() { COMPLETER = nullptr; } bool LinenoiseShell::open(bool) { - linenoiseHistoryLoad(_historyFilename.c_str()); + if (!_historyFilename.empty()) { + linenoiseHistoryLoad(_historyFilename.c_str()); + } _state = STATE_OPENED; return true; } @@ -89,7 +91,9 @@ void LinenoiseShell::addHistory(std::string const& str) { } bool LinenoiseShell::writeHistory() { - linenoiseHistorySave(_historyFilename.c_str()); + if (!_historyFilename.empty()) { + linenoiseHistorySave(_historyFilename.c_str()); + } return true; } diff --git a/lib/Utilities/ShellBase.cpp b/lib/Utilities/ShellBase.cpp index 2104f40583..205a2cdea0 100644 --- a/lib/Utilities/ShellBase.cpp +++ b/lib/Utilities/ShellBase.cpp @@ -54,14 +54,18 @@ void ShellBase::sortAlternatives(std::vector& completions) { ShellBase::ShellBase(std::string const& history, Completer* completer) : _current(), _historyFilename(), _state(STATE_NONE), _completer(completer) { // construct the complete history path - std::string path(TRI_HomeDirectory()); + if (!history.empty()) { + // note: if history is empty, we will not write any history and not + // construct the full filename + std::string path(TRI_HomeDirectory()); - if (!path.empty() && path[path.size() - 1] != TRI_DIR_SEPARATOR_CHAR) { - path.push_back(TRI_DIR_SEPARATOR_CHAR); + if (!path.empty() && path[path.size() - 1] != TRI_DIR_SEPARATOR_CHAR) { + path.push_back(TRI_DIR_SEPARATOR_CHAR); + } + path.append(history); + + _historyFilename = path; } - path.append(history); - - _historyFilename = path; } ShellBase::~ShellBase() { delete _completer; }