1
0
Fork 0

Merge branch 'foxx-pathes' of github.com:triAGENS/ArangoDB into foxx-pathes

This commit is contained in:
Frank Celler 2015-01-18 12:29:39 +01:00
commit bd2a698ab6
22 changed files with 1118 additions and 1309 deletions

View File

@ -7,6 +7,10 @@ v2.5.0 (XXXX-XX-XX)
v2.4.1 (XXXX-XX-XX) v2.4.1 (XXXX-XX-XX)
------------------- -------------------
* better diagnostics for arangoimp
* fixed invalid result of HTTP REST API method `/_admin/foxx/rescan`
* fixed possible segmentation fault when passing a Buffer object into a V8 function * fixed possible segmentation fault when passing a Buffer object into a V8 function
as a parameter as a parameter

View File

@ -86,16 +86,16 @@ collection in the default database (*_system*). To specify a different database,
use the *--server.database* option when invoking _arangoimp_. use the *--server.database* option when invoking _arangoimp_.
An _arangoimp_ import run will print out the final results on the command line. An _arangoimp_ import run will print out the final results on the command line.
By default, it shows the number of documents created, the number of errors that By default, it shows the number of documents created, the number of warnings or errors that
occurred on the server side, and the total number of input file lines/documents occurred on the server side, and the total number of input file lines/documents
that it processed. Additionally, _arangoimp_ will print out details about errors that it processed. Additionally, _arangoimp_ will print out details about warnings and errors
that happened on the server-side (if any). that happened on the server-side (if any).
*Examples* *Examples*
```js ```js
created: 2 created: 2
errors: 0 warnings/errors: 0
total: 2 total: 2
``` ```

View File

@ -447,6 +447,7 @@ SHELL_COMMON = \
@top_srcdir@/js/common/tests/shell-index.js \ @top_srcdir@/js/common/tests/shell-index.js \
@top_srcdir@/js/common/tests/shell-index-geo.js \ @top_srcdir@/js/common/tests/shell-index-geo.js \
@top_srcdir@/js/common/tests/shell-cap-constraint.js \ @top_srcdir@/js/common/tests/shell-cap-constraint.js \
@top_srcdir@/js/common/tests/shell-cap-constraint-timecritical.js \
@top_srcdir@/js/common/tests/shell-unique-constraint.js \ @top_srcdir@/js/common/tests/shell-unique-constraint.js \
@top_srcdir@/js/common/tests/shell-hash-index.js \ @top_srcdir@/js/common/tests/shell-hash-index.js \
@top_srcdir@/js/common/tests/shell-fulltext.js \ @top_srcdir@/js/common/tests/shell-fulltext.js \

View File

@ -3,3 +3,4 @@
{"id": 3, "_from": "UnitTestsImportVertex/v9", "_to": "UnitTestsImportVertex/v4", "what": "v9->v4", "extra": "foo"} {"id": 3, "_from": "UnitTestsImportVertex/v9", "_to": "UnitTestsImportVertex/v4", "what": "v9->v4", "extra": "foo"}
{"id": 4, "_from": "UnitTestsImportVertex/v12", "_to": "UnitTestsImportVertex/what"} {"id": 4, "_from": "UnitTestsImportVertex/v12", "_to": "UnitTestsImportVertex/what"}
{"id": 5}

View File

