Comparar commits
3 Commits
code-coverage
...
v0.17.0
| Autor | SHA1 | Data | |
|---|---|---|---|
| 2eb037d800 | |||
| 162cefd77e | |||
| 5fc63001f0 |
+2
-2
@@ -28,9 +28,9 @@ Or, if you already have NodeJS (v6) installed, simply run
|
||||
npm install -g deepforge
|
||||
```
|
||||
|
||||
Next, start deepforge with `deepforge start`!
|
||||
Finally, start deepforge with `deepforge start`and navigate to [http://localhost:8888](http://localhost:8888) to start using DeepForge! For more, detailed instructions, check out the [wiki](https://github.com/dfst/deepforge/wiki/Installation-Guide).
|
||||
|
||||
Finally, navigate to [http://localhost:8888](http://localhost:8888) to start using DeepForge! For more, detailed instructions, check out the [wiki](https://github.com/dfst/deepforge/wiki/Installation-Guide).
|
||||
**Note**: running deepforge w/ `deepforge start` will also require [MongoDB](https://www.mongodb.com/download-center?jmp=nav#community) to be installed locally.
|
||||
|
||||
Also, be sure to check out the other available features of the `deepforge` cli; it can be used to update, manage your torch installation, uninstall deepforge and run individual components!
|
||||
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
"CHFLayout": {
|
||||
"panels": [
|
||||
{
|
||||
"id": "Header",
|
||||
"panel": "BreadcrumbHeader/BreadcrumbHeaderPanel",
|
||||
"id": "WorkerHeader",
|
||||
"panel": "WorkerHeader/WorkerHeaderPanel",
|
||||
"container": "header",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
|
||||
@@ -32,6 +32,7 @@ config.visualization.panelPaths.push(__dirname + '/../src/visualizers/panels');
|
||||
|
||||
|
||||
config.rest.components['execution/logs'] = __dirname + '/../src/routers/JobLogsAPI/JobLogsAPI.js';
|
||||
config.rest.components['job/origins'] = __dirname + '/../src/routers/JobOriginAPI/JobOriginAPI.js';
|
||||
|
||||
// Visualizer descriptors
|
||||
config.visualization.visualizerDescriptors.push(__dirname + '/../src/visualizers/Visualizers.json');
|
||||
|
||||
+2
-1
@@ -12,7 +12,7 @@
|
||||
"watch-test": "./node_modules/nodemon/bin/nodemon.js --exec 'node ./node_modules/mocha/bin/mocha --recursive test'",
|
||||
"build-nn": "node ./utils/nn-parser.js"
|
||||
},
|
||||
"version": "0.16.0",
|
||||
"version": "0.17.0",
|
||||
"dependencies": {
|
||||
"commander": "^2.9.0",
|
||||
"dotenv": "^2.0.0",
|
||||
@@ -20,6 +20,7 @@
|
||||
"express": "^4.14.0",
|
||||
"lodash.difference": "^4.1.2",
|
||||
"lodash.merge": "^4.5.1",
|
||||
"mongodb": "^2.2.10",
|
||||
"nodemon": "^1.9.2",
|
||||
"q": "1.4.1",
|
||||
"rimraf": "^2.4.0",
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*globals define*/
|
||||
define([
|
||||
'q',
|
||||
'superagent'
|
||||
], function(
|
||||
Q,
|
||||
superagent
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
// Wrap the ability to read, update, and delete logs using the JobLogsAPI
|
||||
var APIClient = function(params) {
|
||||
params = params || {};
|
||||
|
||||
this.logger = this.logger || params.logger.fork('APIClient');
|
||||
|
||||
// Get the server url
|
||||
this.token = params.token;
|
||||
this.origin = this._getServerUrl(params);
|
||||
this.relativeUrl = this.relativeUrl || '';
|
||||
this.url = this.origin + this.relativeUrl;
|
||||
|
||||
this.logger.debug(`Setting url to ${this.url}`);
|
||||
|
||||
this.branch = params.branchName;
|
||||
this.project = params.projectId;
|
||||
this._modifiedJobs = [];
|
||||
|
||||
this.logger.debug(`Using <project>:<branch>: "${this.project}"/"${this.branch}"`);
|
||||
this.logger.info('ctor finished');
|
||||
};
|
||||
|
||||
APIClient.prototype._getServerUrl = function(params) {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.location.origin;
|
||||
}
|
||||
|
||||
// If not in browser, set using the params
|
||||
var server = params.server || '127.0.0.1',
|
||||
port = params.port || '80',
|
||||
protocol = params.httpsecure ? 'https' : 'http'; // default is http
|
||||
|
||||
return params.origin || `${protocol}://${server}:${port}`;
|
||||
};
|
||||
|
||||
APIClient.prototype.getUrl = function() {
|
||||
return this.url;
|
||||
};
|
||||
|
||||
APIClient.prototype._request = function(method, jobId, content) {
|
||||
var deferred = Q.defer(),
|
||||
req = superagent[method](this.getUrl(jobId));
|
||||
|
||||
this.logger.info(`sending ${method} request to ${this.getUrl(jobId)}`);
|
||||
if (this.token) {
|
||||
req.set('Authorization', 'Bearer ' + this.token);
|
||||
}
|
||||
|
||||
if (content) {
|
||||
req = req.send(content);
|
||||
}
|
||||
|
||||
req.end((err, res) => {
|
||||
if (err || res.status > 399) {
|
||||
return deferred.reject(res || err);
|
||||
}
|
||||
|
||||
return deferred.resolve(res);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
return APIClient;
|
||||
});
|
||||
@@ -1,8 +1,10 @@
|
||||
/* globals define */
|
||||
define([
|
||||
'./APIClient',
|
||||
'q',
|
||||
'superagent'
|
||||
], function(
|
||||
APIClient,
|
||||
Q,
|
||||
superagent
|
||||
) {
|
||||
@@ -12,40 +14,22 @@ define([
|
||||
var JobLogsClient = function(params) {
|
||||
params = params || {};
|
||||
|
||||
this.logger = params.logger.fork('JobLogsClient');
|
||||
|
||||
// Get the server url
|
||||
this.token = params.token;
|
||||
this.origin = this._getServerUrl(params);
|
||||
this.relativeUrl = '/execution/logs';
|
||||
this.url = this.origin + this.relativeUrl;
|
||||
|
||||
this.logger.debug(`Setting url to ${this.url}`);
|
||||
this.logger = params.logger.fork('JobLogsClient');
|
||||
APIClient.call(this, params);
|
||||
|
||||
// Get the project, branch name
|
||||
if (!(params.branchName && params.projectId)) {
|
||||
throw Error('"branchName" and "projectId" required');
|
||||
}
|
||||
this.branch = params.branchName;
|
||||
this.project = params.projectId;
|
||||
|
||||
this._modifiedJobs = [];
|
||||
|
||||
this.logger.debug(`Using <project>:<branch>: "${this.project}"/"${this.branch}"`);
|
||||
this.logger.info('ctor finished');
|
||||
};
|
||||
|
||||
JobLogsClient.prototype._getServerUrl = function(params) {
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.location.origin;
|
||||
}
|
||||
|
||||
// If not in browser, set using the params
|
||||
var server = params.server || '127.0.0.1',
|
||||
port = params.port || '80',
|
||||
protocol = params.httpsecure ? 'https' : 'http'; // default is http
|
||||
|
||||
return params.origin || `${protocol}://${server}:${port}`;
|
||||
};
|
||||
JobLogsClient.prototype = Object.create(APIClient.prototype);
|
||||
|
||||
// This method could be optimized - it could make a log of requests
|
||||
JobLogsClient.prototype.fork = function(forkName) {
|
||||
@@ -87,45 +71,21 @@ define([
|
||||
].join('/');
|
||||
};
|
||||
|
||||
JobLogsClient.prototype._logRequest = function(method, jobId, content) {
|
||||
var deferred = Q.defer(),
|
||||
req = superagent[method](this.getUrl(jobId));
|
||||
|
||||
this.logger.info(`sending ${method} request to ${this.getUrl(jobId)}`);
|
||||
if (this.token) {
|
||||
req.set('Authorization', 'Bearer ' + this.token);
|
||||
}
|
||||
|
||||
if (content) {
|
||||
req = req.send(content);
|
||||
}
|
||||
|
||||
req.end((err, res) => {
|
||||
if (err || res.status > 399) {
|
||||
return deferred.reject(err || res.status);
|
||||
}
|
||||
|
||||
return deferred.resolve(res);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
JobLogsClient.prototype.appendTo = function(jobId, text) {
|
||||
this._modifiedJobs.push(jobId);
|
||||
this.logger.info(`Appending logs to ${jobId}`);
|
||||
return this._logRequest('patch', jobId, {patch: text});
|
||||
return this._request('patch', jobId, {patch: text});
|
||||
};
|
||||
|
||||
JobLogsClient.prototype.getLog = function(jobId) {
|
||||
this.logger.info(`Getting logs for ${jobId}`);
|
||||
return this._logRequest('get', jobId)
|
||||
return this._request('get', jobId)
|
||||
.then(res => res.text);
|
||||
};
|
||||
|
||||
JobLogsClient.prototype.deleteLog = function(jobId) {
|
||||
this.logger.info(`Deleting logs for ${jobId}`);
|
||||
return this._logRequest('delete', jobId);
|
||||
return this._request('delete', jobId);
|
||||
};
|
||||
|
||||
return JobLogsClient;
|
||||
@@ -0,0 +1,60 @@
|
||||
/* globals define */
|
||||
define([
|
||||
'./APIClient'
|
||||
], function(
|
||||
APIClient
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var JobOriginClient = function(params) {
|
||||
this.relativeUrl = '/job/origins/';
|
||||
this.logger = params.logger.fork('JobOriginClient');
|
||||
APIClient.call(this, params);
|
||||
};
|
||||
|
||||
JobOriginClient.prototype = Object.create(APIClient.prototype);
|
||||
|
||||
// - Record the origin
|
||||
// - Look up the origin
|
||||
// - Delete record
|
||||
JobOriginClient.prototype.getUrl = function(hash) {
|
||||
return this.url + hash;
|
||||
};
|
||||
|
||||
JobOriginClient.prototype.record = function(hash, info) {
|
||||
var jobInfo = {
|
||||
hash: hash,
|
||||
nodeId: info.nodeId,
|
||||
job: info.job,
|
||||
project: info.project || this.project,
|
||||
branch: info.branch || this.branch,
|
||||
execution: info.execution
|
||||
};
|
||||
|
||||
return this._request('post', hash, jobInfo)
|
||||
.catch(err => {
|
||||
throw err.text || err;
|
||||
});
|
||||
};
|
||||
|
||||
JobOriginClient.prototype.getOrigin = function(hash) {
|
||||
return this._request('get', hash)
|
||||
.then(res => JSON.parse(res.text))
|
||||
.catch(res => {
|
||||
if (res.status && res.status === 404) {
|
||||
return null;
|
||||
}
|
||||
throw res;
|
||||
});
|
||||
};
|
||||
|
||||
JobOriginClient.prototype.fork = function(hash, forkName) {
|
||||
return this._request('patch', hash, {branch: forkName});
|
||||
};
|
||||
|
||||
JobOriginClient.prototype.deleteRecord = function(hash) {
|
||||
return this._request('delete', hash);
|
||||
};
|
||||
|
||||
return JobOriginClient;
|
||||
});
|
||||
@@ -9,7 +9,8 @@ define([
|
||||
'plugin/PluginBase',
|
||||
'deepforge/plugin/LocalExecutor',
|
||||
'deepforge/plugin/PtrCodeGen',
|
||||
'deepforge/JobLogsClient',
|
||||
'deepforge/api/JobLogsClient',
|
||||
'deepforge/api/JobOriginClient',
|
||||
'deepforge/Constants',
|
||||
'deepforge/utils',
|
||||
'./templates/index',
|
||||
@@ -24,6 +25,7 @@ define([
|
||||
LocalExecutor, // DeepForge operation primitives
|
||||
PtrCodeGen,
|
||||
JobLogsClient,
|
||||
JobOriginClient,
|
||||
CONSTANTS,
|
||||
utils,
|
||||
Templates,
|
||||
@@ -79,14 +81,16 @@ define([
|
||||
ExecuteJob.prototype.constructor = ExecuteJob;
|
||||
|
||||
ExecuteJob.prototype.configure = function () {
|
||||
var result = PluginBase.prototype.configure.apply(this, arguments);
|
||||
var result = PluginBase.prototype.configure.apply(this, arguments),
|
||||
params = {
|
||||
logger: this.logger,
|
||||
port: this.gmeConfig.server.port,
|
||||
branchName: this.branchName,
|
||||
projectId: this.projectId
|
||||
};
|
||||
|
||||
this.logManager = new JobLogsClient({
|
||||
logger: this.logger,
|
||||
port: this.gmeConfig.server.port,
|
||||
branchName: this.branchName,
|
||||
projectId: this.projectId
|
||||
});
|
||||
this.logManager = new JobLogsClient(params);
|
||||
this.originManager = new JobOriginClient(params);
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -863,12 +867,26 @@ define([
|
||||
if (info.secret) { // o.w. it is a cached job!
|
||||
this.setAttribute(job, 'secret', info.secret);
|
||||
}
|
||||
return this.watchOperation(executor, hash, opNode, job);
|
||||
return this.recordJobOrigin(hash, job);
|
||||
})
|
||||
.then(() => this.watchOperation(executor, hash, opNode, job))
|
||||
.catch(err => this.logger.error(`Could not execute "${name}": ${err}`));
|
||||
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.recordJobOrigin = function (hash, job) {
|
||||
var execNode = this.core.getParent(job),
|
||||
info;
|
||||
|
||||
info = {
|
||||
hash: hash,
|
||||
nodeId: this.core.getPath(job),
|
||||
job: this.getAttribute(job, 'name'),
|
||||
execution: this.getAttribute(execNode, 'name')
|
||||
};
|
||||
return this.originManager.record(hash, info);
|
||||
};
|
||||
|
||||
ExecuteJob.prototype.createOperationFiles = function (node) {
|
||||
var files = {};
|
||||
// For each operation, generate the output files:
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
define([
|
||||
'plugin/CreateExecution/CreateExecution/CreateExecution',
|
||||
'plugin/ExecuteJob/ExecuteJob/ExecuteJob',
|
||||
'deepforge/JobLogsClient',
|
||||
'common/storage/constants',
|
||||
'common/core/constants',
|
||||
'q',
|
||||
@@ -13,7 +12,6 @@ define([
|
||||
], function (
|
||||
CreateExecution,
|
||||
ExecuteJob,
|
||||
JobLogsClient,
|
||||
STORAGE_CONSTANTS,
|
||||
CONSTANTS,
|
||||
Q,
|
||||
@@ -118,13 +116,6 @@ define([
|
||||
return callback('Current node is not a Pipeline or Execution!', this.result);
|
||||
}
|
||||
|
||||
// Get the gmeConfig...
|
||||
this.logManager = new JobLogsClient({
|
||||
logger: this.logger,
|
||||
port: this.gmeConfig.server.port,
|
||||
branchName: this.branchName,
|
||||
projectId: this.projectId
|
||||
});
|
||||
this._callback = callback;
|
||||
this.currentForkName = null;
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ JobLogManager.prototype.migrate = function(migrationInfo, jobIds) {
|
||||
}
|
||||
|
||||
// Copy the job files and evaluate each of the finish functions
|
||||
this.logger.info('migrating from ' + migrationInfo.srcBranch + ' to '+ migrationInfo.dstBranch);
|
||||
this.logger.debug('migrating from ' + migrationInfo.srcBranch + ' to '+ migrationInfo.dstBranch);
|
||||
return Q.all(jobIds.map(jobId => {
|
||||
src = this._getFilePath({
|
||||
project: migrationInfo.project,
|
||||
@@ -112,7 +112,7 @@ JobLogManager.prototype.appendTo = function(jobInfo, logs) {
|
||||
branchDirname = path.dirname(filename),
|
||||
projDirname = path.dirname(branchDirname);
|
||||
|
||||
this.logger.info(`Appending content to ${filename}`);
|
||||
this.logger.debug(`Appending content to ${filename}`);
|
||||
// Make directory if needed
|
||||
return this.mkdirIfNeeded(this.rootDir)
|
||||
.then(() => this.mkdirIfNeeded(projDirname))
|
||||
@@ -142,7 +142,7 @@ JobLogManager.prototype.delete = function(jobInfo) {
|
||||
this.logger.debug(`Removing file ${filename}`);
|
||||
return Q.nfcall(fs.unlink, filename);
|
||||
}
|
||||
this.logger.info(`${filename} doesn't exist. No need to delete...`);
|
||||
this.logger.debug(`${filename} doesn't exist. No need to delete...`);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/*jshint node:true*/
|
||||
|
||||
/**
|
||||
* This is an API to record the executor job hash to originating project, execution and job
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// This contains
|
||||
var express = require('express'),
|
||||
MONGO_COLLECTION = 'JobOrigins',
|
||||
Q = require('q'),
|
||||
router = express.Router(),
|
||||
mongodb = require('mongodb');
|
||||
|
||||
/**
|
||||
* Called when the server is created but before it starts to listening to incoming requests.
|
||||
* N.B. gmeAuth, safeStorage and workerManager are not ready to use until the start function is called.
|
||||
* (However inside an incoming request they are all ensured to have been initialized.)
|
||||
*
|
||||
* @param {object} middlewareOpts - Passed by the webgme server.
|
||||
* @param {GmeConfig} middlewareOpts.gmeConfig - GME config parameters.
|
||||
* @param {GmeLogger} middlewareOpts.logger - logger
|
||||
* @param {function} middlewareOpts.ensureAuthenticated - Ensures the user is authenticated.
|
||||
* @param {function} middlewareOpts.getUserId - If authenticated retrieves the userId from the request.
|
||||
* @param {object} middlewareOpts.gmeAuth - Authorization module.
|
||||
* @param {object} middlewareOpts.safeStorage - Accesses the storage and emits events (PROJECT_CREATED, COMMIT..).
|
||||
* @param {object} middlewareOpts.workerManager - Spawns and keeps track of "worker" sub-processes.
|
||||
*/
|
||||
// When testing, use in memory storage...
|
||||
function initialize(middlewareOpts) {
|
||||
var logger = middlewareOpts.logger.fork('JobOriginAPI'),
|
||||
gmeConfig = middlewareOpts.gmeConfig,
|
||||
ensureAuthenticated = middlewareOpts.ensureAuthenticated,
|
||||
REQUIRED_FIELDS = ['hash', 'project', 'execution', 'job', 'nodeId', 'branch'],
|
||||
mongo;
|
||||
|
||||
logger.debug('initializing ...');
|
||||
|
||||
// Ensure authenticated can be used only after this rule.
|
||||
router.use('*', function (req, res, next) {
|
||||
// This header ensures that any failures with authentication won't redirect.
|
||||
res.setHeader('X-WebGME-Media-Type', 'webgme.v1');
|
||||
next();
|
||||
});
|
||||
|
||||
// Use ensureAuthenticated if the routes require authentication. (Can be set explicitly for each route.)
|
||||
router.use('*', ensureAuthenticated);
|
||||
|
||||
// Connect to mongo...
|
||||
Q.nfcall(mongodb.MongoClient.connect, gmeConfig.mongo.uri, gmeConfig.mongo.options)
|
||||
.then(db => {
|
||||
mongo = db.collection(MONGO_COLLECTION);
|
||||
logger.debug('Connected to mongo!');
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Could not connect to mongo: ${err}`);
|
||||
throw err;
|
||||
});
|
||||
|
||||
router.get('/:jobHash', function (req, res/*, next*/) {
|
||||
var hash = req.params.jobHash,
|
||||
jobInfo = {};
|
||||
|
||||
mongo.findOne({hash: hash})
|
||||
.then(result => {
|
||||
if (result) {
|
||||
// Filter the result object
|
||||
for (var i = REQUIRED_FIELDS.length; i--;) {
|
||||
jobInfo[REQUIRED_FIELDS[i]] = result[REQUIRED_FIELDS[i]];
|
||||
}
|
||||
return res.json(jobInfo);
|
||||
}
|
||||
res.sendStatus(404);
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Storing job info failed: ${err}`);
|
||||
res.status(500).send(err);
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/:jobHash', function (req, res/*, next*/) {
|
||||
var hash = req.params.jobHash,
|
||||
jobInfo = {
|
||||
hash: hash,
|
||||
project: req.body.project,
|
||||
execution: req.body.execution,
|
||||
branch: req.body.branch,
|
||||
job: req.body.job, // job name
|
||||
nodeId: req.body.nodeId
|
||||
};
|
||||
|
||||
// Check that none of the fields are undefined
|
||||
logger.debug(`Storing job info for ${hash}`);
|
||||
var fields = REQUIRED_FIELDS;
|
||||
for (var i = fields.length; i--;) {
|
||||
if (!jobInfo[fields[i]]) {
|
||||
return res.status(400).send(`Missing required field: ${fields[i]}`);
|
||||
}
|
||||
}
|
||||
|
||||
return mongo.insertOne(jobInfo)
|
||||
.then(() => res.sendStatus(201))
|
||||
.catch(err => {
|
||||
logger.error(`Storing job info failed: ${err}`);
|
||||
res.status(500).send(err.toString());
|
||||
});
|
||||
});
|
||||
|
||||
router.delete('/:jobHash', function (req, res/*, next*/) {
|
||||
var hash = req.params.jobHash;
|
||||
|
||||
mongo.findOneAndDelete({hash: hash})
|
||||
.then(() => res.sendStatus(204));
|
||||
});
|
||||
|
||||
// on fork
|
||||
router.patch('/:jobHash', function (req, res) {
|
||||
var hash = req.params.jobHash;
|
||||
|
||||
if (!req.body.branch) {
|
||||
return res.status(400).send('Missing "branch" field');
|
||||
}
|
||||
|
||||
return mongo.findOneAndUpdate({hash: hash}, {$set: {branch: req.body.branch}})
|
||||
.then(() => {
|
||||
logger.debug('Finished updateOne!');
|
||||
res.sendStatus(200);
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Job update failed: ${err}`);
|
||||
res.status(500).send(err);
|
||||
});
|
||||
});
|
||||
|
||||
logger.debug('ready');
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before the server starts listening.
|
||||
* @param {function} callback
|
||||
*/
|
||||
function start(callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the server stopped listening.
|
||||
* @param {function} callback
|
||||
*/
|
||||
function stop(callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
initialize: initialize,
|
||||
router: router,
|
||||
start: start,
|
||||
stop: stop
|
||||
};
|
||||
@@ -100,5 +100,11 @@
|
||||
"title": "ExecutionIndex",
|
||||
"panel": "panels/ExecutionIndex/ExecutionIndexPanel",
|
||||
"DEBUG_ONLY": false
|
||||
},
|
||||
{
|
||||
"id": "WorkerHeader",
|
||||
"title": "WorkerHeader",
|
||||
"panel": "panels/WorkerHeader/WorkerHeaderPanel",
|
||||
"DEBUG_ONLY": false
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// if the job is running, get the logs from the log-storage
|
||||
define([
|
||||
'q',
|
||||
'deepforge/JobLogsClient',
|
||||
'deepforge/api/JobLogsClient',
|
||||
'js/Constants',
|
||||
'deepforge/Constants',
|
||||
'panels/TextEditor/TextEditorControl'
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/* globals define, WebGMEGlobal */
|
||||
define([
|
||||
'js/Dialogs/Projects/ProjectsDialog',
|
||||
'./WorkerDialog',
|
||||
'js/Panels/Header/ProjectNavigatorController'
|
||||
], function(
|
||||
ProjectsDialog,
|
||||
WorkerDialog,
|
||||
GMEProjectNavigatorController
|
||||
) {
|
||||
'use strict';
|
||||
var ProjectNavigatorController = function() {
|
||||
GMEProjectNavigatorController.apply(this, arguments);
|
||||
};
|
||||
|
||||
ProjectNavigatorController.prototype = Object.create(GMEProjectNavigatorController.prototype);
|
||||
|
||||
ProjectNavigatorController.prototype.initialize = function () {
|
||||
var self = this,
|
||||
newProject,
|
||||
manageProjects,
|
||||
manageWorkers;
|
||||
|
||||
|
||||
// initialize model structure for view
|
||||
self.$scope.navigator = {
|
||||
items: [],
|
||||
separator: true
|
||||
};
|
||||
|
||||
|
||||
manageProjects = function (/*data*/) {
|
||||
var pd = new ProjectsDialog(self.gmeClient);
|
||||
pd.show();
|
||||
};
|
||||
newProject = function (data) {
|
||||
var pd = new ProjectsDialog(self.gmeClient, true, data.newType);
|
||||
pd.show();
|
||||
};
|
||||
self.userId = WebGMEGlobal.userInfo._id;
|
||||
|
||||
manageWorkers = function() {
|
||||
// Create the worker dialog
|
||||
var pd = new WorkerDialog(self.logger);
|
||||
pd.show();
|
||||
};
|
||||
|
||||
// initialize root menu
|
||||
// projects id is mandatory
|
||||
if (self.config.disableProjectActions === false) {
|
||||
self.root.menu = [
|
||||
{
|
||||
id: 'top',
|
||||
items: [
|
||||
{
|
||||
id: 'manageProject',
|
||||
label: 'Manage projects ...',
|
||||
iconClass: 'glyphicon glyphicon-folder-open',
|
||||
action: manageProjects,
|
||||
actionData: {}
|
||||
},
|
||||
{
|
||||
id: 'newProject',
|
||||
label: 'New project ...',
|
||||
disabled: WebGMEGlobal.userInfo.canCreate !== true,
|
||||
iconClass: 'glyphicon glyphicon-plus',
|
||||
action: newProject,
|
||||
actionData: {newType: 'seed'}
|
||||
},
|
||||
{
|
||||
id: 'importProject',
|
||||
label: 'Import project ...',
|
||||
disabled: WebGMEGlobal.userInfo.canCreate !== true,
|
||||
iconClass: 'glyphicon glyphicon-import',
|
||||
action: newProject,
|
||||
actionData: {newType: 'import'}
|
||||
},
|
||||
{
|
||||
id: 'manageWorkers',
|
||||
label: 'View workers ...',
|
||||
iconClass: 'glyphicon glyphicon-cloud',
|
||||
action: manageWorkers
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'projects',
|
||||
label: 'Recent projects',
|
||||
totalItems: 20,
|
||||
items: [],
|
||||
showAllItems: manageProjects
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
self.initWithClient();
|
||||
|
||||
// only root is selected by default
|
||||
self.$scope.navigator = {
|
||||
items: self.config.disableProjectActions ? [] : [self.root],
|
||||
separator: true
|
||||
};
|
||||
};
|
||||
|
||||
return ProjectNavigatorController;
|
||||
});
|
||||
@@ -0,0 +1,239 @@
|
||||
/* globals define, $ */
|
||||
define([
|
||||
'q',
|
||||
'superagent',
|
||||
'deepforge/viz/Utils',
|
||||
'deepforge/api/JobOriginClient',
|
||||
'text!./WorkerModal.html',
|
||||
'text!./WorkerTemplate.html.ejs',
|
||||
'text!./WorkerJobItem.html',
|
||||
'css!./WorkerModal.css'
|
||||
], function(
|
||||
Q,
|
||||
superagent,
|
||||
utils,
|
||||
JobOriginClient,
|
||||
WorkerHtml,
|
||||
WorkerTemplate,
|
||||
WorkerJobItem
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var WORKER_ENDPOINT = '/rest/executor/worker',
|
||||
JOBS_ENDPOINT = '/rest/executor';
|
||||
|
||||
var WorkerDialog = function(logger) {
|
||||
this.workerDict = {};
|
||||
this.workers = {};
|
||||
this.runningWorkers = [];
|
||||
this.jobsDict = {};
|
||||
this.jobs = {};
|
||||
this.active = false;
|
||||
this.logger = logger.fork('WorkerDialog');
|
||||
this.originManager = new JobOriginClient({
|
||||
logger: this.logger
|
||||
});
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.initialize = function() {
|
||||
this._dialog = $(WorkerHtml);
|
||||
this._table = this._dialog.find('.worker-list');
|
||||
this.$noJobs = this._dialog.find('.no-jobs-msg');
|
||||
this.$noWorkers = this._dialog.find('.no-workers-msg');
|
||||
this._isShowingJobs = false;
|
||||
this._isShowingWorkers = true;
|
||||
this._queue = this._dialog.find('.job-queue-list');
|
||||
this._dialog.modal('show');
|
||||
this._dialog.on('hidden.bs.modal', () => this.active = false);
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.show = function() {
|
||||
this.active = true;
|
||||
this.update();
|
||||
this.initialize();
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.get = function(url) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
superagent.get(url)
|
||||
.end((err, res) => {
|
||||
if (err) {
|
||||
return deferred.reject(err);
|
||||
}
|
||||
deferred.resolve(JSON.parse(res.text));
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.update = function() {
|
||||
// Poll the workers
|
||||
return Q.all([
|
||||
this.get(WORKER_ENDPOINT).then(workers => this.updateWorkers(workers)),
|
||||
this.get(JOBS_ENDPOINT).then(jobs => this.updateJobs(jobs))
|
||||
]).then(() => {
|
||||
if (this.active) {
|
||||
setTimeout(this.update.bind(this), 1000);
|
||||
}
|
||||
})
|
||||
.catch(err => this.logger.error('Update failed:', err));
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.updateWorkers = function(workerDict) {
|
||||
var ids = Object.keys(workerDict),
|
||||
oldWorkerIds,
|
||||
visibleWorkers = false,
|
||||
i;
|
||||
|
||||
this.runningWorkers = [];
|
||||
for (i = ids.length; i--;) {
|
||||
this.updateWorker(workerDict[ids[i]]);
|
||||
visibleWorkers = true;
|
||||
delete this.workerDict[ids[i]];
|
||||
}
|
||||
this.toggleNoWorkersMsg(!visibleWorkers);
|
||||
|
||||
// Clear old workers
|
||||
oldWorkerIds = Object.keys(this.workerDict);
|
||||
for (i = oldWorkerIds.length; i--;) {
|
||||
this.removeWorker(oldWorkerIds[i]);
|
||||
}
|
||||
|
||||
this.workerDict = workerDict;
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.updateWorker = function(worker) {
|
||||
var row = this.workers[worker.clientId] || $(WorkerTemplate),
|
||||
clazz;
|
||||
|
||||
worker.lastSeen = utils.getDisplayTime(worker.lastSeen*1000);
|
||||
worker.status = worker.jobs.length ? 'RUNNING' : 'READY';
|
||||
|
||||
clazz = worker.status === 'RUNNING' ? 'warning' : 'success';
|
||||
row[0].className = clazz;
|
||||
|
||||
row.find('.lastSeen').text(worker.lastSeen);
|
||||
row.find('.clientId').text(worker.clientId);
|
||||
row.find('.status').text(worker.status);
|
||||
if (!this.workers[worker.clientId]) {
|
||||
this._table.append(row);
|
||||
this.workers[worker.clientId] = row;
|
||||
}
|
||||
|
||||
if (worker.status === 'RUNNING') {
|
||||
this.runningWorkers.push(worker);
|
||||
}
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.removeWorker = function(workerId) {
|
||||
this.workers[workerId].remove();
|
||||
delete this.workers[workerId];
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.updateJobs = function(jobsDict) {
|
||||
var allJobIds = Object.keys(jobsDict),
|
||||
hasJobs = false,
|
||||
id;
|
||||
|
||||
this.jobsDict = jobsDict;
|
||||
for (var i = allJobIds.length; i--;) {
|
||||
id = allJobIds[i];
|
||||
if (this.jobs[id] || !this.isFinished(id)) {
|
||||
hasJobs = this.updateJobItem(id) || hasJobs;
|
||||
}
|
||||
}
|
||||
this.setNoJobsMessage(!hasJobs); // hide if no queue
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.setNoJobsMessage = function(visible) {
|
||||
var visibility = visible ? 'inherit' : 'none',
|
||||
wasVisible = !this._isShowingJobs;
|
||||
|
||||
if (visible !== wasVisible) {
|
||||
this.$noJobs.css('display', visibility);
|
||||
this._isShowingJobs = !visible;
|
||||
}
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.toggleNoWorkersMsg = function(visible) {
|
||||
var visibility = visible ? 'inherit' : 'none';
|
||||
|
||||
if (visible !== this._isShowingWorkers) {
|
||||
this.$noWorkers.css('display', visibility);
|
||||
this._isShowingWorkers = visible;
|
||||
}
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.isFinished = function(jobId) {
|
||||
return this.jobsDict[jobId].status === 'FAILED_TO_EXECUTE' ||
|
||||
this.jobsDict[jobId].status === 'SUCCESS' ||
|
||||
this.jobsDict[jobId].status === 'CANCELED';
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.updateJobItemName = function(jobId) {
|
||||
return this.originManager.getOrigin(jobId)
|
||||
.then(info => {
|
||||
var job = this.jobs[jobId],
|
||||
project = info.project.replace(/^guest\+/, '');
|
||||
|
||||
if (job && this.active) {
|
||||
if (info.branch !== 'master') {
|
||||
project += ' (' + info.branch + ')';
|
||||
}
|
||||
job.find('.job-id').text(info.job);
|
||||
job.find('.execution').text(info.execution);
|
||||
job.find('.project').text(project);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.getWorkerWithJob = function(jobId) {
|
||||
var jobs;
|
||||
|
||||
for (var i = this.runningWorkers.length; i--;) {
|
||||
jobs = this.runningWorkers[i].jobs;
|
||||
for (var j = jobs.length; j--;) {
|
||||
if (jobs[j].hash === jobId) {
|
||||
return this.runningWorkers[i].clientId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
};
|
||||
|
||||
WorkerDialog.prototype.updateJobItem = function(jobId) {
|
||||
var job = this.jobs[jobId] || $(WorkerJobItem),
|
||||
info = this.jobsDict[jobId],
|
||||
createdTime = new Date(info.createTime).getTime(),
|
||||
clazz = utils.ClassForJobStatus[info.status.toLowerCase()],
|
||||
status = info.status;
|
||||
|
||||
job[0].className = `job-tag ${clazz}`;
|
||||
|
||||
// Add the worker id if running
|
||||
if (info.status.toLowerCase() === 'running') {
|
||||
var workerId = this.getWorkerWithJob(jobId);
|
||||
status += ' (' + workerId + ')';
|
||||
}
|
||||
job.find('.status').text(status);
|
||||
|
||||
if (!this.jobs[jobId]) {
|
||||
job.find('.job-id').text('Loading');
|
||||
job.find('.createdAt').text(utils.getDisplayTime(createdTime));
|
||||
this.updateJobItemName(jobId);
|
||||
this._queue.append(job);
|
||||
this.jobs[jobId] = job;
|
||||
}
|
||||
|
||||
if (this.isFinished(jobId)) {
|
||||
job.remove();
|
||||
delete this.jobs[jobId];
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return WorkerDialog;
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
/*globals define, angular, _,*/
|
||||
/*jshint browser: true*/
|
||||
|
||||
define([
|
||||
'panels/BreadcrumbHeader/BreadcrumbHeaderPanel',
|
||||
'js/Widgets/UserProfile/UserProfileWidget',
|
||||
'js/Widgets/ConnectedUsers/ConnectedUsersWidget',
|
||||
'js/Panels/Header/DefaultToolbar',
|
||||
'panels/BreadcrumbHeader/NodePathNavigator',
|
||||
'js/Toolbar/Toolbar',
|
||||
'./ProjectNavigatorController'
|
||||
], function (
|
||||
BreadcrumbHeader,
|
||||
UserProfileWidget,
|
||||
ConnectedUsersWidget,
|
||||
DefaultToolbar,
|
||||
NodePathNavigator,
|
||||
Toolbar,
|
||||
ProjectNavigatorController
|
||||
) {
|
||||
'use strict';
|
||||
|
||||
var HeaderPanel;
|
||||
|
||||
HeaderPanel = function (layoutManager, params) {
|
||||
BreadcrumbHeader.call(this, layoutManager, params);
|
||||
};
|
||||
|
||||
//inherit from PanelBaseWithHeader
|
||||
_.extend(HeaderPanel.prototype, BreadcrumbHeader.prototype);
|
||||
|
||||
HeaderPanel.prototype._initialize = function () {
|
||||
BreadcrumbHeader.prototype._initialize.call(this);
|
||||
var app = angular.module('gmeApp');
|
||||
|
||||
app.controller('ProjectNavigatorController', ['$scope', 'gmeClient', '$timeout', '$window', '$http',
|
||||
ProjectNavigatorController]);
|
||||
};
|
||||
|
||||
return HeaderPanel;
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
<tr>
|
||||
<td class="job-id"></td>
|
||||
<td class="execution">unknown</td>
|
||||
<td class="project">unknown</td>
|
||||
<td class="createdAt">unknown</td>
|
||||
<td class="status">unknown</td>
|
||||
</tr>
|
||||
@@ -0,0 +1,47 @@
|
||||
.worker-modal .modal-content .modal-header span {
|
||||
font-size: 28px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.worker-modal .modal-content .modal-header .header-icon {
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
margin-right: 1ex;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
background-size: 28px 28px;
|
||||
}
|
||||
|
||||
.worker-modal .modal-content .modal-body th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.worker-modal .job-tag {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.worker-modal .queue-title {
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
font-size: 1.4em;
|
||||
color: #555555;
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.worker-modal .no-jobs-msg {
|
||||
font-style: italic;
|
||||
font-size: 1.3em;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.worker-modal .no-workers-msg {
|
||||
font-style: italic;
|
||||
font-size: 1.3em;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.worker-modal .job-queue {
|
||||
padding-top: 1em;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<div class="worker-modal modal fade in" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">x</button>
|
||||
<i class="header-icon gme-icon"></i>
|
||||
<span>Overview</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
<div class="queue-title">Connected Workers</div>
|
||||
<table class="table table-projects">
|
||||
<thead>
|
||||
<tr class="with-children">
|
||||
<th class="title-owner">Worker Id
|
||||
<!--<i class="glyphicon glyphicon-sort-by-attributes-alt sorted in-order"></i>
|
||||
<i class="glyphicon glyphicon-sort-by-attributes sorted rev-order"></i>-->
|
||||
</th>
|
||||
<th class="title-name">Last Seen
|
||||
<!--<i class="glyphicon glyphicon-sort-by-attributes-alt sorted in-order"></i>
|
||||
<i class="glyphicon glyphicon-sort-by-attributes sorted rev-order"></i>-->
|
||||
</th>
|
||||
<th class="title-modified">Status
|
||||
<!--<i class="glyphicon glyphicon-sort-by-attributes-alt sorted in-order"></i>
|
||||
<i class="glyphicon glyphicon-sort-by-attributes sorted rev-order"></i>-->
|
||||
</th>
|
||||
</tr>
|
||||
<!--<tr class="with-no-children">
|
||||
<th>No projects in this group...</th>
|
||||
</tr>-->
|
||||
</thead>
|
||||
<tbody class="worker-list">
|
||||
<tr><td class="no-workers-msg">No Connected Workers...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="job-queue">
|
||||
<div class="queue-title">Job Queue</div>
|
||||
<table class="table table-projects">
|
||||
<thead>
|
||||
<tr class="with-children">
|
||||
<th>Job</th>
|
||||
<th>Execution</th>
|
||||
<th>Project</th>
|
||||
<th>Creation Date</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
<!--<tr class="with-no-children">-->
|
||||
<!--<th>No queued jobs</th>-->
|
||||
<!--</tr>-->
|
||||
</thead>
|
||||
<tbody class="job-queue-list">
|
||||
<tr><td class="no-jobs-msg">No Running Jobs...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
<tr>
|
||||
<td class="clientId"></td>
|
||||
<td class="lastSeen">unknown</td>
|
||||
<td class="status">unknown</td>
|
||||
</tr>
|
||||
@@ -1,4 +1,4 @@
|
||||
var testFixture = require('../globals'),
|
||||
var testFixture = require('../../globals'),
|
||||
expect = testFixture.expect,
|
||||
assert = require('assert'),
|
||||
path = testFixture.path,
|
||||
@@ -7,7 +7,7 @@ var testFixture = require('../globals'),
|
||||
server = testFixture.WebGME.standaloneServer(gmeConfig),
|
||||
Logger = require('webgme/src/server/logger'),
|
||||
logger = Logger.createWithGmeConfig('gme', gmeConfig, true),
|
||||
JobLogsClient = testFixture.requirejs('deepforge/JobLogsClient'),
|
||||
JobLogsClient = testFixture.requirejs('deepforge/api/JobLogsClient'),
|
||||
rm_rf = require('rimraf'),
|
||||
exists = require('exists-file');
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
var testFixture = require('../../globals'),
|
||||
expect = testFixture.expect,
|
||||
gmeConfig = testFixture.getGmeConfig(),
|
||||
server = testFixture.WebGME.standaloneServer(gmeConfig),
|
||||
Logger = require('webgme/src/server/logger'),
|
||||
logger = Logger.createWithGmeConfig('gme', gmeConfig, true),
|
||||
JobOriginClient = testFixture.requirejs('deepforge/api/JobOriginClient');
|
||||
|
||||
describe('JobOriginClient', function() {
|
||||
var client = new JobOriginClient({
|
||||
logger: logger,
|
||||
origin: server.getUrl(),
|
||||
projectId: 'testProject',
|
||||
branchName: 'master'
|
||||
}),
|
||||
hashes = {},
|
||||
getJobInfo = function() {
|
||||
var hash = 'hashOrigin'+Math.ceil(Math.random()*100000);
|
||||
|
||||
while (hashes[hash]) {
|
||||
hash = 'hashOrigin'+Math.ceil(Math.random()*100000);
|
||||
}
|
||||
hashes[hash] = true;
|
||||
|
||||
return {
|
||||
hash: hash,
|
||||
job: 'SomeJob',
|
||||
execution: 'train_execution',
|
||||
nodeId: 'K/6/1'
|
||||
};
|
||||
};
|
||||
|
||||
before(function(done) {
|
||||
server.start(done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
server.stop(done);
|
||||
});
|
||||
|
||||
it('should store job info', function(done) {
|
||||
var job = getJobInfo();
|
||||
|
||||
client.record(job.hash, job)
|
||||
.nodeify(done);
|
||||
});
|
||||
|
||||
it('should read job info', function(done) {
|
||||
var job = getJobInfo();
|
||||
|
||||
client.record(job.hash, job)
|
||||
.then(() => client.getOrigin(job.hash))
|
||||
.then(jobInfo => {
|
||||
Object.keys(job).forEach(key => {
|
||||
expect(jobInfo[key]).equal(job[key]);
|
||||
});
|
||||
})
|
||||
.nodeify(done);
|
||||
});
|
||||
|
||||
it('should delete job info', function(done) {
|
||||
var job = getJobInfo();
|
||||
|
||||
client.record(job.hash, job)
|
||||
.then(() => client.deleteRecord(job.hash))
|
||||
.then(() => client.getOrigin(job.hash))
|
||||
.then(res => expect(res).equal(null))
|
||||
.nodeify(done);
|
||||
});
|
||||
|
||||
it('should update job branch on fork', function(done) {
|
||||
var job = getJobInfo(),
|
||||
newBranch = 'newBranch';
|
||||
|
||||
client.record(job.hash, job)
|
||||
.then(() => client.fork(job.hash, newBranch))
|
||||
.then(() => client.getOrigin(job.hash))
|
||||
.then(res => expect(res.branch).equal(newBranch))
|
||||
.nodeify(done);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
var testFixture = require('../globals'),
|
||||
var testFixture = require('../../globals'),
|
||||
superagent = testFixture.superagent,
|
||||
Q = testFixture.Q,
|
||||
expect = testFixture.expect,
|
||||
@@ -0,0 +1,115 @@
|
||||
var testFixture = require('../../globals'),
|
||||
superagent = testFixture.superagent,
|
||||
expect = testFixture.expect,
|
||||
gmeConfig = testFixture.getGmeConfig(),
|
||||
server = testFixture.WebGME.standaloneServer(gmeConfig),
|
||||
mntPt = 'job/origins';
|
||||
|
||||
describe('JobOriginAPI', function() {
|
||||
var hashes = {},
|
||||
getUrl = function(hash) {
|
||||
return [
|
||||
server.getUrl(),
|
||||
mntPt,
|
||||
hash
|
||||
].join('/');
|
||||
},
|
||||
getJobInfo = function() {
|
||||
var hash = 'hash'+Math.ceil(Math.random()*100000);
|
||||
|
||||
while (hashes[hash]) {
|
||||
hash = 'hash'+Math.ceil(Math.random()*100000);
|
||||
}
|
||||
hashes[hash] = true;
|
||||
|
||||
return {
|
||||
hash: hash,
|
||||
job: 'SomeJob',
|
||||
branch: 'master',
|
||||
execution: 'train_execution',
|
||||
project: 'guest+example',
|
||||
nodeId: 'K/6/1'
|
||||
};
|
||||
};
|
||||
|
||||
before(function(done) {
|
||||
server.start(done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
server.stop(done);
|
||||
});
|
||||
|
||||
it('should store job info', function(done) {
|
||||
var job = getJobInfo();
|
||||
|
||||
superagent.post(getUrl(job.hash))
|
||||
.send(job)
|
||||
.end(function (err, res) {
|
||||
expect(res.status).equal(201, err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should read job info', function(done) {
|
||||
var job = getJobInfo(),
|
||||
url = getUrl(job.hash);
|
||||
|
||||
superagent.post(url)
|
||||
.send(job)
|
||||
.end(function (err, res) {
|
||||
expect(res.status).equal(201, err);
|
||||
superagent.get(url)
|
||||
.end((err, res) => {
|
||||
var jobInfo = JSON.parse(res.text);
|
||||
Object.keys(jobInfo).forEach(key => {
|
||||
expect(jobInfo[key]).equal(job[key]);
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete job info', function(done) {
|
||||
var job = getJobInfo(),
|
||||
url = getUrl(job.hash);
|
||||
|
||||
superagent.post(url)
|
||||
.send(job)
|
||||
.end(function (err, res) {
|
||||
expect(res.status).equal(201, err);
|
||||
superagent.delete(url).end(err => {
|
||||
expect(err).equal(null);
|
||||
superagent.get(url)
|
||||
.end((err, res) => {
|
||||
expect(res.status).equal(404, err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should update job branch', function(done) {
|
||||
var job = getJobInfo(),
|
||||
url = getUrl(job.hash);
|
||||
|
||||
superagent.post(url) // create the job
|
||||
.send(job)
|
||||
.end(function (err, res) {
|
||||
expect(res.status).equal(201, err);
|
||||
superagent.patch(url) // update the branch
|
||||
.send({branch: 'newBranch'})
|
||||
.end(err => {
|
||||
expect(err).equal(null);
|
||||
|
||||
superagent.get(url) // check the new version
|
||||
.end((err, res) => {
|
||||
var info = JSON.parse(res.text);
|
||||
expect(info.branch).equal('newBranch');
|
||||
expect(res.status).equal(200, err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -230,6 +230,13 @@
|
||||
"panel": "src/visualizers/panels/ExecutionIndex",
|
||||
"secondary": false,
|
||||
"widget": "src/visualizers/widgets/ExecutionIndex"
|
||||
},
|
||||
"WorkerHeader": {
|
||||
"src": "panels/WorkerHeader/WorkerHeaderPanel",
|
||||
"title": "WorkerHeader",
|
||||
"panel": "src/visualizers/panels/WorkerHeader",
|
||||
"secondary": false,
|
||||
"widget": "src/visualizers/widgets/WorkerHeader"
|
||||
}
|
||||
},
|
||||
"addons": {},
|
||||
@@ -289,6 +296,10 @@
|
||||
"JobLogsAPI": {
|
||||
"src": "src/routers/JobLogsAPI",
|
||||
"mount": "execution/logs"
|
||||
},
|
||||
"JobOriginAPI": {
|
||||
"src": "src/routers/JobOriginAPI",
|
||||
"mount": "job/origins"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário