Comparar commits

...

10 Commits

Autor SHA1 Mensagem Data
Brian Broll 3676d4c340 WIP Added istanbul 2016-10-02 09:04:17 -05:00
Brian Broll 16668468f4 Added boolean type check for attributes. Fixes #832 (#834) 2016-09-28 21:31:51 -05:00
Brian Broll ffae88a168 Set the exec status to 'running' on job restart. Fixes #831 (#833) 2016-09-28 21:27:45 -05:00
Brian Broll 25f5759c17 Cancel job if the exec has been canceled. Fixes #820 (#830)
* Cancel job if the exec has been canceled. Fixes #820

* WIP #820 Added test for stopping watching on cancel

* WIP #820 Increased the timeout for import torch
2016-09-27 19:28:57 -05:00
Brian Broll 7a0eae386f Added run.sh script to execution files. Fixes #818 (#829)
* WIP Added basic run.sh script creation

* WIP #818. Added baseURL... still needs work

* WIP #818 Added prompt for DEEPFORGE_URL and fixed wget issue
2016-09-27 15:01:51 -05:00
Brian Broll af5d74483a Added read-only tier of changes being applied. Fixes #826 (#827)
* Added read-only tier of changes being applied. Fixes #826

WIP #826 Updated currentChanges name. Added debugging

WIP #826 Added get/setAttr test for ExecJob

WIP #826 Added get/set attr tests

WIP #826 Added logs to try to figure out what on earth is going wrong

WIP #826 Fixed typo in test

WIP #826 Removed debugging logging

WIP #826 Enabled all tests

* WIP #826 Fixed code climate issue
2016-09-26 19:18:20 -05:00
Brian Broll 9bf7632aba Update README.md 2016-09-24 15:24:43 -05:00
Brian Broll d7f3544bb3 v0.16.0 2016-09-19 17:48:51 -05:00
Brian Broll 19a7b2a8fa Apply node creation/deletion before save. Fixes #824 (#825)
* WIP #824 Added extra logs

* WIP #824 Moved creation events to the local diff strategy

* WIP #824 Added deletion event to the local stored changes to apply

* WIP #824 Fixed variable name

* WIP #824 Fixed apply dependent creation changes

* WIP #824 Update local operation setAttr, getAttr

* WIP #824 Updated execpipeline save method

* WIP #824 Fixed code climate issue

I will probably remove all ArtifactFinder fn-ality

* WIP #824 Removed unused var

