-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
pmeijer
committed
Feb 26, 2015
1 parent
a7751c7
commit b72f467
Showing
5 changed files
with
395 additions
and
1 deletion.
There are no files selected for viewing
4 changes: 3 additions & 1 deletion
4
src/plugin/coreplugins/ConfigurationArtifact/ConfigurationArtifact.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
259 changes: 259 additions & 0 deletions
259
src/plugin/coreplugins/ExecutorPlugin/ExecutorPlugin.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,259 @@ | ||
/*globals define*/ | ||
/*jshint node:true, browser:true*/ | ||
|
||
/** | ||
* | ||
* @author pmeijer / https://github.com/pmeijer | ||
*/ | ||
|
||
define(['plugin/PluginConfig', | ||
'plugin/PluginBase', | ||
'executor/ExecutorClient', | ||
'ejs', | ||
'plugin/ExecutorPlugin/ExecutorPlugin/Templates/Templates'], | ||
function (PluginConfig, PluginBase, ExecutorClient, ejs, TEMPLATES) { | ||
'use strict'; | ||
|
||
/** | ||
* Initializes a new instance of ExecutorPlugin. | ||
* @class | ||
* @augments {PluginBase} | ||
* @classdesc This class represents the plugin ExecutorPlugin. | ||
* @constructor | ||
*/ | ||
var ExecutorPlugin = function () { | ||
// Call base class' constructor. | ||
PluginBase.call(this); | ||
}; | ||
|
||
// Prototypal inheritance from PluginBase. | ||
ExecutorPlugin.prototype = Object.create(PluginBase.prototype); | ||
ExecutorPlugin.prototype.constructor = ExecutorPlugin; | ||
|
||
/** | ||
* Gets the name of the ExecutorPlugin. | ||
* @returns {string} The name of the plugin. | ||
* @public | ||
*/ | ||
ExecutorPlugin.prototype.getName = function () { | ||
return 'Executor Plugin'; | ||
}; | ||
|
||
/** | ||
* Gets the semantic version (semver.org) of the ExecutorPlugin. | ||
* @returns {string} The version of the plugin. | ||
* @public | ||
*/ | ||
ExecutorPlugin.prototype.getVersion = function () { | ||
return '0.1.0'; | ||
}; | ||
|
||
/** | ||
* Gets the description of the ExecutorPlugin. | ||
* @returns {string} The description of the plugin. | ||
* @public | ||
*/ | ||
ExecutorPlugin.prototype.getDescription = function () { | ||
return 'Plugin using the Executor Client'; | ||
}; | ||
|
||
ExecutorPlugin.prototype.getConfigStructure = function () { | ||
return [ | ||
{ | ||
'name': 'pythonCmd', | ||
'displayName': 'Python path', | ||
'description': 'How Python is executed', | ||
'value': 'C:/Python27/python.exe', | ||
'valueType': 'string', | ||
'readOnly': false | ||
}, | ||
{ | ||
'name': 'update', | ||
'displayName': 'Write back to model', | ||
'description': 'If false no need to provide active node.', | ||
'value': true, | ||
'valueType': 'boolean', | ||
'readOnly': false | ||
}, | ||
{ | ||
'name': 'success', | ||
'displayName': 'Should succeed', | ||
'description': 'Should the execution exit with 0?', | ||
'value': true, | ||
'valueType': 'boolean', | ||
'readOnly': false | ||
}, | ||
{ | ||
'name': 'time', | ||
'displayName': 'Execution time [s]', | ||
'description': 'How long should the script run?', | ||
'value': 1, | ||
'valueType': 'number', | ||
'minValue': 0.1, | ||
'maxValue': 10000, | ||
'readOnly': false | ||
} | ||
]; | ||
}; | ||
|
||
/** | ||
* Main function for the plugin to execute. This will perform the execution. | ||
* Notes: | ||
* - Always log with the provided logger.[error,warning,info,debug]. | ||
* - Do NOT put any user interaction logic UI, etc. inside this method. | ||
* - callback always has to be called even if error happened. | ||
* | ||
* @param {function(string, plugin.PluginResult)} callback - the result callback | ||
*/ | ||
ExecutorPlugin.prototype.main = function (callback) { | ||
// Use self to access core, project, result, logger etc from PluginBase. | ||
// These are all instantiated at this point. | ||
var self = this, | ||
config = self.getCurrentConfig(), | ||
exitCode = config.success ? 0 : 1, | ||
executorConfig = { | ||
cmd: config.pythonCmd + ' generate_name.py ', // This is the command that will be executed on the worker | ||
resultArtifacts: [ // These are the results that will be returned | ||
{ | ||
name: 'all', | ||
resultPatterns: [] | ||
}, | ||
{ | ||
name: 'logs', | ||
resultPatterns: ['log/**/*'] | ||
}, | ||
{ | ||
name: 'resultFile', | ||
resultPatterns: ['new_name.json'] | ||
} | ||
] | ||
}, | ||
filesToAdd, | ||
artifact; | ||
|
||
// This is just to track the current node path in the result from the execution. | ||
if (config.update) { | ||
if (!self.activeNode) { | ||
callback('No activeNode specified! Set update to false or invoke from a node.', self.result); | ||
return; | ||
} | ||
executorConfig.cmd += self.core.getPath(self.activeNode); | ||
} else { | ||
executorConfig.cmd += 'dummy'; | ||
} | ||
|
||
// The hash of the artifact with these files will define the job. N.B. executor_config.json must exist. | ||
filesToAdd = { | ||
'executor_config.json': JSON.stringify(executorConfig, null, 4), | ||
'generate_name.py': ejs.render(TEMPLATES['generate_name.py.ejs'], { | ||
exitCode: exitCode, | ||
sleepTime: config.time | ||
}) | ||
}; | ||
|
||
artifact = self.blobClient.createArtifact('executionFiles'); | ||
|
||
artifact.addFiles(filesToAdd, function (err) { | ||
if (err) { | ||
callback(err, self.result); | ||
return; | ||
} | ||
artifact.save(function (err, hash) { | ||
var executorClient; | ||
if (err) { | ||
callback(err, self.result); | ||
return; | ||
} | ||
self.result.addArtifact(hash); | ||
executorClient = new ExecutorClient(); | ||
// Here the hash of the artifact is passed to the new job. | ||
executorClient.createJob({hash: hash}, function (err, jobInfo) { | ||
var intervalID, | ||
atSucceedJob; | ||
if (err) { | ||
callback('Creating job failed: ' + err.toString(), self.result); | ||
return; | ||
} | ||
self.logger.debug(jobInfo); | ||
// This will be called after a succeed job | ||
|
||
intervalID = setInterval(function () { | ||
// Get the job-info at intervals and check for a non-CREATED/RUNNING status. | ||
executorClient.getInfo(hash, function (err, jInfo) { | ||
var key; | ||
self.logger.info(JSON.stringify(jInfo, null, 4)); | ||
if (jInfo.status === 'CREATED' || jInfo.status === 'RUNNING') { | ||
// The job is still running.. | ||
return; | ||
} | ||
|
||
clearInterval(intervalID); | ||
if (jInfo.status === 'SUCCESS') { | ||
self.atSucceedJob(jInfo, callback); | ||
} else { | ||
//Add the resultHashes even though job failed (for user to debug). | ||
for (key in jInfo.resultHashes) { | ||
if (jInfo.resultHashes.hasOwnProperty(key)) { | ||
self.result.addArtifact(jInfo.resultHashes[key]); | ||
} | ||
} | ||
callback('Job execution failed', self.result); | ||
} | ||
}); | ||
}, 400); | ||
}); | ||
}); | ||
}); | ||
}; | ||
|
||
ExecutorPlugin.prototype.atSucceedJob = function (jobInfo, mainCallback) { | ||
var self = this; | ||
//After the job has been executed jobInfo will contain the result hashes. | ||
self.blobClient.getMetadata(jobInfo.resultHashes.resultFile, function (err, metaData) { | ||
var newNameJsonHash; | ||
if (err) { | ||
mainCallback('Getting results metadata failed: ' + err.toString(), self.result); | ||
return; | ||
} | ||
newNameJsonHash = metaData.content['new_name.json'].content; | ||
self.blobClient.getObject(newNameJsonHash, function (err, newName) { | ||
var key; | ||
if (err) { | ||
mainCallback('Getting content failed: ' + err.toString(), self.result); | ||
return; | ||
} | ||
if (self.getCurrentConfig().update) { | ||
for (key in newName) { | ||
if (newName.hasOwnProperty(key)) { | ||
self.core.setAttribute(self.activeNode, 'name', newName[key]); | ||
} | ||
} | ||
for (key in jobInfo.resultHashes) { | ||
if (jobInfo.resultHashes.hasOwnProperty(key)) { | ||
self.result.addArtifact(jobInfo.resultHashes[key]); | ||
} | ||
} | ||
|
||
self.save('Updated new name from execution', function (err) { | ||
if (err) { | ||
mainCallback(err, self.result); | ||
return; | ||
} | ||
self.result.setSuccess(true); | ||
mainCallback(null, self.result); | ||
}); | ||
} else { | ||
for (key in jobInfo.resultHashes) { | ||
if (jobInfo.resultHashes.hasOwnProperty(key)) { | ||
self.result.addArtifact(jobInfo.resultHashes[key]); | ||
} | ||
} | ||
self.result.setSuccess(true); | ||
mainCallback(null, self.result); | ||
} | ||
}); | ||
}); | ||
}; | ||
|
||
return ExecutorPlugin; | ||
}); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
82 changes: 82 additions & 0 deletions
82
src/plugin/coreplugins/ExecutorPlugin/Templates/combine_templates.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* | ||
* Copyright (C) 2014 Vanderbilt University, All rights reserved. | ||
* | ||
* Author: Zsolt Lattmann, Patrik Meijer | ||
* | ||
* This script will combine all ejs files in the current directory (recursively) | ||
* into one Templates.js file. By importing this file as TEMPLATE you can retrieve the | ||
* content of each original ejs file through TEMPLATES['plugin.js.ejs']. | ||
* | ||
* Usage: Run this script in the directory with the ejs-templates, e.g. '%YourPlugin%/Templates'. | ||
*/ | ||
|
||
var main = function () { | ||
'use strict'; | ||
var fs = require('fs'), | ||
isEjsFile = function (str) { | ||
var ending = '.ejs', | ||
lastIndex = str.lastIndexOf(ending); | ||
return (lastIndex !== -1) && (lastIndex + ending.length === str.length); | ||
}, | ||
walk = function (dir, done) { | ||
var results = []; | ||
fs.readdir(dir, function (err, list) { | ||
if (err) { | ||
return done(err); | ||
} | ||
var i = 0; | ||
(function next() { | ||
var file = list[i]; | ||
if (!file) { | ||
return done(null, results); | ||
} | ||
i += 1; | ||
file = dir + '/' + file; | ||
fs.stat(file, function (err, stat) { | ||
if (stat && stat.isDirectory()) { | ||
walk(file, function (err, res) { | ||
results = results.concat(res); | ||
next(); | ||
}); | ||
} else { | ||
results.push(file); | ||
next(); | ||
} | ||
}); | ||
})(); | ||
}); | ||
}, | ||
content = {}, | ||
fileName, | ||
i, | ||
templateContent; | ||
|
||
walk('.', function (err, results) { | ||
if (err) { | ||
throw err; | ||
} | ||
|
||
for (i = 0; i < results.length; i += 1) { | ||
fileName = results[i]; | ||
console.info(fileName); | ||
if (isEjsFile(fileName)) { | ||
console.info('Was ejs -> added!'); | ||
content[fileName.substring(2)] = fs.readFileSync(fileName, {'encoding': 'utf-8'}); | ||
} | ||
} | ||
|
||
console.info(content); | ||
templateContent = ''; | ||
templateContent += '/* Generated file based on ejs templates */\r\n'; | ||
templateContent += 'define([], function() {\r\n'; | ||
templateContent += ' return ' + JSON.stringify(content, null, 4); | ||
templateContent += '});'; | ||
|
||
fs.writeFileSync('Templates.js', templateContent); | ||
console.info('Created Templates.js'); | ||
}); | ||
}; | ||
|
||
if (require.main === module) { | ||
main(); | ||
} |
Oops, something went wrong.