@ -153,8 +153,25 @@ void RestImportHandler::registerError (RestImportResult& result,
++result._numErrors; ++result._numErrors;
result._errors.push_back(errorMsg); result._errors.push_back(errorMsg);
}
LOG_WARNING("%s", errorMsg.c_str()); ////////////////////////////////////////////////////////////////////////////////
/// @brief construct an error message
////////////////////////////////////////////////////////////////////////////////
std::string RestImportHandler::buildParseError (size_t i,
char const* lineStart) {
if (lineStart != nullptr) {
string part(lineStart);
if (part.size() > 255) {
// UTF-8 chars in string will be escaped so we can truncate it at any point
part = part.substr(0, 255) + "...";
}
return positionise(i) + "invalid JSON type (expecting object, probably parse error), offending context: " + part;
}
return positionise(i) + "invalid JSON type (expecting object, probably parse error)";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -162,13 +179,25 @@ void RestImportHandler::registerError (RestImportResult& result,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int RestImportHandler::handleSingleDocument (RestImportTransaction& trx, int RestImportHandler::handleSingleDocument (RestImportTransaction& trx,
char const* lineStart,
TRI_json_t const* json, TRI_json_t const* json,
string& errorMsg, string& errorMsg,
bool isEdgeCollection, bool isEdgeCollection,
bool waitForSync, bool waitForSync,
size_t i) { size_t i) {
if (! TRI_IsObjectJson(json)) { if (! TRI_IsObjectJson(json)) {
errorMsg = positionise(i) + "invalid JSON type (expecting array)"; if (json != nullptr) {
string part = JsonHelper::toString(json);
if (part.size() > 255) {
// UTF-8 chars in string will be escaped so we can truncate it at any point
part = part.substr(0, 255) + "...";
}
errorMsg = positionise(i) + "invalid JSON type (expecting object), offending document: " + part;
}
else {
errorMsg = buildParseError(i, lineStart);
}
return TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID; return TRI_ERROR_ARANGO_DOCUMENT_TYPE_INVALID;
} }
@ -182,7 +211,13 @@ int RestImportHandler::handleSingleDocument (RestImportTransaction& trx,
char const* to = extractJsonStringValue(json, TRI_VOC_ATTRIBUTE_TO); char const* to = extractJsonStringValue(json, TRI_VOC_ATTRIBUTE_TO);
if (from == nullptr || to == nullptr) { if (from == nullptr || to == nullptr) {
errorMsg = positionise(i) + "missing '_from' or '_to' attribute"; string part = JsonHelper::toString(json);
if (part.size() > 255) {
// UTF-8 chars in string will be escaped so we can truncate it at any point
part = part.substr(0, 255) + "...";
}
errorMsg = positionise(i) + "missing '_from' or '_to' attribute, offending document: " + part;
return TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE; return TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE;
} }
@ -600,7 +635,7 @@ bool RestImportHandler::createFromJson (string const& type) {
while (ptr < end) { while (ptr < end) {
char const c = *ptr; char const c = *ptr;
if (c == '\r' || c == '\n' || c == '\t' || c == ' ') { if (c == '\r' || c == '\n' || c == '\t' || c == '\b' || c == '\f' || c == ' ') {
ptr++; ptr++;
continue; continue;
} }
@ -657,7 +692,7 @@ bool RestImportHandler::createFromJson (string const& type) {
// trim whitespace at start of line // trim whitespace at start of line
while (ptr < end && while (ptr < end &&
(*ptr == ' ' || *ptr == '\t' || *ptr == '\r')) { (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\b' || *ptr == '\f')) {
++ptr; ++ptr;
} }
@ -667,6 +702,7 @@ bool RestImportHandler::createFromJson (string const& type) {
// now find end of line // now find end of line
char const* pos = strchr(ptr, '\n'); char const* pos = strchr(ptr, '\n');
char const* oldPtr = nullptr;
TRI_json_t* json = nullptr; TRI_json_t* json = nullptr;
@ -680,18 +716,20 @@ bool RestImportHandler::createFromJson (string const& type) {
// non-empty line // non-empty line
*(const_cast<char*>(pos)) = '\0'; *(const_cast<char*>(pos)) = '\0';
TRI_ASSERT(ptr != nullptr); TRI_ASSERT(ptr != nullptr);
json = parseJsonLine(ptr); oldPtr = ptr;
json = parseJsonLine(ptr, pos);
ptr = pos + 1; ptr = pos + 1;
} }
else { else {
// last-line, non-empty // last-line, non-empty
TRI_ASSERT(pos == nullptr); TRI_ASSERT(pos == nullptr);
TRI_ASSERT(ptr != nullptr); TRI_ASSERT(ptr != nullptr);
oldPtr = ptr;
json = parseJsonLine(ptr); json = parseJsonLine(ptr);
ptr = end; ptr = end;
} }
res = handleSingleDocument(trx, json, errorMsg, isEdgeCollection, waitForSync, i); res = handleSingleDocument(trx, oldPtr, json, errorMsg, isEdgeCollection, waitForSync, i);
if (json != nullptr) { if (json != nullptr) {
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
@ -734,7 +772,7 @@ bool RestImportHandler::createFromJson (string const& type) {
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
TRI_json_t const* json = static_cast<TRI_json_t const*>(TRI_AtVector(&documents->_value._objects, i)); TRI_json_t const* json = static_cast<TRI_json_t const*>(TRI_AtVector(&documents->_value._objects, i));
res = handleSingleDocument(trx, json, errorMsg, isEdgeCollection, waitForSync, i + 1); res = handleSingleDocument(trx, nullptr, json, errorMsg, isEdgeCollection, waitForSync, i + 1);
if (res == TRI_ERROR_NO_ERROR) { if (res == TRI_ERROR_NO_ERROR) {
++result._numCreated; ++result._numCreated;
@ -1071,12 +1109,12 @@ bool RestImportHandler::createFromKeyValueList () {
// trim line // trim line
while (lineStart < bodyEnd && while (lineStart < bodyEnd &&
(*lineStart == ' ' || *lineStart == '\t' || *lineStart == '\r' || *lineStart == '\n')) { (*lineStart == ' ' || *lineStart == '\t' || *lineStart == '\r' || *lineStart == '\n' || *lineStart == '\b' || *lineStart == '\f')) {
++lineStart; ++lineStart;
} }
while (lineEnd > lineStart && while (lineEnd > lineStart &&
(*(lineEnd - 1) == ' ' || *(lineEnd - 1) == '\t' || *(lineEnd - 1) == '\r' || *(lineEnd - 1) == '\n')) { (*(lineEnd - 1) == ' ' || *(lineEnd - 1) == '\t' || *(lineEnd - 1) == '\r' || *(lineEnd - 1) == '\n' || *(lineEnd - 1) == '\b' || *(lineEnd - 1) == '\f')) {
--lineEnd; --lineEnd;
} }
@ -1084,7 +1122,6 @@ bool RestImportHandler::createFromKeyValueList () {
TRI_json_t* keys = parseJsonLine(lineStart, lineEnd); TRI_json_t* keys = parseJsonLine(lineStart, lineEnd);
if (! checkKeys(keys)) { if (! checkKeys(keys)) {
LOG_WARNING("no JSON string array found in first line");
generateError(HttpResponse::BAD, generateError(HttpResponse::BAD,
TRI_ERROR_HTTP_BAD_PARAMETER, TRI_ERROR_HTTP_BAD_PARAMETER,
"no JSON string array found in first line"); "no JSON string array found in first line");
@ -1146,12 +1183,12 @@ bool RestImportHandler::createFromKeyValueList () {
// trim line // trim line
while (lineStart < bodyEnd && while (lineStart < bodyEnd &&
(*lineStart == ' ' || *lineStart == '\t' || *lineStart == '\r' || *lineStart == '\n')) { (*lineStart == ' ' || *lineStart == '\t' || *lineStart == '\r' || *lineStart == '\n' || *lineStart == '\b' || *lineStart == '\f')) {
++lineStart; ++lineStart;
} }
while (lineEnd > lineStart && while (lineEnd > lineStart &&
(*(lineEnd - 1) == ' ' || *(lineEnd - 1) == '\t' || *(lineEnd - 1) == '\r' || *(lineEnd - 1) == '\n')) { (*(lineEnd - 1) == ' ' || *(lineEnd - 1) == '\t' || *(lineEnd - 1) == '\r' || *(lineEnd - 1) == '\n' || *(lineEnd - 1) == '\b' || *(lineEnd - 1) == '\f')) {
--lineEnd; --lineEnd;
} }
@ -1170,7 +1207,7 @@ bool RestImportHandler::createFromKeyValueList () {
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, values); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, values);
if (json != nullptr) { if (json != nullptr) {
res = handleSingleDocument(trx, json, errorMsg, isEdgeCollection, waitForSync, i); res = handleSingleDocument(trx, lineStart, json, errorMsg, isEdgeCollection, waitForSync, i);
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
} }
else { else {
@ -1194,7 +1231,7 @@ bool RestImportHandler::createFromKeyValueList () {
} }
} }
else { else {
string errorMsg = positionise(i) + "no valid JSON data"; string errorMsg = buildParseError(i, lineStart);
registerError(result, errorMsg); registerError(result, errorMsg);
} }
} }

View File

@ -128,11 +128,19 @@ namespace triagens {
void registerError (RestImportResult&, void registerError (RestImportResult&,
std::string const&); std::string const&);
////////////////////////////////////////////////////////////////////////////////
/// @brief construct an error message
////////////////////////////////////////////////////////////////////////////////
std::string buildParseError (size_t,
char const*);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief process a single JSON document /// @brief process a single JSON document
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int handleSingleDocument (RestImportTransaction&, int handleSingleDocument (RestImportTransaction&,
char const*,
TRI_json_t const*, TRI_json_t const*,
std::string&, std::string&,
bool, bool,

View File

@ -708,10 +708,8 @@ namespace triagens {
} }
} }
TRI_json_t const* importResult;
// look up the "created" flag. This returns a pointer, not a copy // look up the "created" flag. This returns a pointer, not a copy
importResult = TRI_LookupObjectJson(json, "created"); TRI_json_t const* importResult = TRI_LookupObjectJson(json, "created");
if (TRI_IsNumberJson(importResult)) { if (TRI_IsNumberJson(importResult)) {
_numberOk += (size_t) importResult->_value._number; _numberOk += (size_t) importResult->_value._number;

View File

@ -411,7 +411,7 @@ int main (int argc, char* argv[]) {
// give information about import // give information about import
if (ok) { if (ok) {
cout << "created: " << ih.getImportedLines() << endl; cout << "created: " << ih.getImportedLines() << endl;
cout << "errors: " << ih.getErrorLines() << endl; cout << "warnings/errors: " << ih.getErrorLines() << endl;
cout << "total: " << ih.getReadLines() << endl; cout << "total: " << ih.getReadLines() << endl;
} }
else { else {

View File

@ -44,124 +44,7 @@ var easyPostCallback = actions.easyPostCallback;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief fetches a FOXX application /// @brief sets up a Foxx application
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url : "_admin/foxx/fetch",
prefix : false,
callback : function (req, res) {
'use strict';
var safe = /^[0-9a-zA-Z_\-\.]+$/;
if (req.suffix.length !== 0) {
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
return;
}
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
if (json === undefined) {
return;
}
var serverFile = json.filename;
var realFile = fs.join(fs.getTempPath(), serverFile);
if (! fs.isFile(realFile)) {
actions.resultNotFound(req, res, arangodb.errors.ERROR_FILE_NOT_FOUND.code);
return;
}
try {
var name = json.name;
var version = json.version;
if (! safe.test(name)) {
fs.remove(realFile);
throw "rejecting unsafe name '" + name + "'";
}
if (! safe.test(version)) {
fs.remove(realFile);
throw "rejecting unsafe version '" + version + "'";
}
var appPath = module.appPath();
if (typeof appPath === "undefined") {
fs.remove(realFile);
throw "javascript.app-path not set, rejecting app loading";
}
var path = fs.join(appPath, name + "-" + version);
if (!fs.exists(path)) {
fs.makeDirectoryRecursive(path);
fs.unzipFile(realFile, path, false, true);
foxxManager.scanAppDirectory();
}
fs.remove(realFile);
var found = arangodb.db._collection("_aal").firstExample({
type: "app",
path: name + "-" + version
});
if (found !== null) {
found = found.app;
}
actions.resultOk(req, res, actions.HTTP_OK, { path: path, app: found });
}
catch (err) {
actions.resultException(req, res, err, undefined, false);
}
}
});
////////////////////////////////////////////////////////////////////////////////
/// @brief mounts a FOXX application
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url : "_admin/foxx/mount",
prefix : false,
callback: easyPostCallback({
body: true,
callback: function (body) {
var appId = body.appId;
var mount = body.mount;
var options = body.options || {};
return foxxManager.mount(appId, mount, options);
}
})
});
////////////////////////////////////////////////////////////////////////////////
/// @brief rescans the FOXX application directory
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url : "_admin/foxx/rescan",
prefix : false,
callback: easyPostCallback({
body: true,
callback: function () {
foxxManager.scanAppDirectory();
return true;
}
})
});
////////////////////////////////////////////////////////////////////////////////
/// @brief sets up a FOXX application
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({ actions.defineHttp({
@ -179,7 +62,7 @@ actions.defineHttp({
}); });
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief tears down a FOXX application /// @brief tears down a Foxx application
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({ actions.defineHttp({
@ -197,11 +80,31 @@ actions.defineHttp({
}); });
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief unmounts a FOXX application /// @brief installs a Foxx application
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({ actions.defineHttp({
url : "_admin/foxx/unmount", url : "_admin/foxx/install",
prefix : false,
callback: easyPostCallback({
body: true,
callback: function (body) {
var appInfo = body.appInfo;
var mount = body.mount;
var options = body.options;
return foxxManager.install(appInfo, mount, options);
}
})
});
////////////////////////////////////////////////////////////////////////////////
/// @brief uninstalls a Foxx application
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url : "_admin/foxx/uninstall",
prefix : false, prefix : false,
callback: easyPostCallback({ callback: easyPostCallback({
@ -209,85 +112,51 @@ actions.defineHttp({
callback: function (body) { callback: function (body) {
var mount = body.mount; var mount = body.mount;
return foxxManager.unmount(mount); return foxxManager.uninstall(mount);
} }
}) })
}); });
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief sets up a FOXX dev application /// @brief replaces a Foxx application
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({ actions.defineHttp({
url : "_admin/foxx/dev-setup", url : "_admin/foxx/replace",
prefix : false, prefix : false,
callback: easyPostCallback({ callback: easyPostCallback({
body: true, body: true,
callback: function (body) { callback: function (body) {
var name = body.name; var appInfo = body.appInfo;
var mount = body.mount;
var options = body.options;
return foxxManager.devSetup(name); return foxxManager.replace(appInfo, mount, options);
} }
}) })
}); });
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief tears down a FOXX dev application /// @brief upgrades a Foxx application
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({ actions.defineHttp({
url : "_admin/foxx/dev-teardown", url : "_admin/foxx/upgrade",
prefix : false, prefix : false,
callback: easyPostCallback({ callback: easyPostCallback({
body: true, body: true,
callback: function (body) { callback: function (body) {
var name = body.name; var appInfo = body.appInfo;
var mount = body.mount;
var options = body.options;
return foxxManager.devTeardown(name); return foxxManager.upgrade(appInfo, mount, options);
} }
}) })
}); });
////////////////////////////////////////////////////////////////////////////////
/// @brief purges a FOXX application
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url : "_admin/foxx/purge",
prefix : false,
callback: easyPostCallback({
body: true,
callback: function (body) {
var name = body.name;
return foxxManager.purge(name);
}
})
});
////////////////////////////////////////////////////////////////////////////////
/// @brief returns directory information
////////////////////////////////////////////////////////////////////////////////
actions.defineHttp({
url : "_admin/foxx/config",
prefix : false,
callback : function (req, res) {
var result = {
appPath: module.appPath(),
devAppPath: internal.developmentMode ? module.devAppPath() : null,
logFilePath: internal.logfilePath,
startupPath: module.startupPath()
};
actions.resultOk(req, res, actions.HTTP_OK, { result: result });
}
});
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE // --SECTION-- END-OF-FILE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -4,6 +4,7 @@
ArangoError = require('org/arangodb').ArangoError, ArangoError = require('org/arangodb').ArangoError,
<%= repository %> = require('<%= repositoryPath %>').Repository, <%= repository %> = require('<%= repositoryPath %>').Repository,
<%= model %> = require('<%= modelPath %>').Model, <%= model %> = require('<%= modelPath %>').Model,
joi = require('joi'),
_ = require('underscore'), _ = require('underscore'),
controller, controller,
<%= repositoryInstance %>; <%= repositoryInstance %>;
@ -34,6 +35,18 @@
res.json(<%= repositoryInstance %>.save(<%= modelInstance %>).forClient()); res.json(<%= repositoryInstance %>.save(<%= modelInstance %>).forClient());
}).bodyParam('<%= modelInstance %>', 'The <%= model %> you want to create', <%= model %>); }).bodyParam('<%= modelInstance %>', 'The <%= model %> you want to create', <%= model %>);
/** Reads a <%= model %>
*
* Reads a <%= model %>-Item.
*/
controller.get('/<%= basePath %>/:id', function (req, res) {
var id = req.params('id');
res.json(<%= repositoryInstance %>.byId(id).forClient());
}).pathParam('id', {
description: 'The id of the <%= model %>-Item',
type: 'string'
});
/** Updates a <%= model %> /** Updates a <%= model %>
* *
* Changes a <%= model %>-Item. The information has to be in the * Changes a <%= model %>-Item. The information has to be in the

File diff suppressed because it is too large Load Diff

View File

@ -1367,16 +1367,9 @@ function require (path) {
/// @brief createApp /// @brief createApp
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Module.prototype.createApp = function (description, options) { Module.prototype.createApp = function (config) {
'use strict'; 'use strict';
return new ArangoApp(config);
return new ArangoApp(
description.id,
description.manifest,
description.root,
description.path,
options
);
}; };
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -1391,16 +1384,18 @@ function require (path) {
/// @brief ArangoApp constructor /// @brief ArangoApp constructor
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ArangoApp = function (id, manifest, root, path, options) { ArangoApp = function (config) {
'use strict'; 'use strict';
this._id = config.id; // ???
this._id = id; this._manifest = config.manifest;
this._manifest = manifest; this._name = config.manifest.name;
this._name = manifest.name; this._version = config.manifest.version;
this._version = manifest.version; this._root = config.root;
this._root = root; this._path = config.path;
this._path = path; this._options = config.options;
this._options = options; this._mount = config.mount;
this._isSystem = config.isSystem || false;
this._isDevelopment = config.isDevelopment || false;
this._exports = {}; this._exports = {};
}; };
@ -1428,6 +1423,35 @@ function require (path) {
// --SECTION-- public methods // --SECTION-- public methods
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a Json representation of itself to be persisted
////////////////////////////////////////////////////////////////////////////////
ArangoApp.prototype.toJSON = function () {
var json = {
id: this._id,
manifest: this._manifest,
name: this._name,
version: this._version,
root: this._root,
path: this._path,
options: this._options,
mount: this._mount,
isSystem: this._isSystem,
isDevelopment: this._isDevelopment
};
if (this._manifest.hasOwnProperty("author")) {
json.author = this._manifest.author;
}
if (this._manifest.hasOwnProperty("description")) {
json.description = this._manifest.description;
}
if (this._manifest.hasOwnProperty("thumbnail")) {
json.thumbnail = this._manifest.thumbnail;
}
return json;
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief createAppModule /// @brief createAppModule
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -39,6 +39,31 @@ var throwDownloadError = arangodb.throwDownloadError;
var errors = arangodb.errors; var errors = arangodb.errors;
var ArangoError = arangodb.ArangoError; var ArangoError = arangodb.ArangoError;
// TODO Only temporary
var tmp_getStorage = function() {
var c = db._tmp_aal;
if (c === undefined) {
c = db._create("_tmp_aal", {isSystem: true});
c.ensureUniqueConstraint("mount");
}
return c;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief comparator for mount points
////////////////////////////////////////////////////////////////////////////////
var compareMounts = function(l, r) {
var left = l.mount.toLowerCase();
var right = r.mount.toLowerCase();
if (left < right) {
return -1;
}
return 1;
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief builds a github repository URL /// @brief builds a github repository URL
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -248,31 +273,34 @@ function getStorage () {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief returns all installed FOXX applications /// @brief returns all running Foxx applications
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function listJson (showPrefix) { function listJson (showPrefix, onlyDevelopment) {
'use strict'; 'use strict';
var aal = getStorage(); var mounts = tmp_getStorage();
var cursor = aal.byExample({ type: "mount" }); var cursor;
if (onlyDevelopment) {
cursor = mounts.byExample({isDevelopment: true});
} else {
cursor = mounts.all();
}
var result = []; var result = [];
var doc, res;
while (cursor.hasNext()) { while (cursor.hasNext()) {
var doc = cursor.next(); doc = cursor.next();
var version = doc.app.replace(/^.+:(\d+(\.\d+)*)$/g, "$1"); res = {
var res = {
mountId: doc._key, mountId: doc._key,
mount: doc.mount, mount: doc.mount,
appId: doc.app,
name: doc.name, name: doc.name,
description: doc.description, description: doc.manifest.description,
author: doc.author, author: doc.manifest.author,
system: doc.isSystem ? "yes" : "no", system: doc.isSystem ? "yes" : "no",
active: doc.active ? "yes" : "no", development: doc.isDevelopment ? "yes" : "no",
version: version version: doc.version
}; };
if (showPrefix) { if (showPrefix) {
@ -285,6 +313,52 @@ function listJson (showPrefix) {
return result; return result;
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief prints all running Foxx applications
////////////////////////////////////////////////////////////////////////////////
function list(onlyDevelopment) {
var apps = listJson(undefined, onlyDevelopment);
arangodb.printTable(
apps.sort(compareMounts),
[ "mount", "name", "author", "description", "version", "development" ],
{
prettyStrings: true,
totalString: "%s application(s) found",
emptyString: "no applications found",
rename: {
"mount": "Mount",
"name" : "Name",
"author" : "Author",
"description" : "Description",
"version" : "Version",
"development" : "Development"
}
}
);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns all running Foxx applications in development mode
////////////////////////////////////////////////////////////////////////////////
function listDevelopmentJson (showPrefix) {
'use strict';
return listJson(showPrefix, true);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief prints all running Foxx applications
////////////////////////////////////////////////////////////////////////////////
function listDevelopment() {
'use strict';
return list(true);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief validate an app name and fail if it is invalid /// @brief validate an app name and fail if it is invalid
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -302,19 +376,28 @@ function validateAppName (name) {
}); });
} }
function mountedApp (mount) {
"use strict";
return tmp_getStorage().firstExample({mount: mount});
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Exports /// @brief Exports
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
exports.mountedApp = mountedApp;
exports.list = list;
exports.listJson = listJson; exports.listJson = listJson;
exports.listDevelopment = listDevelopment;
exports.listDevelopmentJson = listDevelopmentJson;
exports.buildGithubUrl = buildGithubUrl; exports.buildGithubUrl = buildGithubUrl;
exports.repackZipFile = repackZipFile; exports.repackZipFile = repackZipFile;
exports.processDirectory = processDirectory; exports.processDirectory = processDirectory;
exports.processGithubRepository = processGithubRepository; exports.processGithubRepository = processGithubRepository;
exports.validateAppName = validateAppName; exports.validateAppName = validateAppName;
exports.tmp_getStorage = tmp_getStorage;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE // --SECTION-- END-OF-FILE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -70,7 +70,7 @@ exports.Helper = {
else if (iterations === 400) { else if (iterations === 400) {
require("console").log("waited very long for unload of collection " + collection.name()); require("console").log("waited very long for unload of collection " + collection.name());
} }
else if (iterations === 800) { else if (iterations === 1600) {
throw "waited too long for unload of collection " + collection.name(); throw "waited too long for unload of collection " + collection.name();
} }
} }

View File

@ -0,0 +1,192 @@
/*global require, db, assertEqual, assertTrue, ArangoCollection */
////////////////////////////////////////////////////////////////////////////////
/// @brief test the cap constraint
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 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 Dr. Frank Celler, Lucas Dohmen
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var internal = require("internal");
var testHelper = require("org/arangodb/test-helper").Helper;
// -----------------------------------------------------------------------------
// --SECTION-- basic methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: Creation
/// these tests seem to fail when run in cluster or with valgrind from arangosh
/// (while they succeed when run on the frontend)
/// test-helper.js:Helper.waitUnload expects timeley behaviour
////////////////////////////////////////////////////////////////////////////////
function CapConstraintTimecriticalSuite() {
var ERRORS = internal.errors;
var cn = "UnitTestsCollectionCap";
var collection = null;
var nsort = function (l, r) {
if (l !== r) {
return (l < r) ? -1 : 1;
}
return 0;
};
var assertBadParameter = function (err) {
assertTrue(err.errorNum === ERRORS.ERROR_BAD_PARAMETER.code ||
err.errorNum === ERRORS.ERROR_HTTP_BAD_PARAMETER.code);
};
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
internal.db._drop(cn);
collection = internal.db._create(cn);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
if (collection !== null) {
internal.wait(0);
collection.unload();
collection.drop();
collection = null;
}
internal.wait(0.0);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: reload
////////////////////////////////////////////////////////////////////////////////
testReload : function () {
var idx = collection.ensureCapConstraint(5);
var fun = function(d) { return d.n; };
assertEqual("cap", idx.type);
assertEqual(true, idx.isNewlyCreated);
assertEqual(5, idx.size);
assertEqual(0, idx.byteSize);
assertEqual(0, collection.count());
for (var i = 0; i < 10; ++i) {
collection.save({ n : i });
}
assertEqual(5, collection.count());
assertEqual([5, 6, 7, 8, 9], collection.toArray().map(fun).sort(nsort));
testHelper.waitUnload(collection, true);
assertEqual(5, collection.count());
assertEqual([5, 6, 7, 8, 9], collection.toArray().map(fun).sort(nsort));
collection.save({ n : 10 });
assertEqual(5, collection.count());
assertEqual([6, 7, 8, 9, 10], collection.toArray().map(fun).sort(nsort));
collection.save({ n : 11 });
assertEqual(5, collection.count());
assertEqual([7, 8, 9, 10, 11], collection.toArray().map(fun).sort(nsort));
testHelper.waitUnload(collection, true);
assertEqual(5, collection.count());
assertEqual([7, 8, 9, 10, 11], collection.toArray().map(fun).sort(nsort));
for (var i = 15; i < 20; ++i) {
collection.save({ n : i });
}
assertEqual(5, collection.count());
assertEqual([15, 16, 17, 18, 19], collection.toArray().map(fun).sort(nsort));
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: reload
////////////////////////////////////////////////////////////////////////////////
testReloadMulti : function () {
var idx = collection.ensureCapConstraint(5);
var fun = function(d) { return d.n; };
assertEqual("cap", idx.type);
assertEqual(true, idx.isNewlyCreated);
assertEqual(5, idx.size);
assertEqual(0, idx.byteSize);
assertEqual(0, collection.count());
testHelper.waitUnload(collection, true);
assertEqual(0, collection.count());
for (var i = 0; i < 10; ++i) {
collection.save({ n : i });
}
testHelper.waitUnload(collection, true);
assertEqual(5, collection.count());
assertEqual([5, 6, 7, 8, 9], collection.toArray().map(fun).sort(nsort));
collection.save({ n : 10 });
testHelper.waitUnload(collection, true);
assertEqual(5, collection.count());
assertEqual([6, 7, 8, 9, 10], collection.toArray().map(fun).sort(nsort));
collection.save({ n: 0 });
assertEqual([0, 7, 8, 9, 10], collection.toArray().map(fun).sort(nsort));
}
};
}
// -----------------------------------------------------------------------------
// --SECTION-- main
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suites
////////////////////////////////////////////////////////////////////////////////
jsunity.run(CapConstraintTimecriticalSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -398,93 +398,6 @@ function CapConstraintSuite() {
assertEqual([ "abc", "def", "ghi" ], collection.toArray().map(fun).sort()); assertEqual([ "abc", "def", "ghi" ], collection.toArray().map(fun).sort());
}, },
////////////////////////////////////////////////////////////////////////////////
/// @brief test: reload
////////////////////////////////////////////////////////////////////////////////
testReload : function () {
var idx = collection.ensureCapConstraint(5);
var fun = function(d) { return d.n; };
assertEqual("cap", idx.type);
assertEqual(true, idx.isNewlyCreated);
assertEqual(5, idx.size);
assertEqual(0, idx.byteSize);
assertEqual(0, collection.count());
for (var i = 0; i < 10; ++i) {
collection.save({ n : i });
}
assertEqual(5, collection.count());
assertEqual([5, 6, 7, 8, 9], collection.toArray().map(fun).sort(nsort));
testHelper.waitUnload(collection, true);
assertEqual(5, collection.count());
assertEqual([5, 6, 7, 8, 9], collection.toArray().map(fun).sort(nsort));
collection.save({ n : 10 });
assertEqual(5, collection.count());
assertEqual([6, 7, 8, 9, 10], collection.toArray().map(fun).sort(nsort));
collection.save({ n : 11 });
assertEqual(5, collection.count());
assertEqual([7, 8, 9, 10, 11], collection.toArray().map(fun).sort(nsort));
testHelper.waitUnload(collection, true);
assertEqual(5, collection.count());
assertEqual([7, 8, 9, 10, 11], collection.toArray().map(fun).sort(nsort));
for (var i = 15; i < 20; ++i) {
collection.save({ n : i });
}
assertEqual(5, collection.count());
assertEqual([15, 16, 17, 18, 19], collection.toArray().map(fun).sort(nsort));
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test: reload
////////////////////////////////////////////////////////////////////////////////
testReloadMulti : function () {
var idx = collection.ensureCapConstraint(5);
var fun = function(d) { return d.n; };
assertEqual("cap", idx.type);
assertEqual(true, idx.isNewlyCreated);
assertEqual(5, idx.size);
assertEqual(0, idx.byteSize);
assertEqual(0, collection.count());
testHelper.waitUnload(collection, true);
assertEqual(0, collection.count());
for (var i = 0; i < 10; ++i) {
collection.save({ n : i });
}
testHelper.waitUnload(collection, true);
assertEqual(5, collection.count());
assertEqual([5, 6, 7, 8, 9], collection.toArray().map(fun).sort(nsort));
collection.save({ n : 10 });
testHelper.waitUnload(collection, true);
assertEqual(5, collection.count());
assertEqual([6, 7, 8, 9, 10], collection.toArray().map(fun).sort(nsort));
collection.save({ n: 0 });
assertEqual([0, 7, 8, 9, 10], collection.toArray().map(fun).sort(nsort));
},
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief test: cap with byteSize /// @brief test: cap with byteSize
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -1577,8 +1577,15 @@ function resultOk (req, res, httpReturnCode, result, headers) {
result = {}; result = {};
} }
// check the type of the result.
if (typeof result !== "string" &&
typeof result !== "number" &&
typeof result !== "boolean") {
// only modify result properties if none of the above types
// otherwise the strict mode will throw
result.error = false; result.error = false;
result.code = httpReturnCode; result.code = httpReturnCode;
}
res.body = JSON.stringify(result); res.body = JSON.stringify(result);

View File

@ -493,6 +493,9 @@ shutdownActions.startServers = function (dispatchers, cmd, run) {
var i; var i;
var url; var url;
var r; var r;
var serverStates = {};
var error = false;
for (i = 0;i < run.endpoints.length;i++) { for (i = 0;i < run.endpoints.length;i++) {
console.info("Using API to shutdown %s", JSON.stringify(run.pids[i])); console.info("Using API to shutdown %s", JSON.stringify(run.pids[i]));
url = endpointToURL(run.endpoints[i])+"/_admin/shutdown"; url = endpointToURL(run.endpoints[i])+"/_admin/shutdown";
@ -514,13 +517,20 @@ shutdownActions.startServers = function (dispatchers, cmd, run) {
for (i = 0;i < run.pids.length;i++) { for (i = 0;i < run.pids.length;i++) {
var s = statusExternal(run.pids[i]); var s = statusExternal(run.pids[i]);
if (s.status !== "TERMINATED") { if (s.status !== "TERMINATED") {
if (s.hasOwnProperty('signal')) {
error = true;
console.info("done - with problems: " + s);
}
else {
console.info("Shutting down %s the hard way...", console.info("Shutting down %s the hard way...",
JSON.stringify(run.pids[i])); JSON.stringify(run.pids[i]));
killExternal(run.pids[i]); s.killedState = killExternal(run.pids[i]);
console.info("done."); console.info("done.");
} }
serverStates[run.pids[i]] = s;
} }
return {"error": false, "isStartServers": true}; }
return {"error": error, "isStartServers": true, "serverStates" : serverStates};
}; };
cleanupActions.startAgent = function (dispatchers, cmd) { cleanupActions.startAgent = function (dispatchers, cmd) {

View File

@ -152,7 +152,7 @@ BaseMiddleware = function () {
/// ///
/// `request.requestParts()` /// `request.requestParts()`
/// ///
/// Returns a list containing the individual parts of a multi-part request. /// Returns an array containing the individual parts of a multi-part request.
/// Each part contains a `headers` attribute with all headers of the part, /// Each part contains a `headers` attribute with all headers of the part,
/// and a `data` attribute with the content of the part in a Buffer object. /// and a `data` attribute with the content of the part in a Buffer object.
/// If the request is not a multi-part request, this function will throw an /// If the request is not a multi-part request, this function will throw an

View File

@ -24,10 +24,11 @@
/// ///
/// Copyright holder is triAGENS GmbH, Cologne, Germany /// Copyright holder is triAGENS GmbH, Cologne, Germany
/// ///
/// @author Dr. Frank Celler /// @author Dr. Frank Celler, Michael Hackstein
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany /// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/*
var arangodb = require("org/arangodb"); var arangodb = require("org/arangodb");
var ArangoError = arangodb.ArangoError; var ArangoError = arangodb.ArangoError;
var errors = arangodb.errors; var errors = arangodb.errors;
@ -50,6 +51,7 @@ var download = require("internal").download;
var throwDownloadError = arangodb.throwDownloadError; var throwDownloadError = arangodb.throwDownloadError;
var throwFileNotFound = arangodb.throwFileNotFound; var throwFileNotFound = arangodb.throwFileNotFound;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- private functions // --SECTION-- private functions
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -177,52 +179,6 @@ function checkManifest (filename, mf) {
} }
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief extend a context with some helper functions
////////////////////////////////////////////////////////////////////////////////
function extendContext (context, app, root) {
"use strict";
var cp = context.collectionPrefix;
var cname = "";
if (cp === "_") {
cname = "_";
}
else if (cp !== "") {
cname = cp + "_";
}
context.collectionName = function (name) {
var replaced = cname + name.replace(/[^a-zA-Z0-9]/g, '_').replace(/(^_+|_+$)/g, '').substr(0, 64);
if (replaced.length === 0) {
throw new Error("Cannot derive collection name from '" + name + "'");
}
return replaced;
};
context.collection = function (name) {
return arangodb.db._collection(this.collectionName(name));
};
context.path = function (name) {
return fs.join(root, app._path, name);
};
context.comments = [];
context.comment = function (str) {
this.comments.push(str);
};
context.clearComments = function () {
this.comments = [];
};
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief finds mount document from mount path or identifier /// @brief finds mount document from mount path or identifier
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -865,40 +821,6 @@ exports.mount = function (appId, mount, options) {
return { appId: app._id, mountId: doc._key, mount: mount }; return { appId: app._id, mountId: doc._key, mount: mount };
}; };
////////////////////////////////////////////////////////////////////////////////
/// @brief tears down a Foxx application
///
/// Input:
/// * mount: the mount path starting with a "/"
///
/// Output:
/// -
////////////////////////////////////////////////////////////////////////////////
exports.teardown = function (mount) {
"use strict";
checkParameter(
"teardown(<mount>)",
[ [ "Mount identifier", "string" ] ],
[ mount ] );
var appId;
try {
var doc = mountFromId(mount);
appId = doc.app;
var app = createApp(appId);
teardownApp(app, mount, doc.options.collectionPrefix);
} catch (err) {
console.errorLines(
"Teardown not possible for mount '%s': %s", mount, String(err.stack || err));
throw err;
}
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief unmounts a Foxx application /// @brief unmounts a Foxx application
/// ///
@ -1209,16 +1131,6 @@ exports.fetchFromGithub = function (url, name, version) {
return "app:" + source.name + ":" + source.version; return "app:" + source.name + ":" + source.version;
}; };
////////////////////////////////////////////////////////////////////////////////
///// @brief returns all installed FOXX applications
////////////////////////////////////////////////////////////////////////////////
exports.listJson = function () {
"use strict";
return utils.listJson();
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief initializes the Foxx apps /// @brief initializes the Foxx apps
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1287,6 +1199,7 @@ exports.initializeFoxx = function () {
}); });
} }
}; };
*/
(function() { (function() {
"use strict"; "use strict";
@ -1294,6 +1207,22 @@ exports.initializeFoxx = function () {
// --CHAPTER-- used code // --CHAPTER-- used code
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- imports
// -----------------------------------------------------------------------------
var fs = require("fs");
var utils = require("org/arangodb/foxx/manager-utils");
var store = require("org/arangodb/foxx/store");
var arangodb = require("org/arangodb");
var ArangoError = arangodb.ArangoError;
var checkParameter = arangodb.checkParameter;
var errors = arangodb.errors;
var download = require("internal").download;
var throwDownloadError = arangodb.throwDownloadError;
var throwFileNotFound = arangodb.throwFileNotFound;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- private variables // --SECTION-- private variables
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -1321,6 +1250,52 @@ var prefixFromMount = function(mount) {
return mount.substr(1).replace(/-/g, "_").replace(/\//g, "_"); return mount.substr(1).replace(/-/g, "_").replace(/\//g, "_");
}; };
////////////////////////////////////////////////////////////////////////////////
/// @brief extend a context with some helper functions
////////////////////////////////////////////////////////////////////////////////
var extendContext = function(context, app, root) {
var cp = context.collectionPrefix;
var cname = "";
if (cp === "_") {
cname = "_";
}
else if (cp !== "") {
cname = cp + "_";
}
context.collectionName = function (name) {
var replaced = cname + name.replace(/[^a-zA-Z0-9]/g, '_').replace(/(^_+|_+$)/g, '').substr(0, 64);
if (replaced.length === 0) {
throw new Error("Cannot derive collection name from '" + name + "'");
}
return replaced;
};
context.collection = function (name) {
return arangodb.db._collection(this.collectionName(name));
};
context.path = function (name) {
return fs.join(root, app._path, name);
};
context.comments = [];
context.comment = function (str) {
this.comments.push(str);
};
context.clearComments = function () {
this.comments = [];
};
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief validates a manifest file and returns it. /// @brief validates a manifest file and returns it.
/// All errors are handled including file not found. Returns undefined if manifest is invalid /// All errors are handled including file not found. Returns undefined if manifest is invalid
@ -1348,16 +1323,22 @@ var validateManifestFile = function(file) {
return mf; return mf;
}; };
////////////////////////////////////////////////////////////////////////////////
/// @brief Checks if the mountpoint is reserved for system apps
////////////////////////////////////////////////////////////////////////////////
var isSystemMount = function(mount) {
return (/^\/_/).test(mount);
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief returns the root path for application. Knows about system apps /// @brief returns the root path for application. Knows about system apps
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
var computeRootAppPath = function(mount) { var computeRootAppPath = function(mount) {
if (/^\/_/.test(mount)) { if (isSystemMount(mount)) {
// Must be a system app
return module.systemAppPath(); return module.systemAppPath();
} }
// A standard app
return module.appPath(); return module.appPath();
}; };
@ -1366,7 +1347,9 @@ var computeRootAppPath = function(mount) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
var transformMountToPath = function(mount) { var transformMountToPath = function(mount) {
return fs.join.apply(this, mount.split("/").push("APP")); var list = mount.split("/");
list.push("APP");
return fs.join.apply(this, list);
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1415,36 +1398,29 @@ var executeAppScript = function(app, name, mount, prefix) {
app.loadAppScript(appContext, desc[name]); app.loadAppScript(appContext, desc[name]);
} }
} };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief sets up an app /// @brief sets up an app
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function setupApp (app, mount, prefix) { var setupApp = function (app, mount, prefix) {
"use strict";
return executeAppScript(app, "setup", mount, prefix); return executeAppScript(app, "setup", mount, prefix);
} };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief tears down an app /// @brief tears down an app
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function teardownApp (app, mount, prefix) { var teardownApp = function (app, mount, prefix) {
"use strict";
return executeAppScript(app, "teardown", mount, prefix); return executeAppScript(app, "teardown", mount, prefix);
} };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief returns the app path and manifest /// @brief returns the app path and manifest
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
var appDescription = function (mount, options) { var appConfig = function (mount, options) {
var root = computeRootAppPath(mount); var root = computeRootAppPath(mount);
var path = transformMountToPath(mount); var path = transformMountToPath(mount);
@ -1462,7 +1438,10 @@ function teardownApp (app, mount, prefix) {
root: root, root: root,
path: path, path: path,
manifest: manifest, manifest: manifest,
options: options options: options,
mount: mount,
isSystem: isSystemMount(mount),
isDevelopment: false
}; };
}; };
@ -1473,8 +1452,9 @@ function teardownApp (app, mount, prefix) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
var createApp = function(mount, options) { var createApp = function(mount, options) {
var description = appDescription(mount); var config = appConfig(mount);
var app = module.createApp(description, options || {}); config.options = options || {};
var app = module.createApp(config);
if (app === null) { if (app === null) {
console.errorLines( console.errorLines(
"Cannot find application '%s'", mount); "Cannot find application '%s'", mount);
@ -1632,7 +1612,7 @@ var installAppFromLocal = function(path, targetPath) {
/// @brief sets up a Foxx application /// @brief sets up a Foxx application
/// ///
/// Input: /// Input:
/// * mount: the mount identifier or path /// * mount: the mount path starting with a "/"
/// ///
/// Output: /// Output:
/// - /// -
@ -1655,6 +1635,32 @@ var setup = function (mount) {
} }
}; };
////////////////////////////////////////////////////////////////////////////////
/// @brief tears down a Foxx application
///
/// Input:
/// * mount: the mount path starting with a "/"
///
/// Output:
/// -
////////////////////////////////////////////////////////////////////////////////
var teardown = function (mount) {
checkParameter(
"teardown(<mount>)",
[ [ "Mount path", "string" ] ],
[ mount ] );
var app = lookupApp(mount);
try {
teardownApp(app, mount, prefixFromMount(mount));
} catch (err) {
console.errorLines(
"Teardown not possible for mount '%s': %s", mount, String(err.stack || err));
throw err;
}
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Scans the sources of the given mountpoint and publishes the routes /// @brief Scans the sources of the given mountpoint and publishes the routes
/// ///
@ -1662,22 +1668,16 @@ var setup = function (mount) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
var scanFoxx = function(mount, options) { var scanFoxx = function(mount, options) {
delete appCache[mount]; delete appCache[mount];
createApp(mount, options); var app = createApp(mount, options);
utils.tmp_getStorage().save(app.toJSON());
// TODO Routing?
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Installs a new foxx application on the given mount point. /// @brief Internal install function. Check install.
/// /// Does not check parameters and throws errors.
/// TODO: Long Documentation!
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
var install = function(appInfo, mount, options) { var _install = function(appInfo, mount, options, runSetup) {
checkParameter(
"mount(<appInfo>, <mount>, [<options>])",
[ [ "Install information", "string" ],
[ "Mount path", "string" ] ],
[ appInfo, mount ] );
var targetPath = computeAppPath(mount, true); var targetPath = computeAppPath(mount, true);
if (fs.exists(targetPath)) { if (fs.exists(targetPath)) {
throw "An app is already installed at this location."; throw "An app is already installed at this location.";
@ -1685,7 +1685,7 @@ var install = function(appInfo, mount, options) {
fs.makeDirectoryRecursive(targetPath); fs.makeDirectoryRecursive(targetPath);
// Remove the empty APP folder. // Remove the empty APP folder.
// Ohterwise move will fail. // Ohterwise move will fail.
fs.removeDirectoryRecursive(targetPath); fs.removeDirectory(targetPath);
if (appInfo === "EMPTY") { if (appInfo === "EMPTY") {
// Make Empty app // Make Empty app
@ -1701,27 +1701,110 @@ var install = function(appInfo, mount, options) {
throw "Not implemented yet"; throw "Not implemented yet";
} }
scanFoxx(mount, options); scanFoxx(mount, options);
if (runSetup) {
setup(mount); setup(mount);
}
}; };
////////////////////////////////////////////////////////////////////////////////
/// @brief Installs a new foxx application on the given mount point.
///
/// TODO: Long Documentation!
////////////////////////////////////////////////////////////////////////////////
var install = function(appInfo, mount, options) {
checkParameter(
"mount(<appInfo>, <mount>, [<options>])",
[ [ "Install information", "string" ],
[ "Mount path", "string" ] ],
[ appInfo, mount ] );
_install(appInfo, mount, options, true);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Internal install function. Check install.
/// Does not check parameters and throws errors.
////////////////////////////////////////////////////////////////////////////////
var _uninstall = function(mount) {
var targetPath = computeAppPath(mount, true);
if (!fs.exists(targetPath)) {
throw "No foxx app found at this location.";
}
teardown(mount);
// TODO Delete routing?
utils.tmp_getStorage().removeByExample({mount: mount});
delete appCache[mount];
// Remove the APP folder.
fs.removeDirectoryRecursive(targetPath, true);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Uninstalls the foxx application on the given mount point.
///
/// TODO: Long Documentation!
////////////////////////////////////////////////////////////////////////////////
var uninstall = function(mount) {
checkParameter(
"mount(<mount>)",
[ [ "Mount path", "string" ] ],
[ mount ] );
_uninstall(mount);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Replaces a foxx application on the given mount point by an other one.
///
/// TODO: Long Documentation!
////////////////////////////////////////////////////////////////////////////////
var replace = function(appInfo, mount, options) {
checkParameter(
"mount(<appInfo>, <mount>, [<options>])",
[ [ "Install information", "string" ],
[ "Mount path", "string" ] ],
[ appInfo, mount ] );
_uninstall(mount, true);
_install(appInfo, mount, options, true);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Upgrade a foxx application on the given mount point by a new one.
///
/// TODO: Long Documentation!
////////////////////////////////////////////////////////////////////////////////
var upgrade = function(appInfo, mount, options) {
checkParameter(
"mount(<appInfo>, <mount>, [<options>])",
[ [ "Install information", "string" ],
[ "Mount path", "string" ] ],
[ appInfo, mount ] );
_uninstall(mount, false);
_install(appInfo, mount, options, false);
};
// -----------------------------------------------------------------------------
// --SECTION-- exports
// -----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Exports /// @brief Exports
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
exports.scanFoxx = scanFoxx;
exports.install = install; exports.install = install;
/*
exports.uninstall = uninstall;
exports.setup = setup; exports.setup = setup;
exports.teardown = teardown; exports.teardown = teardown;
exports.list = list; exports.uninstall = uninstall;
exports.listJson = listJson;
exports.replace = replace; exports.replace = replace;
exports.mountedApp = mountedApp;
exports.upgrade = upgrade; exports.upgrade = upgrade;
exports.scanFoxx = scanFoxx;
exports.developmentMounts = developmentMounts; ////////////////////////////////////////////////////////////////////////////////
exports.developmentMountsJson = developmentMountsJson; /// @brief Exports from foxx utils module.
*/ ////////////////////////////////////////////////////////////////////////////////
exports.mountedApp = utils.mountedApp;
exports.list = utils.list;
exports.listJson = utils.listJson;
exports.listDevelopment = utils.listDevelopment;
exports.listDevelopmentJson = utils.listDevelopmentJson;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Exports from foxx store module. /// @brief Exports from foxx store module.

View File

@ -98,6 +98,7 @@ var Planner = require("org/arangodb/cluster").Planner;
var Kickstarter = require("org/arangodb/cluster").Kickstarter; var Kickstarter = require("org/arangodb/cluster").Kickstarter;
var endpointToURL = require("org/arangodb/cluster/planner").endpointToURL; var endpointToURL = require("org/arangodb/cluster/planner").endpointToURL;
var serverCrashed = false;
var optionsDefaults = { "cluster": false, var optionsDefaults = { "cluster": false,
"valgrind": false, "valgrind": false,
@ -394,6 +395,9 @@ function checkInstanceAlive(instanceInfo, options) {
copy("bin/arangod", storeArangodPath); copy("bin/arangod", storeArangodPath);
} }
} }
if (!ret) {
serverCrashed = true;
}
return ret; return ret;
} }
var ClusterFit = true; var ClusterFit = true;
@ -419,30 +423,69 @@ function checkInstanceAlive(instanceInfo, options) {
} }
} }
} }
return ClusterFit && instanceInfo.kickstarter.isHealthy(); if (ClusterFit && instanceInfo.kickstarter.isHealthy()) {
return true;
}
else {
serverCrashed = true;
return false;
}
} }
function shutdownInstance (instanceInfo, options) { function shutdownInstance (instanceInfo, options) {
if (!checkInstanceAlive(instanceInfo, options)) {
print("Server already dead, doing nothing. This shouldn't happen?");
}
if (options.cluster) { if (options.cluster) {
instanceInfo.kickstarter.shutdown(); var rc = instanceInfo.kickstarter.shutdown();
if (options.cleanup) { if (options.cleanup) {
instanceInfo.kickstarter.cleanup(); instanceInfo.kickstarter.cleanup();
} }
if (rc.error) {
for (var i in rc.serverStates) {
if (rc.serverStates.hasOwnProperty(i)){
if (rc.serverStates[i].hasOwnProperty('signal')) {
print("Server shut down with : " + rc.serverStates[i] + " marking run as crashy.");
serverCrashed = true;
}
}
}
}
} }
else { else {
if (typeof(instanceInfo.exitStatus) === 'undefined') { if (typeof(instanceInfo.exitStatus) === 'undefined') {
download(instanceInfo.url+"/_admin/shutdown","", download(instanceInfo.url+"/_admin/shutdown","",
makeAuthorisationHeaders(options)); makeAuthorisationHeaders(options));
if (typeof(options.valgrind) === 'string') {
print("Waiting for server shut down"); print("Waiting for server shut down");
var res = statusExternal(instanceInfo.pid, true); var count = 0;
print("Server gone: "); while (1) {
print(res); instanceInfo.exitStatus = statusExternal(instanceInfo.pid, false);
if (instanceInfo.exitStatus.status === "RUNNING") {
count ++;
if (typeof(options.valgrind) === 'string') {
continue;
}
if (count > 10) {
print("forcefully terminating " + instanceInfo.pid + " after 10 s grace period.");
serverCrashed = true;
killExternal(instanceInfo.pid);
break;
} }
else { else {
wait(10); wait(1);
killExternal(instanceInfo.pid); }
}
else if (instanceInfo.exitStatus.status !== "TERMINATED") {
if (instanceInfo.exitStatus.hasOwnProperty('signal')) {
print("Server shut down with : " + instanceInfo.exitStatus + " marking build as crashy.");
serverCrashed = true;
}
}
else {
print("Server shutdown: Success.");
break; // Success.
}
} }
} }
else { else {
@ -1485,7 +1528,7 @@ testFuncs.authentication_parameters = function (options) {
return results; return results;
}; };
var internalMembers = ["code", "error", "status", "duration", "failed", "total"]; var internalMembers = ["code", "error", "status", "duration", "failed", "total", "crashed", "all_ok", "ok", "message"];
function unitTestPrettyPrintResults(r) { function unitTestPrettyPrintResults(r) {
var testrun; var testrun;
@ -1498,13 +1541,13 @@ function unitTestPrettyPrintResults(r) {
try { try {
for (testrun in r) { for (testrun in r) {
if (r.hasOwnProperty(testrun) && (testrun !== 'all_ok')) { if (r.hasOwnProperty(testrun) && (internalMembers.indexOf(testrun) === -1)) {
var isSuccess = true; var isSuccess = true;
var oneOutput = ""; var oneOutput = "";
oneOutput = "Testrun: " + testrun + "\n"; oneOutput = "Testrun: " + testrun + "\n";
for (test in r[testrun]) { for (test in r[testrun]) {
if (r[testrun].hasOwnProperty(test) && (test !== 'ok')) { if (r[testrun].hasOwnProperty(test) && (internalMembers.indexOf(test) === -1)) {
if (r[testrun][test].status) { if (r[testrun][test].status) {
oneOutput += " [Success] " + test + "\n"; oneOutput += " [Success] " + test + "\n";
} }
@ -1599,6 +1642,7 @@ function UnitTest (which, options) {
results.all_ok = allok; results.all_ok = allok;
} }
results.all_ok = allok; results.all_ok = allok;
results.crashed = serverCrashed;
if (allok) { if (allok) {
cleanupDBDirectories(options); cleanupDBDirectories(options);
} }
@ -1634,6 +1678,7 @@ function UnitTest (which, options) {
} }
r.ok = ok; r.ok = ok;
results.all_ok = ok; results.all_ok = ok;
results.crashed = serverCrashed;
if (allok) { if (allok) {
cleanupDBDirectories(options); cleanupDBDirectories(options);
@ -1652,6 +1697,7 @@ function UnitTest (which, options) {
} }
} }
exports.internalMembers = internalMembers;
exports.testFuncs = testFuncs; exports.testFuncs = testFuncs;
exports.UnitTest = UnitTest; exports.UnitTest = UnitTest;
exports.unitTestPrettyPrintResults = unitTestPrettyPrintResults; exports.unitTestPrettyPrintResults = unitTestPrettyPrintResults;

View File

@ -1,7 +1,9 @@
/*jshint unused: false */ /*jshint unused: false */
/*global require, start_pretty_print */ /*global require, start_pretty_print */
var internalMembers = ["code", "error", "status", "duration", "failed", "total", "message"]; var UnitTest = require("org/arangodb/testing");
var internalMembers = UnitTest.internalMembers;
var fs = require("fs"); var fs = require("fs");
var print = require("internal").print; var print = require("internal").print;
@ -46,10 +48,10 @@ function resultsToXml(results, baseName) {
} }
for (var testrun in results) { for (var testrun in results) {
if ((testrun !== "all_ok") && (results.hasOwnProperty(testrun))) { if ((internalMembers.indexOf(testrun) === -1) && (results.hasOwnProperty(testrun))) {
for (var test in results[testrun]) { for (var test in results[testrun]) {
if ((test !== "ok") && if ((internalMembers.indexOf(test) === -1) &&
results[testrun].hasOwnProperty(test) && results[testrun].hasOwnProperty(test) &&
!results[testrun][test].hasOwnProperty('skipped')) { !results[testrun][test].hasOwnProperty('skipped')) {
@ -127,7 +129,6 @@ function main (argv) {
} }
} }
options.jsonReply = true; options.jsonReply = true;
var UnitTest = require("org/arangodb/testing");
start_pretty_print(); start_pretty_print();
try { try {
@ -139,7 +140,7 @@ function main (argv) {
print(JSON.stringify(r)); print(JSON.stringify(r));
} }
fs.write("UNITTEST_RESULT.json",JSON.stringify(r)); fs.write("UNITTEST_RESULT.json",JSON.stringify(r));
fs.write("UNITTEST_RESULT_SUMMARY.txt",JSON.stringify(r.all_ok)); fs.write("UNITTEST_RESULT_SUMMARY.txt",JSON.stringify(!r.crashed));
try { try {
resultsToXml(r, "UNITTEST_RESULT_"); resultsToXml(r, "UNITTEST_RESULT_");
} }