* WIP #824 update the meta on fast forward
2016-09-17 14:40:09 -05:00
Brian Broll aadd581189 Fixed regex for plotting negative numbers. Fixes #822 (#823) 2016-09-15 10:26:03 -05:00
11 arquivos alterados com 665 adições e 93 exclusões
+7 -1
Ver Arquivo
@@ -7,7 +7,13 @@
**Notice**: DeepForge is still a work in progress and is also lacking significant documentation! That being said, any contributions and/or feedback is greatly appreciated (and feel free to always ask any questions on the gitter)!
# DeepForge
DeepForge is an open-source visual development environment for deep learning. Currently, it supports Convolutional Neural Networks but we are planning on supporting additional deep learning classifiers such as RNNs and LSTMs. Additional features include real-time collaborative editing and version control.
DeepForge is an open-source visual development environment for deep learning. Currently, it supports Convolutional Neural Networks, RNNs and LSTMs as well as the creation of custom layers. Additional features include:
- Graphical architecture editor
- Training/testing pipeline creation
- Distributed pipeline execution
- Real-time pipeline feedback
- Collaborative editing
- Automatic version control.
## Quick Start
Simply run the following command to install deepforge with its dependencies:
+1
Ver Arquivo
@@ -20,6 +20,7 @@ config.seedProjects.basePaths.push(__dirname + '/../src/seeds/devPipelineTests')
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/project');
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/cifar10');
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/xor');
config.seedProjects.basePaths.push(__dirname + '/../src/seeds/devProject');
+3 -1
Ver Arquivo
@@ -9,10 +9,11 @@
"local": "node ./bin/start-local.js",
"worker": "node ./bin/start-worker.js",
"test": "mkdir ./test-tmp; node ./node_modules/mocha/bin/mocha --recursive test",
"test-cover": "node node_modules/istanbul/lib/cli.js --hook-run-in-context cover node_modules/mocha/bin/_mocha -- -R spec test/**/*",
"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.15.0",
"version": "0.16.0",
"dependencies": {
"commander": "^2.9.0",
"dotenv": "^2.0.0",
@@ -35,6 +36,7 @@
"chai": "^3.0.0",
"jszip": "^2.5.0",
"mocha": "^2.2.5",
"istanbul": "^0.4.5",
"mockery": "^1.7.0"
}
}
+14 -19
Ver Arquivo
@@ -20,13 +20,13 @@ define([
var output = cntrs
.find(cntr => {
var metaNode = this.core.getMetaType(cntr),
metaName = this.core.getAttribute(metaNode, 'name');
metaName = this.getAttribute(metaNode, 'name');
return metaName === 'Outputs';
});
return this.core.loadChildren(output);
})
.then(dataNodes => {
hash = this.core.getAttribute(dataNodes[0], 'data');
hash = this.getAttribute(dataNodes[0], 'data');
return this.getOutputs(node);
})
.then(outputTuples => {
@@ -48,7 +48,7 @@ define([
var hash,
typeId = this.core.getPointerPath(node, 'type'),
type,
artifactName = this.core.getAttribute(node, 'artifactName');
artifactName = this.getAttribute(node, 'artifactName');
return this.core.loadByPath(this.rootNode, typeId)
.then(_type => {
@@ -58,25 +58,20 @@ define([
.then(saveDir => this.core.loadChildren(saveDir))
.then(artifacts => {
return artifacts.find(artifact =>
this.core.getAttribute(artifact, 'name') === artifactName &&
this.getAttribute(artifact, 'name') === artifactName &&
this.isMetaTypeOf(artifact, type));
})
.then(matchingArtifact => {
hash = matchingArtifact && this.core.getAttribute(matchingArtifact, 'data');
hash = matchingArtifact && this.getAttribute(matchingArtifact, 'data');
// If no hash, just continue (the subsequent ops will receive 'nil')
if (!hash) {
return this.onOperationComplete(node);
} else {
return this.getOutputs(node)
.then(outputPairs => {
var outputs = outputPairs.map(pair => pair[2]),
paths;
paths = outputs.map(output => this.core.getPath(output));
var outputs = outputPairs.map(pair => pair[2]);
// Get the 'data' hash and store it in the output data ports
this.logger.info(`Loading blob data (${hash}) to ${paths.map(p => `"${p}"`)}`);
outputs.forEach(output => this.core.setAttribute(output, 'data', hash));
outputs.forEach(output => this.setAttribute(output, 'data', hash));
this.onOperationComplete(node);
});
@@ -99,7 +94,7 @@ define([
if (containers.length > 1) {
saveDir = containers.find(c =>
this.core.getAttribute(c, 'name').toLowerCase().indexOf('artifacts') > -1
this.getAttribute(c, 'name').toLowerCase().indexOf('artifacts') > -1
) || containers[0];
}
@@ -121,8 +116,8 @@ define([
.then(artifacts => {
currNameHashPairs = artifacts
.map(node => [
this.core.getAttribute(node, 'name'),
this.core.getAttribute(node, 'data')
this.getAttribute(node, 'name'),
this.getAttribute(node, 'data')
]);
return this.getInputs(node);
})
@@ -142,9 +137,9 @@ define([
// Remove nodes that already exist
dataNodes = allDataNodes.filter(dataNode => {
var hash = this.core.getAttribute(dataNode, 'data'),
var hash = this.getAttribute(dataNode, 'data'),
name = this.core.getOwnAttribute(node, 'saveName') ||
this.core.getAttribute(dataNode, 'name');
this.getAttribute(dataNode, 'name');
return !(currNameHashPairs
.find(pair => pair[0] === name && pair[1] === hash));
@@ -156,10 +151,10 @@ define([
newName = this.core.getOwnAttribute(node, 'saveName');
if (newName) {
newNodes.forEach(node =>
this.core.setAttribute(node, 'name', newName)
this.setAttribute(node, 'name', newName)
);
}
var hashes = dataNodes.map(n => this.core.getAttribute(n, 'data'));
var hashes = dataNodes.map(n => this.getAttribute(n, 'data'));
this.logger.info(`saving hashes: ${hashes.map(h => `"${h}"`)}`);
} else if (allDataNodes.length === 0) {
this.logger.warn('No data nodes found!');
+340 -64
Ver Arquivo
@@ -2,6 +2,7 @@
/*jshint node:true, browser:true*/
define([
'common/util/assert',
'common/storage/constants',
'text!./metadata.json',
'executor/ExecutorClient',
@@ -15,6 +16,7 @@ define([
'q',
'underscore'
], function (
assert,
STORAGE_CONSTANTS,
pluginMetadata,
ExecutorClient,
@@ -33,7 +35,9 @@ define([
pluginMetadata = JSON.parse(pluginMetadata);
var OUTPUT_INTERVAL = 1500,
STDOUT_FILE = 'job_stdout.txt';
STDOUT_FILE = 'job_stdout.txt',
CREATE_PREFIX = 'created_node_',
INDEX = 1;
/**
* Initializes a new instance of ExecuteJob.
@@ -53,7 +57,12 @@ define([
this._oldMetadataByName = {}; // name -> id
this.lastAppliedCmd = {};
this.canceled = false;
this.changes = {};
this.currentChanges = {}; // read-only changes being applied
this.creations = {};
this.deletions = [];
this.createIdToMetadataId = {};
this.logManager = null;
};
@@ -69,6 +78,18 @@ define([
ExecuteJob.prototype = Object.create(PluginBase.prototype);
ExecuteJob.prototype.constructor = ExecuteJob;
ExecuteJob.prototype.configure = function () {
var result = PluginBase.prototype.configure.apply(this, arguments);
this.logManager = new JobLogsClient({
logger: this.logger,
port: this.gmeConfig.server.port,
branchName: this.branchName,
projectId: this.projectId
});
return result;
};
/**
* Main function for the plugin to execute. This will perform the execution.
* Notes:
@@ -81,19 +102,21 @@ define([
ExecuteJob.prototype.main = function (callback) {
// Check the activeNode to make sure it is a valid node
var type = this.core.getMetaType(this.activeNode),
typeName = type && this.getAttribute(type, 'name');
typeName = type && this.getAttribute(type, 'name'),
execNode,
status;
if (typeName !== 'Job') {
return callback(`Cannot execute ${typeName} (expected Job)`, this.result);
}
// Get the gmeConfig...
this.logManager = new JobLogsClient({
logger: this.logger,
port: this.gmeConfig.server.port,
branchName: this.branchName,
projectId: this.projectId
});
// Set the parent execution to 'running'
execNode = this.core.getParent(this.activeNode);
status = this.getAttribute(execNode, 'status');
if (status !== 'running') {
this.setAttribute(execNode, 'status', 'running');
}
this._callback = callback;
this.currentForkName = null;
this.prepare()
@@ -117,12 +140,51 @@ define([
});
};
//////////////////////////// Safe Save ////////////////////////////
ExecuteJob.prototype.getCreateId = function () {
return CREATE_PREFIX + (++INDEX);
};
ExecuteJob.prototype.isCreateId = function (id) {
return (typeof id === 'string') && (id.indexOf(CREATE_PREFIX) === 0);
};
ExecuteJob.prototype.createNode = function (baseType, parent) {
var id = this.getCreateId(),
parentId = this.isCreateId(parent) ? parent : this.core.getPath(parent);
this.logger.info(`Creating ${id} of type ${baseType} in ${parentId}`);
assert(this.META[baseType], `Cannot create node w/ unrecognized type: ${baseType}`);
this.creations[id] = {
base: baseType,
parent: parentId
};
return id;
};
ExecuteJob.prototype.deleteNode = function (nodeId) {
this.deletions.push(nodeId);
};
ExecuteJob.prototype.delAttribute = function (node, attr) {
return this.setAttribute(node, attr, null);
};
ExecuteJob.prototype.setAttribute = function (node, attr, value) {
var nodeId = this.core.getPath(node);
var nodeId;
if (this.isCreateId(node)) {
nodeId = node;
} else {
nodeId = this.core.getPath(node);
assert(typeof nodeId === 'string', `Cannot set attribute of ${nodeId}`);
}
if (value !== null) {
this.logger.info(`Setting ${attr} of ${nodeId} to ${value}`);
} else {
this.logger.info(`Deleting ${attr} of ${nodeId}`);
}
if (!this.changes[nodeId]) {
this.changes[nodeId] = {};
@@ -131,32 +193,53 @@ define([
};
ExecuteJob.prototype.getAttribute = function (node, attr) {
var nodeId = this.core.getPath(node),
base,
baseId;
var nodeId;
// Check the changes; fallback on actual node
if (this.changes[nodeId] && this.changes[nodeId][attr] !== undefined) {
// If deleted the attribute, get the default (inherited) value
if (this.changes[nodeId][attr] === null) {
base = this.core.getBase(node);
baseId = this.core.getPath(base);
return this.getAttribute(baseId, attr);
}
return this.changes[nodeId][attr];
assert(this.deletions.indexOf(nodeId) === -1,
`Cannot get ${attr} from deleted node ${nodeId}`);
// Check if it was newly created
if (this.isCreateId(node)) {
nodeId = node;
assert(this.creations[nodeId], `Creation node not updated: ${nodeId}`);
node = this.META[this.creations[nodeId].base];
} else {
nodeId = this.core.getPath(node);
}
// Check the most recent changes, then the currentChanges, then the model
var value = this._getValueFrom(nodeId, attr, node, this.changes) ||
this._getValueFrom(nodeId, attr, node, this.currentChanges);
if (value) {
return value;
}
return this.core.getAttribute(node, attr);
};
ExecuteJob.prototype._getValueFrom = function (nodeId, attr, node, changes) {
var base;
if (changes[nodeId] && changes[nodeId][attr] !== undefined) {
// If deleted the attribute, get the default (inherited) value
if (changes[nodeId][attr] === null) {
base = this.isCreateId(nodeId) ? node : this.core.getBase(node);
return this.getAttribute(base, attr);
}
return changes[nodeId][attr];
}
};
ExecuteJob.prototype._applyNodeChanges = function (node, changes) {
var attr,
value;
this.logger.info(`About to apply changes for ${this.core.getPath(node)}`);
for (var i = changes.length; i--;) {
attr = changes[i][0];
value = changes[i][1];
if (value !== null) {
this.logger.info(`Setting ${attr} to ${value} (${this.core.getPath(node)})`);
this.core.setAttribute(node, attr, value);
} else {
this.core.delAttribute(node, attr);
@@ -165,6 +248,12 @@ define([
return node;
};
ExecuteJob.prototype.applyModelChanges = function () {
return this.applyCreations()
.then(() => this.applyChanges())
.then(() => this.applyDeletions());
};
ExecuteJob.prototype.applyChanges = function () {
var nodeIds = Object.keys(this.changes),
attrs,
@@ -175,6 +264,8 @@ define([
id,
promise;
this.logger.info('Collecting changes to apply in commit');
for (var i = nodeIds.length; i--;) {
changes = [];
attrs = Object.keys(this.changes[nodeIds[i]]);
@@ -183,27 +274,165 @@ define([
changes.push([attrs[a], value]);
}
changesFor[nodeIds[i]] = changes;
assert(changes, `changes are invalid for ${nodeIds[i]}: ${changes}`);
assert(!this.isCreateId(nodeIds[i]),
`Creation id not resolved to actual id: ${nodeIds[i]}`);
promise = this.core.loadByPath(this.rootNode, nodeIds[i]);
promises.push(promise);
}
this.currentChanges = this.changes;
this.changes = {};
// Need to differentiate between read/write changes.
this.logger.info(`About to apply changes for ${promises.length} nodes`);
return Q.all(promises)
.then(nodes => {
for (var i = nodes.length; i--;) {
id = this.core.getPath(nodes[i]);
assert(nodes[i], `node is ${nodes[i]} (${nodeIds[i]})`);
this._applyNodeChanges(nodes[i], changesFor[id]);
}
// Local model is now up-to-date. No longer need currentChanges
this.currentChanges = {};
});
};
ExecuteJob.prototype.applyCreations = function () {
var nodeIds = Object.keys(this.creations),
tiers = this.createCreationTiers(nodeIds),
creations = this.creations,
newIds = {},
promise = Q(),
tier;
this.logger.info('Applying node creations');
for (var i = 0; i < tiers.length; i++) {
tier = tiers[i];
// Chain the promises, loading each tier sequentially
promise = promise.then(this.applyCreationTier.bind(this, creations, newIds, tier));
}
this.creations = {};
return promise;
};
ExecuteJob.prototype.applyCreationTier = function (creations, newIds, tier) {
var promises = [],
parentId,
node;
for (var j = tier.length; j--;) {
node = creations[tier[j]];
assert(node, `Could not find create info for ${tier[j]}`);
parentId = newIds[node.parent] || node.parent;
promises.push(this.applyCreation(tier[j], node.base, parentId));
}
return Q.all(promises).then(nodes => {
for (var i = nodes.length; i--;) {
id = this.core.getPath(nodes[i]);
this._applyNodeChanges(nodes[i], changesFor[id]);
// Record the newIds so they can be used to resolve creation ids
// in subsequent tiers
for (var i = tier.length; i--;) {
newIds[tier[i]] = this.core.getPath(nodes[i]);
}
});
};
// Figure out the dependencies between nodes to create.
// eg, if newId1 is to be created in newId2, then newId2 will
// be in an earlier tier than newId1. Essentially a topo-sort
// on a tree structure
ExecuteJob.prototype.createCreationTiers = function (nodeIds) {
var tiers = [],
prevTier = {},
tier = {},
id,
prevLen,
i;
// Create first tier (created inside existing nodes)
for (i = nodeIds.length; i--;) {
id = nodeIds[i];
if (!this.isCreateId(this.creations[id].parent)) {
tier[id] = true;
nodeIds.splice(i, 1);
}
}
prevTier = tier;
tiers.push(Object.keys(tier));
// Now, each tier consists of the nodes to be created inside a
// node from the previous tier
while (nodeIds.length) {
prevLen = nodeIds.length;
tier = {};
for (i = nodeIds.length; i--;) {
id = nodeIds[i];
if (prevTier[this.creations[id].parent]) {
tier[id] = true;
nodeIds.splice(i, 1);
}
}
prevTier = tier;
tiers.push(Object.keys(tier));
// Every iteration should find at least one node
assert(prevLen > nodeIds.length,
`Created empty create tier! Remaining: ${nodeIds.join(', ')}`);
}
return tiers;
};
ExecuteJob.prototype.applyCreation = function (tmpId, baseType, parentId) {
var base = this.META[baseType],
nodeId,
id;
this.logger.info(`Applying creation of ${tmpId} (${baseType}) in ${parentId}`);
assert(!this.isCreateId(parentId),
`Did not resolve parent id: ${parentId} for ${tmpId}`);
assert(base, `Invalid base type: ${baseType}`);
return this.core.loadByPath(this.rootNode, parentId)
.then(parent => this.core.createNode({base, parent}))
.then(node => { // Update the _metadata records
id = this.createIdToMetadataId[tmpId];
delete this.createIdToMetadataId[tmpId];
this._metadata[id] = node;
// Update creations
nodeId = this.core.getPath(node);
if (this.changes[tmpId]) {
assert(!this.changes[nodeId],
`Newly created node cannot already have changes! (${nodeId})`);
this.changes[nodeId] = this.changes[tmpId];
delete this.changes[tmpId];
}
return node;
});
};
ExecuteJob.prototype.applyDeletions = function () {
var deletions = this.deletions;
this.deletions = [];
return Q.all(deletions.map(id => this.core.loadByPath(this.rootNode, id)))
.then(nodes => {
for (var i = nodes.length; i--;) {
this.core.deleteNode(nodes[i]);
}
});
};
// Override 'save' to notify the user on fork
ExecuteJob.prototype.save = function (msg) {
var name = this.getAttribute(this.activeNode, 'name');
return this.updateForkName(name)
.then(() => this.applyChanges())
.then(() => this.applyModelChanges())
.then(() => PluginBase.prototype.save.call(this, msg))
.then(result => {
var msg;
this.logger.info(`Save finished w/ status: ${result.status}`);
if (result.status === STORAGE_CONSTANTS.FORKED) {
msg = `"${name}" execution has forked to "${result.forkName}"`;
this.currentForkName = result.forkName;
@@ -228,9 +457,37 @@ define([
this.rootNode = rootObject;
return this.core.loadByPath(rootObject,activeId);
})
.then(activeObject => this.activeNode = activeObject);
.then(activeObject => this.activeNode = activeObject)
.then(() => {
var metaNames = Object.keys(this.META);
return Q.all(metaNames.map(name => this.updateMetaNode(name)));
})
.then(() => {
var mdNodes,
mdIds;
mdIds = Object.keys(this._metadata)
.filter(id => !this.isCreateId(this._metadata[id]));
mdNodes = mdIds.map(id => this.core.getPath(this._metadata[id]))
.map(nodeId => this.core.loadByPath(this.rootNode, nodeId));
return Q.all(mdNodes).then(nodes => {
for (var i = nodes.length; i--;) {
this._metadata[mdIds[i]] = nodes[i];
}
});
});
};
ExecuteJob.prototype.updateMetaNode = function (name) {
var id = this.core.getPath(this.META[name]);
return this.core.loadByPath(this.rootNode, id).then(node => this.META[name] = node);
};
//////////////////////////// END Safe Save ////////////////////////////
ExecuteJob.prototype.getConnections = function (nodes) {
var conns = [];
for (var i = nodes.length; i--;) {
@@ -279,7 +536,8 @@ define([
idsToDelete = [],
type,
base,
child;
child,
i;
this.lastAppliedCmd[nodeId] = 0;
this._oldMetadataByName[nodeId] = {};
@@ -287,7 +545,7 @@ define([
return this.core.loadChildren(job)
.then(jobChildren => {
// Remove any metadata nodes
for (var i = jobChildren.length; i--;) {
for (i = jobChildren.length; i--;) {
child = jobChildren[i];
if (this.isMetaTypeOf(child, this.META.Metadata)) {
id = this.core.getPath(child);
@@ -310,18 +568,22 @@ define([
}
// make the deletion ids relative to the job node
idsToDelete = idsToDelete.map(id => id.replace(nodeId, ''));
return Q.all(idsToDelete.map(id => this.core.loadByPath(job, id)));
})
.then(nodes => nodes.forEach(node => this.core.deleteNode(node)));
this.logger.debug(`About to delete ${idsToDelete.length}: ${idsToDelete.join(', ')}`);
for (i = idsToDelete.length; i--;) {
this.deleteNode(idsToDelete[i]);
}
});
};
ExecuteJob.prototype.clearOldMetadata = function (job) {
var nodeId = this.core.getPath(job),
nodeIds = Object.keys(this._markForDeletion[nodeId]);
nodeIds = Object.keys(this._markForDeletion[nodeId]),
node;
this.logger.debug(`About to delete ${nodeIds.length}: ${nodeIds.join(', ')}`);
for (var i = nodeIds.length; i--;) {
this.core.deleteNode(this._markForDeletion[nodeId][nodeIds[i]]);
node = this._markForDeletion[nodeId][nodeIds[i]];
this.deleteNode(this.core.getPath(node));
}
delete this.lastAppliedCmd[nodeId];
delete this._markForDeletion[nodeId];
@@ -472,20 +734,32 @@ define([
})
.then(mds => {
// Record the large files
var inputData = {};
var inputData = {},
runsh = '# Bash script to download data files and run job\n' +
'if [ -z "$DEEPFORGE_URL" ]; then\n echo "Please set DEEPFORGE_URL and' +
' re-run:"\n echo "" \n echo " DEEPFORGE_URL=http://my.' +
'deepforge.server.com:8080 bash run.sh"\n echo ""\n exit 1\nfi\n';
mds.forEach((metadata, i) => {
// add the hashes for each input
var input = inputs[i],
hash = files.inputAssets[input];
hash = files.inputAssets[input],
dataPath = 'inputs/' + input + '/data',
url = this.blobClient.getRelativeDownloadURL(hash);
inputData['inputs/' + input + '/data'] = {
inputData[dataPath] = {
req: hash,
cache: metadata.content
};
// Add to the run.sh file
runsh += `wget $DEEPFORGE_URL${url} -O ${dataPath}\n`;
});
delete files.inputAssets;
files['input-data.json'] = JSON.stringify(inputData, null, 2);
runsh += 'th init.lua';
files['run.sh'] = runsh;
// Add pointer assets
Object.keys(files.ptrAssets)
@@ -865,8 +1139,8 @@ define([
};
ExecuteJob.prototype.createAttributeFile = function (node, files) {
var skip = ['code'],
numRegex = /^\d+\.?\d*((e|e-)\d+)?$/,
var skip = ['code', 'stdout', 'execFiles', 'jobId', 'secret'],
numOrBool = /^(-?\d+\.?\d*((e|e-)\d+)?|(true|false))$/,
table;
this.logger.info('Creating attributes file...');
@@ -874,7 +1148,7 @@ define([
.filter(attr => skip.indexOf(attr) === -1)
.map(name => {
var value = this.getAttribute(node, name);
if (!numRegex.test(value)) {
if (!numOrBool.test(value)) {
value = `"${value}"`;
}
return [name, value];
@@ -920,6 +1194,11 @@ define([
});
};
ExecuteJob.prototype.isExecutionCanceled = function () {
var execNode = this.core.getParent(this.activeNode);
return this.getAttribute(execNode, 'status') === 'canceled';
};
ExecuteJob.prototype.watchOperation = function (executor, hash, op, job) {
var jobId = this.core.getPath(job),
opId = this.core.getPath(op),
@@ -928,7 +1207,7 @@ define([
name = this.getAttribute(job, 'name');
// If canceled, stop the operation
if (this.canceled) {
if (this.canceled || this.isExecutionCanceled()) {
secret = this.getAttribute(job, 'secret');
if (secret) {
executor.cancelJob(hash, secret);
@@ -954,6 +1233,7 @@ define([
last = stdout.lastIndexOf('\n'),
result,
lastLine,
next = Q(),
msg;
// parse deepforge commands
@@ -967,13 +1247,15 @@ define([
if (output) {
// Send notification to all clients watching the branch
this.logManager.appendTo(jobId, output)
next = next
.then(() => this.logManager.appendTo(jobId, output))
.then(() => this.notifyStdoutUpdate(jobId));
}
if (result.hasMetadata) {
msg = `Updated graph/image output for ${name}`;
return this.save(msg);
next = next.then(() => this.save(msg));
}
return next;
});
}
})
@@ -1186,14 +1468,12 @@ define([
// Check if the graph already exists
graph = this._getExistingMetadata(jobId, 'Graph', name);
if (!graph) {
graph = this.core.createNode({
base: this.META.Graph,
parent: job
});
graph = this.createNode('Graph', job);
if (name) {
this.setAttribute(graph, 'name', name);
}
this.createIdToMetadataId[graph] = id;
}
this._metadata[id] = graph;
@@ -1201,25 +1481,25 @@ define([
ExecuteJob.prototype[CONSTANTS.GRAPH_PLOT] = function (job, id, x, y) {
var jobId = this.core.getPath(job),
nonNum = /[^\d\.]*/g,
graph,
nonNum = /[^\d-\.]*/g,
line,
points;
id = jobId + '/' + id;
this.logger.info(`Adding point ${x}, ${y} to ${id}`);
graph = this._metadata[id];
if (!graph) {
this.logger.warn(`Can't add point to non-existent graph: ${id}`);
line = this._metadata[id];
if (!line) {
this.logger.warn(`Can't add point to non-existent line: ${id}`);
return;
}
// Clean the points by removing and special characters
x = x.replace(nonNum, '');
y = y.replace(nonNum, '');
points = this.getAttribute(graph, 'points');
points = this.getAttribute(line, 'points');
points += `${x},${y};`;
this.setAttribute(graph, 'points', points);
this.setAttribute(line, 'points', points);
};
ExecuteJob.prototype[CONSTANTS.GRAPH_CREATE_LINE] = function (job, graphId, id) {
@@ -1230,12 +1510,10 @@ define([
// Create a 'line' node in the given Graph metadata node
name = name.replace(/\s+$/, '');
line = this.core.createNode({
base: this.META.Line,
parent: graph
});
line = this.createNode('Line', graph);
this.setAttribute(line, 'name', name);
this._metadata[jobId + '/' + id] = line;
this.createIdToMetadataId[line] = jobId + '/' + id;
};
ExecuteJob.prototype[CONSTANTS.IMAGE.BASIC] =
@@ -1265,11 +1543,9 @@ define([
imageNode = this._getExistingMetadata(jobId, 'Image', name);
if (!imageNode) {
this.logger.info(`Creating image ${id} named ${name}`);
imageNode = this.core.createNode({
base: this.META.Image,
parent: job
});
imageNode = this.createNode('Image', job);
this.setAttribute(imageNode, 'name', name);
this.createIdToMetadataId[imageNode] = id;
}
this._metadata[id] = imageNode;
}
+9 -1
Ver Arquivo
@@ -37,6 +37,10 @@ define([
this._currentSave = Q();
this.changes = {};
this.currentChanges = {}; // read-only changes being applied
this.creations = {};
this.deletions = [];
this.createIdToMetadataId = {};
this.initRun();
};
@@ -149,7 +153,7 @@ define([
// before continuing
this._currentSave = this._currentSave
.then(() => this.updateForkName(this.pipelineName))
.then(() => this.applyChanges())
.then(() => this.applyModelChanges())
.then(() => CreateExecution.prototype.save.call(this, msg))
.then(result => {
var msg;
@@ -185,6 +189,10 @@ define([
});
};
ExecutePipeline.prototype.isExecutionCanceled = function () {
return this.getAttribute(this.activeNode, 'status') === 'canceled';
};
ExecutePipeline.prototype.isInputData = function (node) {
var prnt = this.core.getParent(node);
return this.core.isTypeOf(prnt, this.META.Inputs);
+2 -2
Ver Arquivo
@@ -12,7 +12,7 @@ var JobLogManager = function(logger, config) {
};
JobLogManager.prototype._getFilePath = function(jInfo) {
this.logger.info(`getting file path for ${jInfo.job} in ${jInfo.project} on ${jInfo.branch}`);
this.logger.debug(`getting file path for ${jInfo.job} in ${jInfo.project} on ${jInfo.branch}`);
var jobId = jInfo.job.replace(/\//g, '_'),
filename = `${jobId}.txt`;
@@ -123,7 +123,7 @@ JobLogManager.prototype.appendTo = function(jobInfo, logs) {
JobLogManager.prototype.getLog = function(jobInfo) {
var filename = this._getFilePath(jobInfo);
this.logger.info(`Getting log content to ${filename}`);
this.logger.info(`Getting log content from ${filename}`);
return this.exists(jobInfo)
.then(exists => {
if (exists) {
Arquivo binário não exibido.
+285 -4
Ver Arquivo
@@ -10,12 +10,14 @@ describe('ExecuteJob', function () {
PluginCliManager = testFixture.WebGME.PluginCliManager,
projectName = 'testProject',
pluginName = 'ExecuteJob',
manager = new PluginCliManager(null, logger, gmeConfig),
project,
gmeAuth,
storage,
commitHash;
before(function (done) {
this.timeout(10000);
testFixture.clearDBAndGetGMEAuth(gmeConfig, projectName)
.then(function (gmeAuth_) {
gmeAuth = gmeAuth_;
@@ -25,7 +27,7 @@ describe('ExecuteJob', function () {
})
.then(function () {
var importParam = {
projectSeed: testFixture.path.join(testFixture.SEED_DIR, 'EmptyProject.webgmex'),
projectSeed: testFixture.path.join(testFixture.DF_SEED_DIR, 'devProject', 'devProject.webgmex'),
projectName: projectName,
branchName: 'master',
logger: logger,
@@ -51,9 +53,7 @@ describe('ExecuteJob', function () {
});
it('should verify activeNode is "Job"', function (done) {
var manager = new PluginCliManager(null, logger, gmeConfig),
pluginConfig = {
},
var pluginConfig = {},
context = {
project: project,
commitHash: commitHash,
@@ -68,4 +68,285 @@ describe('ExecuteJob', function () {
done();
});
});
////////// Helper Functions //////////
var plugin,
node,
preparePlugin = function(done) {
var context = {
project: project,
commitHash: commitHash,
branchName: 'test',
activeNode: '/K/R/p' // hello world job
};
return manager.initializePlugin(pluginName)
.then(plugin_ => {
plugin = plugin_;
return manager.configurePlugin(plugin, {}, context);
})
.then(() => node = plugin.activeNode)
.nodeify(done);
};
////////// END Helper Functions //////////
// Race condition checks w/ saving...
describe('get/set', function() {
beforeEach(preparePlugin);
it('should get correct attribute after set', function() {
plugin.setAttribute(node, 'status', 'queued');
var attrValue = plugin.getAttribute(node, 'status');
expect(attrValue).to.equal('queued');
});
it('should get correct attribute before updating nodes', function(done) {
// Run setAttribute on some node
plugin.setAttribute(node, 'status', 'queued');
// Check that the value is correct before applying node changes
var updateNodes = plugin.updateNodes;
plugin.updateNodes = function() {
var attrValue = plugin.getAttribute(node, 'status');
expect(attrValue).to.equal('queued');
return updateNodes.apply(this, arguments);
};
plugin.save().nodeify(done);
});
it('should get correct attribute (from new node) before updating nodes', function(done) {
// Run setAttribute on some node
var graphTmp = plugin.createNode('pipeline.Graph', node),
newVal = 'testGraph',
id = 'testId';
// Get the
plugin.setAttribute(graphTmp, 'name', newVal);
plugin._metadata[id] = graphTmp;
plugin.createIdToMetadataId[graphTmp] = id;
// Check that the value is correct before applying node changes
var updateNodes = plugin.updateNodes;
plugin.updateNodes = function() {
var graph = plugin._metadata[id],
attrValue = plugin.getAttribute(graph, 'name');
expect(attrValue).to.equal(newVal);
return updateNodes.apply(this, arguments);
};
plugin.save().nodeify(done);
});
it('should get correct attribute after save', function(done) {
// Run setAttribute on some node
plugin.setAttribute(node, 'status', 'queued');
// Check that the value is correct before applying node changes
plugin.save()
.then(() => {
var attrValue = plugin.getAttribute(node, 'status');
expect(attrValue).to.equal('queued');
})
.nodeify(done);
});
it('should get correct attribute while applying node changes', function(done) {
// Run setAttribute on some node
plugin.setAttribute(node, 'status', 'queued');
// Check that the value is correct before applying node changes
var oldApplyChanges = plugin._applyNodeChanges;
plugin._applyNodeChanges = function() {
var attrValue = plugin.getAttribute(node, 'status');
expect(attrValue).to.equal('queued');
return oldApplyChanges.apply(this, arguments);
};
plugin.save().nodeify(done);
});
});
describe('createNode', function() {
beforeEach(preparePlugin);
it('should update _metadata after applying changes', function(done) {
// Run setAttribute on some node
var graphTmp = plugin.createNode('pipeline.Graph', node),
id = 'testId';
plugin._metadata[id] = graphTmp;
plugin.createIdToMetadataId[graphTmp] = id;
// Check that the value is correct before applying node changes
var applyModelChanges = plugin.applyModelChanges;
plugin.applyModelChanges = function() {
return applyModelChanges.apply(this, arguments)
.then(() => {
var graph = plugin._metadata[id];
expect(graph).to.not.equal(graphTmp);
});
};
plugin.save().nodeify(done);
});
it('should update _metadata in updateNodes', function(done) {
var id = 'testId';
plugin._metadata[id] = node;
node.old = true;
plugin.updateNodes()
.then(() => {
var graph = plugin._metadata[id];
expect(graph.old).to.not.equal(true);
})
.nodeify(done);
});
// Check that it gets the correct value from a newly created node after
// it has been saved/created
it('should get changed attribute', function(done) {
// Run setAttribute on some node
var graphTmp = plugin.createNode('pipeline.Graph', node),
id = 'testId';
plugin._metadata[id] = node;
plugin.createIdToMetadataId[graphTmp] = id;
plugin.setAttribute(graphTmp, 'name', 'firstName');
// Check that the value is correct before applying node changes
plugin.save()
.then(() => {
var graph = plugin._metadata[id],
val = plugin.getAttribute(graph, 'name');
expect(val).to.equal('firstName');
})
.nodeify(done);
});
it('should get inherited attribute', function(done) {
// Run setAttribute on some node
var graphTmp = plugin.createNode('pipeline.Graph', node),
id = 'testId',
val;
// Check that the value is correct before applying node changes
plugin._metadata[id] = node;
plugin.createIdToMetadataId[graphTmp] = id;
val = plugin.getAttribute(graphTmp, 'name');
expect(val).to.equal('Graph');
plugin.save()
.then(() => {
var graph = plugin._metadata[id];
val = plugin.getAttribute(graph, 'name');
expect(val).to.equal('Graph');
})
.nodeify(done);
});
});
// Canceling
describe('cancel', function() {
beforeEach(preparePlugin);
it('should stop the job if the execution is canceled', function(done) {
var job = node,
hash = 'abc123',
exec = {
cancelJob: jobHash => expect(jobHash).equal(hash)
};
plugin.setAttribute(node, 'secret', 'abc');
plugin.isExecutionCanceled = () => true;
plugin.onOperationCanceled = () => done();
plugin.watchOperation(exec, hash, job, job);
});
it('should stop the job if a job is canceled', function(done) {
var job = node,
hash = 'abc123',
exec = {
cancelJob: jobHash => expect(jobHash).equal(hash)
};
plugin.setAttribute(job, 'secret', 'abc');
plugin.canceled = true;
plugin.onOperationCanceled = () => done();
plugin.watchOperation(exec, hash, job, job);
});
it('should set exec to running', function(done) {
var job = node,
execNode = plugin.core.getParent(job);
// Set the execution to canceled
plugin.setAttribute(execNode, 'status', 'canceled');
plugin.prepare = () => {
var status = plugin.getAttribute(execNode, 'status');
expect(status).to.not.equal('canceled');
return {then: () => done()};
};
plugin.main();
});
});
describe('exec files', function() {
describe('attribute file', function() {
var boolString = /['"](true|false)['"]/g;
beforeEach(preparePlugin);
it('should not quote true (s) boolean values', function() {
var files = {},
content,
matches;
plugin.setAttribute(node, 'debug', 'true');
plugin.createAttributeFile(node, files);
content = files['attributes.lua'];
matches = content.match(boolString);
expect(matches).to.equal(null);
});
it('should not quote true boolean values', function() {
var files = {},
content,
matches;
plugin.setAttribute(node, 'debug', true);
plugin.createAttributeFile(node, files);
content = files['attributes.lua'];
matches = content.match(boolString);
expect(matches).to.equal(null);
});
it('should not quote false (s) boolean values', function() {
var files = {},
content,
matches;
plugin.setAttribute(node, 'debug', 'false');
plugin.createAttributeFile(node, files);
content = files['attributes.lua'];
matches = content.match(boolString);
expect(matches).to.equal(null);
});
it('should not quote false boolean values', function() {
var files = {},
content,
matches;
plugin.setAttribute(node, 'debug', false);
plugin.createAttributeFile(node, files);
content = files['attributes.lua'];
matches = content.match(boolString);
expect(matches).to.equal(null);
});
});
});
});
+1 -1
Ver Arquivo
@@ -14,7 +14,7 @@ var testFixture = require('../../globals'),
];
describe('ImportTorch', function () {
this.timeout(5000);
this.timeout(7500);
var gmeConfig = testFixture.getGmeConfig(),
Q = testFixture.Q,
GraphChecker = testFixture.requirejs('deepforge/GraphChecker'),
+3
Ver Arquivo
@@ -280,6 +280,9 @@
},
"xor": {
"src": "src/seeds/xor"
},
"devProject": {
"src": "src/seeds/devProject"
}
},
"routers": {