8000 enterprise license support (#14753) · arangodb/arangodb@9e3e291 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9e3e291

Browse files
kvahedjsteemannKVS85
authored
enterprise license support (#14753)
* steps * devel merge * devel merge * add feature * added expiry metric * change log update * serverstate readonly modes * repaired tests * mock framework to embrace licensefeatrue * Update lib/Basics/errors.dat Co-authored-by: Jan <jsteemann@users.noreply.github.com> * Update lib/Basics/errors.dat Co-authored-by: Jan <jsteemann@users.noreply.github.com> * Update scripts/startLocalCluster.sh Co-authored-by: Jan <jsteemann@users.noreply.github.com> * metric docs * jan comments * Update CHANGELOG Co-authored-by: Jan <jsteemann@users.noreply.github.com> * Node behaviour fixed for toBuilder and showHidden true * Node behaviour fixed for toBuilder and showHidden true * review * review * shell features * .agency mode * metric naming * correct counting of raft index * read only visibility * Update js/client/modules/@arangodb/testsuites/license.js Co-authored-by: Jan <jsteemann@users.noreply.github.com> * promtool * promtool * arangosh cleanup * iresearch tests fixed * repair user manager tests * test link ambiguity of cosmic black matter dientangled * overlapping namespaces and Step class fixed * fixed last persistent error * break non failure_test builds * Update CHANGELOG Co-authored-by: Jan <jsteemann@users.noreply.github.com> Co-authored-by: Vadim <vadim@arangodb.com>
1 parent eec3f1c commit 9e3e291

23 files changed

+297
-34
lines changed

CHANGELOG

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
devel
22
-----
33

4+
<<<<<<< HEAD
5+
* Added enterprise licensing error codes and metrics support.
6+
=======
7+
* Added enterprise licensing support including (only for Enterprise version):
8+
- additional API endpoint `_admin/license(GET/PUT)?force=true <text>`
9+
- arangosh functions: `setLicense(<text>)`, `getLicense()`
10+
- new error codes and metrics support
11+
>>>>>>> 32cfbe6ff959c4696f9576690bf9b8e84dba4e37
12+
413
* Fix issue #14807: Fix crash during optimization of certain AQL queries during
514
the remove-collect-variables optimizer rule, when a COLLECT node without output
615
variables (this includes RETURN DISTINCT) occured in the plan.
716

817
* Update iresearch library to the upstream. Fixed TSan/ASan detected issues.
918

10-
* Added new ArangoSearch analyzer type 'collation'
19+
* Added new ArangoSearch analyzer type 'collation'.
1120

1221

1322
* Add basic overload control to arangod.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: arangodb_license_expires
2+
introducedIn: "3.9.1"
3+
help: |
4+
This instance's license expiry in days.
5+
unit: number
6+
type: gauge
7+
category: License
8+
complexity: simple
9+
exposedBy:
10+
- coordinator
11+
- dbserver
12+
description: |
13+
This instance's remaining license validity time.

arangod/Agency/Node.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -934,7 +934,7 @@ void Node::toBuilder(Builder& builder, bool showHidden) const {
934934
continue;
935935
}
936936
builder.add(VPackValue(child.first));
937-
cptr->toBuilder(builder);
937+
cptr->toBuilder(builder, showHidden);
938938
}
939939
}
940940
} else {

arangod/Agency/Store.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ bool Store::read(VPackSlice const& query, Builder& ret) const {
668668
std::vector<std::string> query_strs;
669669
for (auto const& sub_query : VPackArrayIterator(query)) {
670670
query_strs.emplace_back(sub_query.copyString());
671-
showHidden |= (query_strs.back().find("/.") != std::string::npos);
671+
showHidden |= (query_strs.back().find('.') == 0 || (query_strs.back().find("/.") != std::string::npos));
672672
}
673673

674674
// Remove double ranges (inclusion / identity)

arangod/Cluster/HeartbeatThread.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ void HeartbeatThread::runSingleServer() {
923923
ttlFeature.allowRunning(false);
924924

925925
ServerState::instance()->setFoxxmaster(leaderStr); // leader is foxxmater
926-
ServerState::instance()->setReadOnly(true); // Disable writes with dirty-read header
926+
ServerState::instance()->setReadOnly(ServerState::API_TRUE); // Disable writes with dirty-read header
927927

928928
std::string endpoint = ci.getServerEndpoint(leaderStr);
929929
if (endpoint.empty()) {
@@ -1052,7 +1052,7 @@ void HeartbeatThread::updateServerMode(VPackSlice const& readOnlySlice) {
10521052
readOnly = readOnlySlice.getBool();
10531053
}
10541054

1055-
ServerState::instance()->setReadOnly(readOnly);
1055+
ServerState::instance()->setReadOnly(readOnly ? ServerState::API_TRUE : ServerState::API_FALSE);
10561056
}
10571057

10581058
////////////////////////////////////////////////////////////////////////////////

arangod/Cluster/ServerState.cpp

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -322,14 +322,34 @@ ServerState::Mode ServerState::setServerMode(ServerState::Mode value) {
322322
}
323323

324324
static std::atomic<bool> _serverstate_readonly(false);
325+
static std::atomic<bool> _license_readonly(false);
325326

326327
bool ServerState::readOnly() {
328+
return _serverstate_readonly.load(std::memory_order_acquire) ||
329+
_license_readonly.load(std::memory_order_acquire);
330+
}
331+
332+
bool ServerState::readOnlyByAPI() {
327333
return _serverstate_readonly.load(std::memory_order_acquire);
328334
}
329335

336+
bool ServerState::readOnlyByLicense() {
337+
return _license_readonly.load(std::memory_order_acquire);
338+
}
339+
330340
/// @brief set server read-only
331-
bool ServerState::setReadOnly(bool ro) {
332-
return _serverstate_readonly.exchange(ro, std::memory_order_release);
341+
bool ServerState::setReadOnly(ReadOnlyMode ro) {
342+
auto ret = readOnly();
343+
if(ro == API_FALSE) {
344+
_serverstate_readonly.exchange(false, std::memory_order_release);
345+
} else if (ro == API_TRUE) {
346+
_serverstate_readonly.exchange(true, std::memory_order_release);
347+
} else if (ro == LICENSE_FALSE) {
348+
_license_readonly.exchange(false, std::memory_order_release);
349+
} else if (ro == LICENSE_TRUE) {
350+
_license_readonly.exchange(true, std::memory_order_release);
351+
}
352+
return ret;
333353
}
334354

335355
// ============ Instance methods =================
@@ -371,7 +391,7 @@ bool ServerState::logoff(double timeout) {
371391

372392
AgencyWriteTransaction unregisterTransaction(operations);
373393
AgencyComm comm(_server);
374-
394+
375395
// Try only once to unregister because maybe the agencycomm
376396
// is shutting down as well...
377397
int maxTries = static_cast<int>(timeout / 3.0);;
@@ -547,7 +567,7 @@ std::string ServerState::roleToAgencyKey(ServerState::RoleEnum role) {
547567
return "Coordinator";
548568
case ROLE_SINGLE:
549569
return "Single";
550-
case ROLE_AGENT:
570+
case ROLE_AGENT:
551571
return "Agent";
552 B94F 572
case ROLE_UNDEFINED: {
553573
return "Undefined";
@@ -1108,9 +1128,9 @@ bool ServerState::isFoxxmaster() const {
11081128
return /*!isRunningInCluster() ||*/ _foxxmaster == getId();
11091129
}
11101130

1111-
std::string ServerState::getFoxxmaster() const {
1131+
std::string ServerState::getFoxxmaster() const {
11121132
READ_LOCKER(readLocker, _foxxmasterLock);
1113-
return _foxxmaster;
1133+
return _foxxmaster;
11141134
}
11151135

11161136
void ServerState::setFoxxmaster(std::string const& foxxmaster) {
@@ -1167,6 +1187,6 @@ Result ServerState::propagateClusterReadOnly(bool mode) {
11671187
// the change, we delay here for 2 seconds.
11681188
std::this_thread::sleep_for(std::chrono::seconds(2));
11691189
}
1170-
setReadOnly(mode);
1190+
setReadOnly(mode ? API_TRUE : API_FALSE);
11711191
return Result();
11721192
}

arangod/Cluster/ServerState.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ class ServerState {
5757
STATE_SHUTDOWN // used by all roles
5858
};
5959

60+
enum ReadOnlyMode {
61+
API_TRUE, // Set from outside via API
62+
API_FALSE,
63+
LICENSE_TRUE, // Set from license feature
64+
LICENSE_FALSE
65+
};
66+
6067
enum class Mode : uint8_t {
6168
DEFAULT = 0,
6269
/// reject all requests
@@ -116,8 +123,14 @@ class ServerState {
116123
/// @brief should not allow DDL operations / transactions
117124
static bool readOnly();
118125

126+
/// @brief should not allow DDL operations / transactions
127+
static bool readOnlyByLicense();
128+
129+
/// @brief should not allow DDL operations / transactions
130+
static bool readOnlyByAPI();
131+
119132
/// @brief set server read-only
120-
static bool setReadOnly(bool ro);
133+
static bool setReadOnly(ReadOnlyMode);
121134

122135
public:
123136
/// @brief sets the initialized flag

arangod/GeneralServer/GeneralServerFeature.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110

111111
#ifdef USE_ENTERPRISE
112112
#include "Enterprise/RestHandler/RestHotBackupHandler.h"
113+
#include "Enterprise/RestHandler/RestLicenseHandler.h"
113114
#include "Enterprise/StorageEngine/HotBackupFeature.h"
114115
#endif
115116

@@ -660,8 +661,10 @@ void GeneralServerFeature::defineHandlers() {
660661
#ifdef USE_ENTERPRISE
661662
if (backup.isAPIEnabled()) {
662663
_handlerFactory->addPrefixHandler("/_admin/backup",
663-
RestHandlerCreator<arangodb::RestHotBackupHandler>::createNoData);
664+
RestHandlerCreator<arangodb::RestHotBackupHandler>::createNoData);
664665
}
666+
_handlerFactory->addPrefixHandler("/_admin/license",
667+
RestHandlerCreator<arangodb::RestLicenseHandler>::createNoData);
665668
#endif
666669

667670

js/client/modules/@arangodb/arango-database.js

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,11 @@ var helpArangoDatabase = arangosh.createHelpHeadline('ArangoDatabase (db) help')
238238
' _view(<name>) get view by name ' + '\n' +
239239
' _createView(<name>, <type>, creates a new view ' + '\n' +
240240
' <properties>) ' + '\n' +
241-
' _dropView(<name>) delete a view ';
241+
' _dropView(<name>) delete a view ' + '\n' +
242+
' ' + '\n' +
243+
'License Functions: ' + '\n' +
244+
' _getLicense() get license information ' + '\n' +
245+
' _setLicense(<license-string>) set license string ';
242246

243247
ArangoDatabase.prototype._help = function () {
244248
internal.print(helpArangoDatabase);
@@ -252,6 +256,43 @@ ArangoDatabase.prototype.toString = function () {
252256
return '[object ArangoDatabase "' + this._name() + '"]';
253257
};
254258

259+
// //////////////////////////////////////////////////////////////////////////////
260+
// / @brief return license information
261+
// //////////////////////////////////////////////////////////////////////////////
262+
263+
ArangoDatabase.prototype._getLicense = function (options) {
264+
let url = "/_admin/license";
265+
var requestResult = this._connection.GET(url, {});
266+
267+
arangosh.checkRequestResult(requestResult);
268+
269+
return requestResult;
270+
};
271+
272+
// //////////////////////////////////////////////////////////////////////////////
273+
// / @brief return license information
274+
// //////////////////////////////////////////////////////////////////////////////
275+
276+
ArangoDatabase.prototype._setLicense = function (data, options) {
277+
let url = "/_admin/license?";
278+
if (options && options.force) {
279+
url += "force=true";
280+
}
281+
if (typeof data !== 'string') {
282+
throw new ArangoError({
283+
error: true,
284+
code: internal.errors.ERROR_HTTP_PRECONDITION_FAILED.code,
285+
errorNum: internal.errors.ERROR_LICENSE_INVALID.code,
286+
errorMessage: "License body must be a string. It is " + (typeof data)
287+
});
288+
}
289+
290+
var requestResult = this._connection.PUT(url, data);
291+
arangosh.checkRequestResult(requestResult);
292+
293+
return requestResult.result;
294+
};
295+
255296
// //////////////////////////////////////////////////////////////////////////////
256297
// / @brief compacts the entire database
257298
// //////////////////////////////////////////////////////////////////////////////
@@ -260,7 +301,7 @@ ArangoDatabase.prototype._compact = function (options) {
260301
let url = "/_admin/compact?";
261302
if (options && options.changeLevel) {
262303
url += "changeLevel=true&";
263-
}
304+
}
264305
if (options && options.bottomMost) {
265306
url += "bottomMost=true";
266307
}
@@ -353,7 +394,7 @@ ArangoDatabase.prototype._create = function (name, properties, type, options) {
353394
options = type;
354395
type = undefined;
355396
}
356-
397+
357398
let urlAddons = [];
358399
if (typeof options === "object" && options !== null) {
359400
if (options.hasOwnProperty('waitForSyncReplication')) {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/* jshint strict: false, sub: true */
2+
/* global print */
3+
'use strict';
4+
5+
// ////////////////////////////////////////////////////////////////////////////
6+
// DISCLAIMER
7+
//
8+
// Copyright 2021 ArangoDB Inc, Cologne, Germany
9+
//
10+
// Licensed under the Apache License, Version 2.0 (the "License")
11+
// you may not use this file except in compliance with the License.
12+
// You may obtain a copy of the License at
13+
//
14+
// http://www.apache.org/licenses/LICENSE-2.0
15+
//
16+
// Unless required by applicable law or agreed to in writing, software
17+
// distributed under the License is distributed on an "AS IS" BASIS,
18+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19+
// See the License for the specific language governing permissions and
20+
// limitations under the License.
21+
//
22+
// Copyright holder is ArangoDB Inc, Cologne, Germany
23+
//
24+
// @author Kaveh Vahedipour
25+
// //////////////////////////////////////////////////////////////////////////////
26+
27+
const functionsDocumentation = {
28+
'license': 'license tests'
29+
};
30+
const optionsDocumentation = [
31+
' - `skipLicense` : if set to true the license tests are skipped',
32+
];
33+
34+
const _ = require('lodash');
35+
const tu = require('@arangodb/testutils/test-utils');
36+
const fs = require('fs');
37+
const request = require('@arangodb/request');
38+
const crypto = require('@arangodb/crypto');
39+
40+
// const BLUE = require('internal').COLORS.COLOR_BLUE;
41+
const CYAN = require('internal').COLORS.COLOR_CYAN;
42+
// const GREEN = require('internal').COLORS.COLOR_GREEN;
43+
// const RED = require('internal').COLORS.COLOR_RED;
44+
const RESET = require('internal').COLORS.COLOR_RESET;
45+
// const YELLOW = require('internal').COLORS.COLOR_YELLOW;
46+
47+
const testPaths = {
48+
'license': [tu.pathForTesting('client/license')],
49+
};
50+
51+
// //////////////////////////////////////////////////////////////////////////////
52+
// / @brief Shared conf
53+
// //////////////////////////////////////////////////////////////////////////////
54+
55+
exports.setup = function(testFns, defaultFns, opts, fnDocs, optionsDoc, allTestPaths) {
56+
Object.assign(allTestPaths, testPaths);
57+
// just a convenience wrapper for the regular tests
58+
testFns['license'] = ['license-api'];
59+
60+
// turn off license tests by default.
61+
opts['skipLicense'] = true;
62+
63+
// only enable them in Enterprise Edition
64+
let version = {};
65+
if (global.ARANGODB_CLIENT_VERSION) {
66+
version = global.ARANGODB_CLIENT_VERSION(true);
67+
if (version['enterprise-version']) {
68+
opts['skipLicense'] = false;
69+
}
70+
}
71+
72+
for (var attrname in functionsDocumentation) {
73+
fnDocs[attrname] = functionsDocumentation[attrname];
74+
}
75+
for (var i = 0; i < optionsDocumentation.length; i++) {
76+
optionsDoc.push(optionsDocumentation[i]);
77+
}
78+
};

0 commit comments

Comments
 (0)
0