1
0
Fork 0
arangodb/js/server/modules/@arangodb/cluster/kickstarter.js

1500 lines
47 KiB
JavaScript

/*jshint strict: false, unused: false */
/*global ArangoServerState */
////////////////////////////////////////////////////////////////////////////////
/// @brief Cluster kickstarting functionality using dispatchers
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 triagens 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 triAGENS GmbH, Cologne, Germany
///
/// @author Max Neunhoeffer
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var download = require("internal").download;
var executeExternal = require("internal").executeExternal;
var killExternal = require("internal").killExternal;
var statusExternal = require("internal").statusExternal;
var base64Encode = require("internal").base64Encode;
var fs = require("fs");
var wait = require("internal").wait;
var toArgv = require("internal").toArgv;
var exchangePort = require("@arangodb/cluster/planner").exchangePort;
var exchangeProtocol = require("@arangodb/cluster/planner").exchangeProtocol;
var console = require("console");
var launchActions = {};
var shutdownActions = {};
var cleanupActions = {};
var isHealthyActions = {};
var upgradeActions = {};
var getAddrPort = require("@arangodb/cluster/planner").getAddrPort;
var getPort = require("@arangodb/cluster/planner").getPort;
var endpointToURL = require("@arangodb/cluster/planner").endpointToURL;
function extractErrorMessage (result) {
if (result === undefined) {
return "unknown error";
}
if (result.hasOwnProperty("body") && result.body !== undefined) {
try {
var e = JSON.parse(result.body);
if (e.hasOwnProperty("errorMessage")) {
return e.errorMessage;
}
if (e.hasOwnProperty("errorNum")) {
return e.errorNum;
}
if (result.body !== "") {
return result.body;
}
}
catch (err) {
if (result.body !== "") {
return result.body;
}
}
}
if (result.hasOwnProperty("message")) {
return result.message;
}
if (result.hasOwnProperty("code")) {
return String(result.code);
}
return "unknown error";
}
function makePath (path) {
return fs.join.apply(null,path.split("/"));
}
function encode (st) {
var st2 = "";
var i;
for (i = 0; i < st.length; i++) {
if (st[i] === "_") {
st2 += "@U";
}
else if (st[i] === "@") {
st2 += "@@";
}
else {
st2 += st[i];
}
}
return encodeURIComponent(st2);
}
function getAuthorizationHeader (username, passwd) {
return "Basic " + base64Encode(username + ":" + passwd);
}
function getAuthorization (dispatcher) {
return getAuthorizationHeader(dispatcher.username, dispatcher.passwd);
}
function waitForServerUp (cmd, endpoint, timeout) {
var url = endpointToURL(endpoint) + "/_api/version";
var time = 0;
while (true) {
var r = download(url, "", {});
if (r.code === 200 || r.code === 401) {
if (cmd.extremeVerbosity) {
console.info("Could talk to "+endpoint+" .");
}
return true;
}
if (time >= timeout) {
console.info("Could not talk to endpoint "+endpoint+", giving up.");
return false;
}
wait(0.5);
time += 0.5;
}
}
function waitForServerDown (cmd, endpoint, timeout) {
var url = endpointToURL(endpoint) + "/_api/version";
var time = 0;
while (true) {
var r = download(url, "", {});
if (r.code === 500 || r.code === 401) {
if (cmd.extremeVerbosity) {
console.info("Server at " + endpoint + " does not answer any more.");
}
return true;
}
if (time >= timeout) {
console.info("After timeout, server at " + endpoint +
"is still there, giving up.");
return false;
}
wait(0.5);
time += 0.5;
}
}
function sendToAgency (cmd, agencyURL, path, obj) {
var res;
var body;
if (cmd.extremeVerbosity) {
console.info("Sending %s to agency...", path);
}
if (typeof obj === "string") {
var count = 0;
while (count++ <= 2) {
body = "value=" + encodeURIComponent(obj);
res = download(agencyURL + path,body,
{"method":"PUT", "followRedirects": true,
"headers": { "Content-Type": "application/x-www-form-urlencoded"}});
if (res.code === 201 || res.code === 200) {
return true;
}
wait(3); // wait 3 seconds before trying again
}
return res;
}
if (typeof obj !== "object") {
return "Strange object found: not a string or object";
}
var keys = Object.keys(obj);
var i;
if (keys.length !== 0) {
for (i = 0; i < keys.length; i++) {
res = sendToAgency(cmd, agencyURL, path + "/" + encode(keys[i]), obj[keys[i]]);
if (res !== true) {
return res;
}
}
return true;
}
// Create a directory
var count2 = 0;
while (count2++ <= 2) {
body = "dir=true";
res = download(agencyURL+path,body,
{"method": "PUT", "followRedirects": true,
"headers": { "Content-Type": "application/x-www-form-urlencoded"}});
if (res.code === 201 || res.code === 200) {
return true;
}
wait(3); // wait 3 seconds before trying again
}
return res;
}
launchActions.startAgent = function (dispatchers, cmd, isRelaunch) {
var yaml = require("js-yaml");
console.info("Starting agent...");
var dataPath = fs.makeAbsolute(cmd.dataPath);
if (dataPath !== cmd.dataPath) { // path was relative
dataPath = fs.normalize(fs.join(ArangoServerState.dataPath(),cmd.dataPath));
}
var agentDataDir = fs.join(dataPath, "agent"+cmd.agencyPrefix+cmd.extPort);
if (!isRelaunch) {
if (fs.exists(agentDataDir)) {
fs.removeDirectoryRecursive(agentDataDir,true);
}
}
var instanceName = "agent"+cmd.agencyPrefix+cmd.extPort;
var extEndpoint = getAddrPort(exchangePort(dispatchers[cmd.dispatcher].endpoint, cmd.extPort));
var extBind = cmd.onlyLocalhost ? ("127.0.0.1:"+cmd.extPort) : extEndpoint;
var clusterEndPoint = getAddrPort(exchangePort(dispatchers[cmd.dispatcher].endpoint, cmd.intPort));
var clusterBind = cmd.onlyLocalhost ? ("127.0.0.1:"+cmd.intPort) : clusterEndPoint;
var clusterUrl = "http://" + clusterEndPoint;
var agencyUrl = "http://" + extBind;
if (require("internal").platform.substr(0,3) === 'win') {
agentDataDir = agentDataDir.split("\\").join("/");
}
var args = {
"data-dir": agentDataDir,
"name": instanceName,
"bind-addr": extBind,
"addr": extEndpoint,
"peer-bind-addr": clusterBind,
"peer-addr": clusterEndPoint,
"initial-cluster-state": "new",
"initial-cluster": instanceName + "=" + clusterUrl
// the following might speed up etcd, but it might also
// make it more unstable:
// ,"peer-heartbeat-timeout": "10",
// "peer-election-timeout": "20"
};
var i;
if (cmd.peers.length > 0) {
var st = getAddrPort(cmd.peers[0]);
for (i = 1; i < cmd.peers.length; i++) {
st = st + "," + getAddrPort(cmd.peers[i]);
}
args.peers = st;
}
var agentPath = cmd.agentPath;
if (agentPath === "") {
agentPath = ArangoServerState.agentPath();
}
if (! fs.exists(agentPath)) {
return {"error":true, "isStartAgent": true,
"errorMessage": "agency binary not found at '" + agentPath + "'"};
}
var pid = executeExternal(agentPath, toArgv(args));
var res;
var count = 0;
while (++count < 20) {
wait(0.5); // Wait a bit to give it time to startup
res = download(agencyUrl + "/v2/stats/self");
if (res.code === 200) {
return {"error":false, "isStartAgent": true, "pid": pid,
"endpoint": "tcp://"+extEndpoint};
}
res = statusExternal(pid, false);
if (res.status !== "RUNNING") {
return {"error":true, "isStartAgent": true,
"errorMessage": "agency failed to start:\n" + yaml.safeDump({"exit status": res})};
}
}
killExternal(pid);
return {"error":true, "isStartAgent": true,
"errorMessage": "agency did not come alive on time, giving up."};
};
launchActions.sendConfiguration = function (dispatchers, cmd, isRelaunch) {
if (isRelaunch) {
// nothing to do here
console.info("Waiting 1 second for agency to come alive...");
wait(1);
return {"error":false, "isSendConfiguration": true};
}
var url = endpointToURL(cmd.agency.endpoints[0]) + "/v2/keys";
var res = sendToAgency(cmd, url, "", cmd.data);
if (res === true) {
return {"error":false, "isSendConfiguration": true};
}
var yaml = require("js-yaml");
return {"error":true, "isSendConfiguration": true, "suberror": res, errorMessage : yaml.safeDump(res)};
};
launchActions.startServers = function (dispatchers, cmd, isRelaunch) {
var dataPath = fs.makeAbsolute(cmd.dataPath);
if (dataPath !== cmd.dataPath) { // path was relative
dataPath = fs.normalize(fs.join(ArangoServerState.dataPath(),cmd.dataPath));
}
var logPath = fs.makeAbsolute(cmd.logPath);
if (logPath !== cmd.logPath) { // path was relative
logPath = fs.normalize(fs.join(ArangoServerState.logPath(), cmd.logPath));
}
var url = endpointToURL(cmd.agency.endpoints[0]) + "/v2/keys/"+
cmd.agency.agencyPrefix + "/";
if (cmd.extremeVerbosity) {
console.info("Downloading %sLaunchers/%s", url, encode(cmd.name));
}
var res = download(url + "Dispatcher/Launchers/" + encode(cmd.name), "", { method: "GET",
followRedirects: true });
if (res.code !== 200) {
return {"error": true, "isStartServers": true, "suberror": res};
}
var body = JSON.parse( res.body );
var info = JSON.parse(body.node.value);
var id, ep, args, pids, port, endpoints, endpointNames, roles;
console.info("Starting servers...");
var i;
var servers = info.DBservers.concat(info.Coordinators);
roles = [];
for (i = 0; i < info.DBservers.length; i++) {
roles.push("PRIMARY");
}
for (i = 0; i < info.Coordinators.length; i++) {
roles.push("COORDINATOR");
}
pids = [];
endpoints = [];
endpointNames = [];
for (i = 0; i < servers.length; i++) {
id = servers[i];
var serverUrl = url + "Dispatcher/Endpoints/" + encodeURIComponent(id);
if (cmd.extremeVerbosity) {
console.info("Downloading ", serverUrl);
}
res = download(serverUrl);
if (res.code !== 200) {
return {"error": true, "pids": pids,
"isStartServers": true, "suberror": res};
}
console.info("Starting server %s",id);
body = JSON.parse(res.body);
ep = body.node.value;
port = getPort(ep);
var useSSL = false;
if (roles[i] === "PRIMARY") {
args = ["--configuration", ArangoServerState.dbserverConfig()];
useSSL = cmd.useSSLonDBservers;
}
else {
args = ["--configuration", ArangoServerState.coordinatorConfig()];
useSSL = cmd.useSSLonCoordinators;
}
args = args.concat([
"--cluster.disable-dispatcher-kickstarter", "true",
"--cluster.disable-dispatcher-frontend", "true",
"--cluster.my-local-info", id,
"--cluster.my-role", roles[i],
"--cluster.my-address", ep,
"--cluster.agency-prefix", cmd.agency.agencyPrefix,
"--cluster.agency-endpoint", cmd.agency.endpoints[0],
"--server.endpoint"]);
if (cmd.onlyLocalhost) {
args.push(exchangeProtocol("tcp://127.0.0.1:" + port, useSSL));
}
else {
args.push(exchangeProtocol("tcp://0.0.0.0:" + port, useSSL));
}
args.push("--log.file");
var logfile = fs.join(logPath, "log-" + cmd.agency.agencyPrefix + "-" + id);
args.push(logfile);
if (!isRelaunch) {
if (!fs.exists(logPath)) {
fs.makeDirectoryRecursive(logPath);
}
if (fs.exists(logfile)) {
fs.remove(logfile);
}
}
var datadir = fs.join(dataPath, "data-" + cmd.agency.agencyPrefix + "-" + id);
if (!isRelaunch) {
if (!fs.exists(dataPath)) {
fs.makeDirectoryRecursive(dataPath);
}
if (fs.exists(datadir)) {
fs.removeDirectoryRecursive(datadir,true);
}
fs.makeDirectory(datadir);
}
args.push("--database.directory");
args.push(datadir);
args = args.concat(dispatchers[cmd.dispatcher].arangodExtraArgs);
var arangodPath = fs.makeAbsolute(cmd.arangodPath);
if (arangodPath !== cmd.arangodPath) {
arangodPath = ArangoServerState.arangodPath();
}
if ((cmd.valgrind !== '') &&
(cmd.valgrindHosts !== undefined) &&
(cmd.valgrindHosts.indexOf(roles[i]) > -1)) {
var valgrindOpts = cmd.valgrindOpts.concat(
["--xml-file=" + cmd.valgrindXmlFileBase + '_' + cmd.valgrindTestname + '_' + id + '.%p.xml',
"--log-file=" + cmd.valgrindXmlFileBase + '_' + cmd.valgrindTestname + '_' + id + '.%p.valgrind.log']);
var newargs = valgrindOpts.concat([arangodPath]).concat(args);
var cmdline = cmd.valgrind;
pids.push(executeExternal(cmdline, newargs));
}
else {
pids.push(executeExternal(arangodPath, args));
}
ep = exchangePort(dispatchers[cmd.dispatcher].endpoint,port);
ep = exchangeProtocol(ep,useSSL);
endpoints.push(ep);
endpointNames.push(id);
}
var error = false;
for (i = 0;i < endpoints.length;i++) {
var timeout = 50;
if (cmd.valgrind !== '') {
timeout *= 10000;
}
if (! waitForServerUp(cmd, endpoints[i], timeout)) {
error = true;
}
}
return {"error": error,
"isStartServers": true,
"pids": pids,
"endpoints": endpoints,
"endpointNames": endpointNames,
"roles": roles};
};
launchActions.bootstrapServers = function (dispatchers, cmd, isRelaunch,
username, password) {
var coordinators = cmd.coordinators;
if (username === undefined && password === undefined) {
username = "root";
password = "";
}
// we need at least one coordinator
if (0 === coordinators.length) {
return {"error": true, "bootstrapServers": true, "errorMessage": "No coordinators to start"};
}
// authorization header for coordinator
var hdrs = {
Authorization: getAuthorizationHeader(username, password)
};
// default options
var timeout = 90;
if (cmd.valgrind !== '') {
timeout *= 10000;
}
var options = {
method: "POST",
timeout: timeout,
headers: hdrs,
returnBodyOnError: true
};
// execute bootstrap command on first server
var retryCount = 0;
var result;
var url = coordinators[0] + "/_admin/cluster/bootstrapDbServers";
var body = '{"isRelaunch": ' + (isRelaunch ? "true" : "false") + '}';
while (retryCount < timeout) {
result = download(url, body, options);
if ((result.code === 503) && (retryCount < 3)) {
wait(1);
retryCount+=1;
continue;
}
if (result.code !== 200) {
var err1 = "bootstrapping DB servers failed: " + extractErrorMessage(result);
console.error("%s", err1);
return {"error": true, "bootstrapServers": true, "errorMessage": err1};
}
break;
}
// execute cluster database upgrade
url = coordinators[0] + "/_admin/cluster/upgradeClusterDatabase";
result = download(url, body, options);
if (result.code !== 200) {
var err2 = "upgrading cluster database failed: " + extractErrorMessage(result);
console.error("%s", err2);
return {"error": true, "bootstrapServers": true, "errorMessage": err2};
}
// bootstrap coordinators
var i;
for (i = 0; i < coordinators.length; ++i) {
url = coordinators[i] + "/_admin/cluster/bootstrapCoordinator";
result = download(url, body, options);
if (result.code !== 200) {
var err = "bootstrapping coordinator " +
coordinators[i] + " failed: " +
extractErrorMessage(result);
console.error("%s", err);
return {"error": true, "bootstrapServers": true, "errorMessage": err};
}
}
return {"error": false, "bootstrapServers": true, "errorMessage": ''};
};
shutdownActions.startAgent = function (dispatchers, cmd, run) {
if (cmd.extremeVerbosity) {
console.info("Shutting down agent %s", JSON.stringify(run.pid));
}
killExternal(run.pid);
return {"error": false, "isStartAgent": true};
};
shutdownActions.sendConfiguration = function (dispatchers, cmd, run) {
return {"error": false, "isSendConfiguration": true};
};
shutdownActions.startServers = function (dispatchers, cmd, run) {
var i;
var url;
var r;
var serverStates = {};
var error = false;
for (i = 0;i < run.endpoints.length;i++) {
console.info("Using API to shutdown %s %s %s %s",
run.roles[i],
run.endpointNames[i],
run.endpoints[i],
JSON.stringify(run.pids[i]));
url = endpointToURL(run.endpoints[i])+"/_admin/shutdown";
// We use the cluster-internal authentication:
var hdrs = { Authorization : ArangoServerState.getClusterAuthentication() };
r = download(url,"",{method:"GET", headers: hdrs});
if (r.code !== 200) {
console.info("Shutdown API result: %s %s %s %s %s",
run.roles[i],
run.endpointNames[i],
run.endpoints[i],
JSON.stringify(run.pids[i]),
JSON.stringify(r));
}
}
for (i = 0;i < run.endpoints.length;i++) {
waitForServerDown(cmd, run.endpoints[i], 30);
// we cannot do much with the result...
}
var shutdownWait = 20;
if (cmd.valgrind !== '') {
shutdownWait *= 600; // should be enough, even with Valgrind
}
console.info("Waiting up to " + shutdownWait + " seconds for servers to shutdown gracefully...");
var j = 0;
var runpids = run.pids.length;
while ((j < shutdownWait) && (runpids > 0)) {
wait(1);
j++;
for (i = 0; i < run.pids.length; i++) {
if (serverStates[JSON.stringify(run.pids[i])] === undefined) {
var s = statusExternal(run.pids[i]);
if ((s.status === "NOT-FOUND") ||
(s.status === "TERMINATED") ||
s.hasOwnProperty('signal')) {
runpids -=1;
serverStates[JSON.stringify(run.pids[i])] = s;
error = true;
}
else if (j > shutdownWait) {
if (s.status !== "TERMINATED") {
if (s.hasOwnProperty('signal')) {
error = true;
console.error("shuting down %s %s done - with problems: " + s,
run.roles[i],
run.endpointNames[i],
JSON.stringify(run.pids[i]));
}
else {
console.info("Shutting down %s the hard way...",
JSON.stringify(run.pids[i]));
s.killedState = killExternal(run.pids[i]);
console.info("done.");
runpids -=1;
}
serverStates[JSON.stringify(run.pids[i])] = s;
}
}
}
}
}
return {"error": error, "isStartServers": true, "serverStates" : serverStates};
};
cleanupActions.startAgent = function (dispatchers, cmd) {
if (cmd.extremeVerbosity) {
console.info("Cleaning up agent...");
}
var dataPath = fs.makeAbsolute(cmd.dataPath);
if (dataPath !== cmd.dataPath) { // path was relative
dataPath = fs.normalize(fs.join(ArangoServerState.dataPath(),cmd.dataPath));
}
var agentDataDir = fs.join(dataPath, "agent"+cmd.agencyPrefix+cmd.extPort);
if (fs.exists(agentDataDir)) {
fs.removeDirectoryRecursive(agentDataDir,true);
}
return {"error":false, "isStartAgent": true};
};
cleanupActions.startServers = function (dispatchers, cmd, isRelaunch) {
if (cmd.extremeVerbosity) {
console.info("Cleaning up DBservers...");
}
var dataPath = fs.makeAbsolute(cmd.dataPath);
if (dataPath !== cmd.dataPath) { // path was relative
dataPath = fs.normalize(fs.join(ArangoServerState.dataPath(),cmd.dataPath));
}
var logPath = fs.makeAbsolute(cmd.logPath);
if (logPath !== cmd.logPath) { // path was relative
logPath = fs.normalize(fs.join(ArangoServerState.logPath(), cmd.logPath));
}
var servers = cmd.DBservers.concat(cmd.Coordinators);
var i;
var logfile, datadir;
for (i = 0; i < servers.length; i++) {
var id = servers[i];
logfile = fs.join(logPath,"log-"+cmd.agency.agencyPrefix+"-"+id);
if (fs.exists(logfile)) {
fs.remove(logfile);
}
datadir = fs.join(dataPath,"data-"+cmd.agency.agencyPrefix+"-"+id);
if (fs.exists(datadir)) {
fs.removeDirectoryRecursive(datadir,true);
}
}
return {"error": false, "isStartServers": true};
};
isHealthyActions.startAgent = function (dispatchers, cmd, run) {
if (cmd.extremeVerbosity) {
console.info("Checking health of agent %s", JSON.stringify(run.pid));
}
var r = statusExternal(run.pid);
r.isStartAgent = true;
r.error = false;
return r;
};
isHealthyActions.startServers = function (dispatchers, cmd, run) {
var i;
var r = [];
for (i = 0;i < run.pids.length;i++) {
if (cmd.extremeVerbosity) {
console.info("Checking health of %s %s %s %s",
run.roles[i],
run.endpointNames[i],
run.endpoints[i],
JSON.stringify(run.pids[i]));
}
r.push(statusExternal(run.pids[i]));
}
var s = [];
var x;
var error = false;
for (i = 0;i < run.endpoints.length;i++) {
x = waitForServerUp(cmd, run.endpoints[i], 0);
s.push(x);
if (x === false) {
error = true;
}
}
return {"error": error, "isStartServers": true, "status": r,
"answering": s};
};
// Upgrade for the agent is exactly as launch/relaunch:
upgradeActions.startAgent = launchActions.startAgent;
// Upgrade for the configuration
upgradeActions.sendConfiguration = launchActions.sendConfiguration;
// Upgrade for servers, copied from launchactions, but modified:
upgradeActions.startServers = function (dispatchers, cmd, isRelaunch) {
var dataPath = fs.makeAbsolute(cmd.dataPath);
if (dataPath !== cmd.dataPath) { // path was relative
dataPath = fs.normalize(fs.join(ArangoServerState.dataPath(),cmd.dataPath));
}
var logPath = fs.makeAbsolute(cmd.logPath);
if (logPath !== cmd.logPath) { // path was relative
logPath = fs.normalize(fs.join(ArangoServerState.logPath(), cmd.logPath));
}
var url = endpointToURL(cmd.agency.endpoints[0])+"/v2/keys/"+
cmd.agency.agencyPrefix+"/";
if (cmd.extremeVerbosity) {
console.info("Downloading %sLaunchers/%s", url, encode(cmd.name));
}
var res = download(url+"Launchers/" + encode(cmd.name), "", { method: "GET",
followRedirects: true });
if (res.code !== 200) {
return {"error": true, "isStartServers": true, "suberror": res};
}
var body = JSON.parse( res.body );
var info = JSON.parse(body.node.value);
var id,ep,args,pids,port,endpoints,endpointNames,roles;
console.info("Upgrading servers...");
var i;
var servers = info.DBservers.concat(info.Coordinators);
roles = [];
for (i = 0; i < info.DBservers.length; i++) {
roles.push("DBserver");
}
for (i = 0; i < info.Coordinators.length; i++) {
roles.push("Coordinator");
}
pids = [];
endpoints = [];
var useSSL;
var arangodPath;
var logfile, datadir;
for (i = 0; i < servers.length; i++) {
id = servers[i];
if (cmd.extremeVerbosity) {
console.info("Downloading %sTarget/MapIDToEndpoint/%s", url, id);
}
res = download(url + "Target/MapIDToEndpoint/" + id);
if (res.code !== 200) {
return {"error": true, "pids": pids,
"isStartServers": true, "suberror": res};
}
console.info("Upgrading server %s",id);
body = JSON.parse(res.body);
ep = JSON.parse(body.node.value);
port = getPort(ep);
useSSL = false;
if (roles[i] === "DBserver") {
args = ["--configuration", ArangoServerState.dbserverConfig()];
useSSL = cmd.useSSLonDBservers;
}
else {
args = ["--configuration", ArangoServerState.coordinatorConfig()];
useSSL = cmd.useSSLonCoordinators;
}
args = args.concat([
"--cluster.disable-dispatcher-kickstarter", "true",
"--cluster.disable-dispatcher-frontend", "true",
"--cluster.my-id", id,
"--cluster.agency-prefix", cmd.agency.agencyPrefix,
"--cluster.agency-endpoint", cmd.agency.endpoints[0],
"--server.endpoint"]);
if (cmd.onlyLocalhost) {
args.push(exchangeProtocol("tcp://127.0.0.1:"+port,useSSL));
}
else {
args.push(exchangeProtocol("tcp://0.0.0.0:"+port,useSSL));
}
args.push("--log.file");
logfile = fs.join(logPath,"log-"+cmd.agency.agencyPrefix+"-"+id);
args.push(logfile);
datadir = fs.join(dataPath,"data-"+cmd.agency.agencyPrefix+"-"+id);
args.push("--database.directory");
args.push(datadir);
args = args.concat(dispatchers[cmd.dispatcher].arangodExtraArgs);
args.push("--upgrade");
arangodPath = fs.makeAbsolute(cmd.arangodPath);
if (arangodPath !== cmd.arangodPath) {
arangodPath = ArangoServerState.arangodPath();
}
pids.push(executeExternal(arangodPath, args));
ep = exchangePort(dispatchers[cmd.dispatcher].endpoint,port);
ep = exchangeProtocol(ep,useSSL);
endpoints.push(ep);
}
var error = false;
for (i = 0;i < pids.length;i++) {
var s = statusExternal(pids[i], true);
if (s.status !== "TERMINATED" || s.exit !== 0) {
error = true;
console.info("Upgrade of server "+servers[i]+" went wrong.");
}
}
if (error === true) {
return {"error": error, "isStartServers": true,
"errorMessage": "error on upgrade"};
}
console.info("Starting servers...");
servers = info.DBservers.concat(info.Coordinators);
roles = [];
for (i = 0; i < info.DBservers.length; i++) {
roles.push("DBserver");
}
for (i = 0; i < info.Coordinators.length; i++) {
roles.push("Coordinator");
}
pids = [];
endpoints = [];
endpointNames = [];
for (i = 0; i < servers.length; i++) {
id = servers[i];
if (cmd.extremeVerbosity) {
console.info("Downloading %sTarget/MapIDToEndpoint/%s", url, id);
}
res = download(url + "Target/MapIDToEndpoint/" + id);
if (res.code !== 200) {
return {"error": true, "pids": pids,
"isStartServers": true, "suberror": res};
}
console.info("Starting server %s",id);
body = JSON.parse(res.body);
ep = JSON.parse(body.node.value);
port = getPort(ep);
useSSL = false;
if (roles[i] === "DBserver") {
args = ["--configuration", ArangoServerState.dbserverConfig()];
useSSL = cmd.useSSLonDBservers;
}
else {
args = ["--configuration", ArangoServerState.coordinatorConfig()];
useSSL = cmd.useSSLonCoordinators;
}
args = args.concat([
"--cluster.disable-dispatcher-kickstarter", "true",
"--cluster.disable-dispatcher-frontend", "true",
"--cluster.my-id", id,
"--cluster.agency-prefix", cmd.agency.agencyPrefix,
"--cluster.agency-endpoint", cmd.agency.endpoints[0],
"--server.endpoint"]);
if (cmd.onlyLocalhost) {
args.push(exchangeProtocol("tcp://127.0.0.1:"+port,useSSL));
}
else {
args.push(exchangeProtocol("tcp://0.0.0.0:"+port,useSSL));
}
args.push("--log.file");
logfile = fs.join(logPath,"log-"+cmd.agency.agencyPrefix+"-"+id);
args.push(logfile);
if (!isRelaunch) {
if (!fs.exists(logPath)) {
fs.makeDirectoryRecursive(logPath);
}
if (fs.exists(logfile)) {
fs.remove(logfile);
}
}
datadir = fs.join(dataPath,"data-"+cmd.agency.agencyPrefix+"-"+id);
args.push("--database.directory");
args.push(datadir);
if (!isRelaunch) {
if (!fs.exists(dataPath)) {
fs.makeDirectoryRecursive(dataPath);
}
if (fs.exists(datadir)) {
fs.removeDirectoryRecursive(datadir,true);
}
fs.makeDirectory(datadir);
}
args = args.concat(dispatchers[cmd.dispatcher].arangodExtraArgs);
arangodPath = fs.makeAbsolute(cmd.arangodPath);
if (arangodPath !== cmd.arangodPath) {
arangodPath = ArangoServerState.arangodPath();
}
pids.push(executeExternal(arangodPath, args));
ep = exchangePort(dispatchers[cmd.dispatcher].endpoint,port);
ep = exchangeProtocol(ep,useSSL);
endpoints.push(ep);
endpointNames.push(id);
}
error = false;
for (i = 0;i < endpoints.length;i++) {
if (! waitForServerUp(cmd, endpoints[i], 30)) {
error = true;
}
}
return {"error": error,
"isStartServers": true,
"pids": pids,
"endpoints": endpoints,
"endpointNames": endpointNames,
"roles": roles};
};
// Upgrade for the upgrade-database as in launch:
upgradeActions.bootstrapServers = function (dispatchers, cmd, isRelaunch,
username, password) {
var coordinators = cmd.coordinators;
// we need at least one coordinator
if (0 === coordinators.length) {
return {"error": true, "bootstrapServers": true};
}
// We wait another half second (we have already waited for all servers
// until they responded!), just to be on the safe side:
wait(0.5);
// authorization header for coordinator
var hdrs = {
Authorization: getAuthorizationHeader(username, password)
};
// default options
var timeout = 90;
if (cmd.valgrind !== '') {
timeout *= 10000;
}
var options = {
method: "POST",
timeout: timeout,
headers: hdrs,
returnBodyOnError: true
};
// execute bootstrap command on first server
var url = coordinators[0] + "/_admin/cluster/bootstrapDbServers";
var body = '{"isRelaunch": false, "upgrade": true}';
var result = download(url, body, options);
if (result.code !== 200) {
console.error("bootstrapping DB servers failed: %s", extractErrorMessage(result));
return {"error": true, "bootstrapServers": true};
}
// execute cluster database upgrade
console.info("Running upgrade script on whole cluster...");
url = coordinators[0] + "/_admin/cluster/upgradeClusterDatabase";
result = download(url, body, options);
if (result.code !== 200) {
console.error("upgrading cluster database failed: %s", extractErrorMessage(result));
return {"error": true, "bootstrapServers": true};
}
// bootstrap coordinators
var i;
for (i = 0; i < coordinators.length; ++i) {
url = coordinators[i] + "/_admin/cluster/bootstrapCoordinator";
result = download(url, body, options);
if (result.code !== 200) {
console.error("bootstrapping coordinator %s failed: %s",
coordinators[i],
extractErrorMessage(result));
return {"error": true, "bootstrapServers": true};
}
}
return {"error": false, "bootstrapServers": true};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_Cluster_Kickstarter_Constructor
////////////////////////////////////////////////////////////////////////////////
function Kickstarter (clusterPlan, myname) {
this.clusterPlan = clusterPlan;
if (myname === undefined) {
this.myname = "me";
}
else {
this.myname = myname;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_Kickstarter_prototype_launch
////////////////////////////////////////////////////////////////////////////////
Kickstarter.prototype.launch = function () {
var clusterPlan = this.clusterPlan;
var myname = this.myname;
var dispatchers = clusterPlan.dispatchers;
var cmds = clusterPlan.commands;
var results = [];
var cmd;
var errors = "";
var error = false;
var i;
var res;
for (i = 0; i < cmds.length; i++) {
cmd = cmds[i];
if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
if (launchActions.hasOwnProperty(cmd.action)) {
res = launchActions[cmd.action](dispatchers, cmd, false);
results.push(res);
if (res.error === true) {
errors += "Launchjob " + cmd.action + " failed: " + res.errorMessage;
error = true;
break;
}
}
else {
results.push({ error: false, action: cmd.action });
}
}
else {
var ep = dispatchers[cmd.dispatcher].endpoint;
var body = JSON.stringify({ "action": "launch",
"clusterPlan": {
"dispatchers": dispatchers,
"commands": [cmd] },
"myname": cmd.dispatcher });
var url = endpointToURL(ep) + "/_admin/clusterDispatch";
var hdrs = {};
if (dispatchers[cmd.dispatcher].username !== undefined &&
dispatchers[cmd.dispatcher].passwd !== undefined) {
hdrs.Authorization = getAuthorization(dispatchers[cmd.dispatcher]);
}
var timeout = 90;
if (cmd.valgrind !== '') {
timeout *= 10000;
}
var response = download(url, body, {method: "POST", headers: hdrs, timeout: timeout});
if (response.code !== 200) {
error = true;
results.push({"error":true, "errorMessage": "bad HTTP response code",
"response": response});
}
else {
try {
res = JSON.parse(response.body);
results.push(res.runInfo[0]);
}
catch (err) {
results.push({"error":true, "errorMessage": "exception in JSON.parse"});
error = true;
}
}
if (error) {
break;
}
}
}
this.runInfo = results;
if (error) {
return {"error": true, "errorMessage": errors,
"runInfo": results};
}
return {"error": false, "errorMessage": "none",
"runInfo": results};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_Kickstarter_prototype_relaunch
////////////////////////////////////////////////////////////////////////////////
Kickstarter.prototype.relaunch = function (username, password) {
var clusterPlan = this.clusterPlan;
var myname = this.myname;
var dispatchers = clusterPlan.dispatchers;
var cmds = clusterPlan.commands;
var results = [];
var cmd;
if (username === undefined && password === undefined) {
username = "root";
password = "";
}
var error = false;
var i;
var res;
for (i = 0; i < cmds.length; i++) {
cmd = cmds[i];
if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
if (launchActions.hasOwnProperty(cmd.action)) {
res = launchActions[cmd.action](dispatchers, cmd, true,
username, password);
results.push(res);
if (res.error === true) {
error = true;
break;
}
}
else {
results.push({ error: false, action: cmd.action });
}
}
else {
var ep = dispatchers[cmd.dispatcher].endpoint;
var body = JSON.stringify({ "action": "relaunch",
"clusterPlan": {
"dispatchers": dispatchers,
"commands": [cmd] },
"myname": cmd.dispatcher,
"username": username,
"password": password});
var url = endpointToURL(ep) + "/_admin/clusterDispatch";
var hdrs = {};
if (dispatchers[cmd.dispatcher].username !== undefined &&
dispatchers[cmd.dispatcher].passwd !== undefined) {
hdrs.Authorization = getAuthorization(dispatchers[cmd.dispatcher]);
}
var timeout = 90;
if (cmd.valgrind !== '') {
timeout *= 10000;
}
var response = download(url, body, {method: "POST", headers: hdrs, timeout: timeout});
if (response.code !== 200) {
error = true;
results.push({"error":true, "errorMessage": "bad HTTP response code",
"response": response});
}
else {
try {
res = JSON.parse(response.body);
results.push(res.runInfo[0]);
}
catch (err) {
results.push({"error":true, "errorMessage": "exception in JSON.parse"});
error = true;
}
}
if (error) {
break;
}
}
}
this.runInfo = results;
if (error) {
return {"error": true, "errorMessage": "some error during relaunch",
"runInfo": results};
}
return {"error": false, "errorMessage": "none",
"runInfo": results};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_Kickstarter_prototype_shutdown
////////////////////////////////////////////////////////////////////////////////
Kickstarter.prototype.shutdown = function() {
var clusterPlan = this.clusterPlan;
var myname = this.myname;
var dispatchers = clusterPlan.dispatchers;
var cmds = clusterPlan.commands;
var runInfo = this.runInfo;
var results = [];
var cmd;
var error = false;
var i;
var res;
for (i = cmds.length-1; i >= 0; i--) {
cmd = cmds[i];
var run = runInfo[i];
if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
if (shutdownActions.hasOwnProperty(cmd.action)) {
res = shutdownActions[cmd.action](dispatchers, cmd, run);
if (res.error === true) {
error = true;
}
results.push(res);
}
else {
results.push({ error: false, action: cmd.action });
}
}
else {
var ep = dispatchers[cmd.dispatcher].endpoint;
var body = JSON.stringify({ "action": "shutdown",
"clusterPlan": {
"dispatchers": dispatchers,
"commands": [cmd] },
"runInfo": [run],
"myname": cmd.dispatcher });
var url = endpointToURL(ep) + "/_admin/clusterDispatch";
var hdrs = {};
if (dispatchers[cmd.dispatcher].username !== undefined &&
dispatchers[cmd.dispatcher].passwd !== undefined) {
hdrs.Authorization = getAuthorization(dispatchers[cmd.dispatcher]);
}
var timeout = 90;
if (cmd.valgrind !== '') {
timeout *= 10000;
}
var response = download(url, body, {method: "POST", headers: hdrs, timeout: timeout});
if (response.code !== 200) {
error = true;
results.push({"error":true, "errorMessage": "bad HTTP response code",
"response": response});
}
else {
try {
res = JSON.parse(response.body);
results.push(res.results[0]);
}
catch (err) {
results.push({"error":true,
"errorMessage": "exception in JSON.parse"});
error = true;
}
}
}
}
results = results.reverse();
if (error) {
return {"error": true, "errorMessage": "some error during shutdown",
"results": results};
}
return {"error": false, "errorMessage": "none", "results": results};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_Kickstarter_prototype_cleanup
////////////////////////////////////////////////////////////////////////////////
Kickstarter.prototype.cleanup = function() {
var clusterPlan = this.clusterPlan;
var myname = this.myname;
var dispatchers = clusterPlan.dispatchers;
var cmds = clusterPlan.commands;
var results = [];
var cmd;
var error = false;
var i;
var res;
for (i = 0; i < cmds.length; i++) {
cmd = cmds[i];
if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
if (cleanupActions.hasOwnProperty(cmd.action)) {
res = cleanupActions[cmd.action](dispatchers, cmd);
results.push(res);
if (res.error === true) {
error = true;
}
}
else {
results.push({ error: false, action: cmd.action });
}
}
else {
var ep = dispatchers[cmd.dispatcher].endpoint;
var body = JSON.stringify({ "action": "cleanup",
"clusterPlan": {
"dispatchers": dispatchers,
"commands": [cmd] },
"myname": cmd.dispatcher });
var url = endpointToURL(ep) + "/_admin/clusterDispatch";
var hdrs = {};
if (dispatchers[cmd.dispatcher].username !== undefined &&
dispatchers[cmd.dispatcher].passwd !== undefined) {
hdrs.Authorization = getAuthorization(dispatchers[cmd.dispatcher]);
}
var timeout = 90;
if (cmd.valgrind !== '') {
timeout *= 10000;
}
var response = download(url, body, {method: "POST", headers: hdrs, timeout: timeout});
if (response.code !== 200) {
error = true;
results.push({"error":true, "errorMessage": "bad HTTP response code",
"response": response});
}
else {
try {
res = JSON.parse(response.body);
results.push(res.results[0]);
}
catch (err) {
results.push({"error":true,
"errorMessage": "exception in JSON.parse"});
error = true;
}
}
}
}
if (error) {
return {"error": true, "errorMessage": "some error during cleanup",
"results": results};
}
return {"error": false, "errorMessage": "none", "results": results};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_Kickstarter_prototype_isHealthy
////////////////////////////////////////////////////////////////////////////////
Kickstarter.prototype.isHealthy = function() {
var clusterPlan = this.clusterPlan;
var myname = this.myname;
var dispatchers = clusterPlan.dispatchers;
var cmds = clusterPlan.commands;
var runInfo = this.runInfo;
var results = [];
var cmd;
var error = false;
var i;
var res;
for (i = cmds.length-1; i >= 0; i--) {
cmd = cmds[i];
if (runInfo === undefined || ! Array.isArray(runInfo) ||
i >= runInfo.length) {
return {"error": true,
"errorMessage": "runInfo object not found or broken",
"results": results};
}
var run = runInfo[i];
if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
if (isHealthyActions.hasOwnProperty(cmd.action)) {
res = isHealthyActions[cmd.action](dispatchers, cmd, run);
if (res.error === true) {
error = true;
}
results.push(res);
}
else {
results.push({ error: false, action: cmd.action });
}
}
else {
var ep = dispatchers[cmd.dispatcher].endpoint;
var body = JSON.stringify({ "action": "isHealthy",
"clusterPlan": {
"dispatchers": dispatchers,
"commands": [cmd] },
"runInfo": [run],
"myname": cmd.dispatcher });
var url = "http" + ep.substr(3) + "/_admin/clusterDispatch";
var hdrs = {};
if (dispatchers[cmd.dispatcher].username !== undefined &&
dispatchers[cmd.dispatcher].passwd !== undefined) {
hdrs.Authorization = getAuthorization(dispatchers[cmd.dispatcher]);
}
var timeout = 90;
if (cmd.valgrind !== '') {
timeout *= 10000;
}
var response = download(url, body, {method: "POST", headers: hdrs, timeout: timeout});
if (response.code !== 200) {
error = true;
results.push({"error":true, "errorMessage": "bad HTTP response code",
"response": response});
}
else {
try {
res = JSON.parse(response.body);
results.push(res.results[0]);
if (res.results[0].error === true) {
error = true;
}
}
catch (err) {
results.push({"error":true,
"errorMessage": "exception in JSON.parse"});
error = true;
}
}
}
}
results = results.reverse();
if (error) {
return {"error": true, "errorMessage": "some error during isHealthy check",
"results": results};
}
return {"error": false, "errorMessage": "none", "results": results};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_Kickstarter_prototype_upgrade
////////////////////////////////////////////////////////////////////////////////
Kickstarter.prototype.upgrade = function (username, password) {
var clusterPlan = this.clusterPlan;
var myname = this.myname;
var dispatchers = clusterPlan.dispatchers;
var cmds = clusterPlan.commands;
var results = [];
var cmd;
if (username === undefined || password === undefined) {
username = "root";
password = "";
}
var error = false;
var i;
var res;
for (i = 0; i < cmds.length; i++) {
cmd = cmds[i];
if (cmd.dispatcher === undefined || cmd.dispatcher === myname) {
if (upgradeActions.hasOwnProperty(cmd.action)) {
res = upgradeActions[cmd.action](dispatchers, cmd, true,
username, password);
results.push(res);
if (res.error === true) {
error = true;
break;
}
}
else {
results.push({ error: false, action: cmd.action });
}
}
else {
var ep = dispatchers[cmd.dispatcher].endpoint;
var body = JSON.stringify({ "action": "upgrade",
"clusterPlan": {
"dispatchers": dispatchers,
"commands": [cmd] },
"myname": cmd.dispatcher,
"username": username,
"password": password});
var url = endpointToURL(ep) + "/_admin/clusterDispatch";
var hdrs = {};
if (dispatchers[cmd.dispatcher].username !== undefined &&
dispatchers[cmd.dispatcher].passwd !== undefined) {
hdrs.Authorization = getAuthorization(dispatchers[cmd.dispatcher]);
}
var timeout = 90;
if (cmd.valgrind !== '') {
timeout *= 10000;
}
var response = download(url, body, {method: "POST", headers: hdrs, timeout: timeout});
if (response.code !== 200) {
error = true;
results.push({"error":true, "errorMessage": "bad HTTP response code",
"response": response});
}
else {
try {
res = JSON.parse(response.body);
results.push(res.runInfo[0]);
}
catch (err) {
results.push({"error":true, "errorMessage": "exception in JSON.parse"});
error = true;
}
}
if (error) {
break;
}
}
}
this.runInfo = results;
if (error) {
return {"error": true, "errorMessage": "some error during upgrade",
"runInfo": results};
}
return {"error": false, "errorMessage": "none",
"runInfo": results};
};
exports.Kickstarter = Kickstarter;