aboutsummaryrefslogtreecommitdiffstats
path: root/lib/cloudify.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cloudify.js')
-rw-r--r--lib/cloudify.js231
1 files changed, 143 insertions, 88 deletions
diff --git a/lib/cloudify.js b/lib/cloudify.js
index b1565c4..145a10e 100644
--- a/lib/cloudify.js
+++ b/lib/cloudify.js
@@ -18,10 +18,8 @@ See the License for the specific language governing permissions and limitations
"use strict";
-const stream = require('stream');
-const targz = require('node-tar.gz');
+const admzip = require('adm-zip');
-const utils = require('./utils');
const repeat = require('./repeat');
const req = require('./promise_request');
const doRequest = req.doRequest;
@@ -39,83 +37,153 @@ var delay = function(dtime) {
});
};
+// Get current status of a workflow execution
+// Function for getting execution info
+const getExecutionStatus = function(executionId) {
+ var reqOptions = {
+ method : "GET",
+ uri : cfyAPI + "/executions/" + executionId
+ };
+ if (cfyAuth) {
+ reqOptions.auth = cfyAuth;
+ }
+ return doRequest(reqOptions);
+};
+
// Poll for the result of a workflow execution
var getWorkflowResult = function(execution_id) {
var finished = [ "terminated", "cancelled", "failed" ];
var retryInterval = 15000; // Every 15 seconds
var maxTries = 240; // Up to an hour
- logger.debug("Getting workflow status for execution id: " + execution_id);
-
- // Function for getting execution info
- var getExecutionStatus = function() {
- var reqOptions = {
- method : "GET",
- uri : cfyAPI + "/executions/" + execution_id
- };
- if (cfyAuth) {
- reqOptions.auth = cfyAuth;
- }
- return doRequest(reqOptions);
- };
+ logger.debug("Getting workflow result for execution id: " + execution_id);
// Function for testing if workflow is finished
- // Expects the result of getExecutionStatus
+ // Expects the result of getExecStatus
var checkStatus = function(res) {
logger.debug("Checking result: " + JSON.stringify(res) + " ==> " + (res.json && res.json.status && finished.indexOf(res.json.status) < 0));
return res.json && res.json.status && finished.indexOf(res.json.status) < 0;
};
-
- return repeat.repeatWhile(getExecutionStatus, checkStatus, maxTries,
- retryInterval).then(function(res) {
- if (res.json && res.json.status && res.json.status !== "terminated") {
- throw ("workflow failed!");
- } else {
+
+ // Create execution status checker function
+ var getExecStatus = function() { return getExecutionStatus(execution_id);};
+
+ return repeat.repeatWhile(getExecStatus, checkStatus, maxTries, retryInterval)
+ .then(
+
+ /* Handle fulfilled promise from repeatWhile */
+ function(res) {
+
+ logger.debug('workflow result: ' + JSON.stringify(res));
+
+ /* Successful completion */
+ if (res.json && res.json.status && res.json.status === 'terminated') {
return res;
}
+
+ /* If we get here, we don't have a success and we're going to throw something */
+
+ var error = {};
+
+ /* We expect a JSON object with a status */
+ if (res.json && res.json.status) {
+
+ /* Failure -- we need to return something that looks like the CM API failures */
+ if (res.json.status === 'failed') {
+ error.body = 'workflow failed: ' + execution_id + ' -- ' + (res.json.error ? JSON.stringify(res.json.error) : 'no error information');
+ }
+
+ /* Cancellation -- don't really expect this */
+ else if (res.json.status === 'canceled' || res.json.status === 'cancelled') {
+ error.body = 'workflow canceled: ' + execution_id;
+ }
+
+ /* Don't expect anything else -- but if we get it, it's not a success! */
+ else {
+ error.body = 'workflow--unexpected status ' + res.json.status + ' for ' + execution_id;
+ }
+ }
+
+ /* The body of the response from the API call to get execution status is not what we expect at all */
+ else {
+ error.body = 'workflow--unexpected result body getting execution status from CM for ' + execution_id;
+ }
+
+ throw error;
+ },
+
+ /* Handle rejection of promise from repeatWhile--don't use a catch because it would catch the error thrown above */
+ function(err) {
+ /* repeatWhile could fail and we get here because:
+ * -- repeatWhile explicitly rejects the promise because it has exhausted the retries
+ * -- repeatWhile propagates a system error (e.g., network problem) trying to access the API
+ * -- repeatWhile propagates a rejected promise due to a bad HTTP response status
+ * These should all get normalized in deploy.js--so we just rethrow the error.
+ */
+
+ throw err;
+
});
};
+//Initiate a workflow execution against a deployment
+const initiateWorkflowExecution = function(dpid, workflow) {
+ // Set up the HTTP POST request
+ var reqOptions = {
+ method : "POST",
+ uri : cfyAPI + "/executions",
+ headers : {
+ "Content-Type" : "application/json",
+ "Accept" : "*/*"
+ }
+ };
+ if (cfyAuth) {
+ reqOptions.auth = cfyAuth;
+ }
+ var body = {
+ deployment_id : dpid,
+ workflow_id : workflow
+ };
+
+ // Make the POST request
+ return doRequest(reqOptions, JSON.stringify(body))
+ .then(function(result) {
+ logger.debug("Result from POSTing workflow execution start: " + JSON.stringify(result));
+ if (result.json && result.json.id) {
+ return {deploymentId: dpid, workflowType: workflow, executionId: result.json.id};
+ }
+ else {
+ logger.debug("Did not get expected JSON body from POST to start workflow");
+ var err = new Error("POST to start workflow got success response but no body");
+ err.status = err.code = 502;
+ }
+ });
+};
+
// Uploads a blueprint via the Cloudify API
exports.uploadBlueprint = function(bpid, blueprint) {
+
// Cloudify API wants a gzipped tar of a directory, not the blueprint text
- // So we make a directory and feed a gzipped tar as the body of the PUT
- // request
- var workingDir = "./work/" + bpid;
-
- return utils.makeDirAndFile(workingDir, 'blueprint.yaml', blueprint)
-
- .then(function() {
- // Set up a read stream that presents tar'ed and gzipped data
- var src = targz().createReadStream(workingDir);
-
- // Set up the HTTP PUT request
- var reqOptions = {
+ var zip = new admzip();
+ zip.addFile('work/', new Buffer(0));
+ zip.addFile('work/blueprint.yaml', new Buffer(blueprint, 'utf8'));
+ var src = (zip.toBuffer());
+
+ // Set up the HTTP PUT request
+ var reqOptions = {
method : "PUT",
uri : cfyAPI + "/blueprints/" + bpid,
headers : {
"Content-Type" : "application/octet-stream",
"Accept" : "*/*"
}
- };
-
- if (cfyAuth) {
- reqOptions.auth = cfyAuth;
- }
- // Initiate PUT request and return the promise for a result
- return doRequest(reqOptions, src).then(
- // Cleaning up the working directory without perturbing the result is
- // messy!
- function(result) {
- utils.removeDir(workingDir);
- return result;
- }, function(err) {
- logger.debug("Problem on upload: " + JSON.stringify(err));
- utils.removeDir(workingDir);
- throw err;
- });
+ };
- });
+ if (cfyAuth) {
+ reqOptions.auth = cfyAuth;
+ }
+ // Initiate PUT request and return the promise for a result
+ return doRequest(reqOptions, src);
};
// Creates a deployment from a blueprint
@@ -145,41 +213,31 @@ exports.createDeployment = function(dpid, bpid, inputs) {
return doRequest(reqOptions, JSON.stringify(body));
};
-// Executes a workflow against a deployment (use for install and uninstall)
-exports.executeWorkflow = function(dpid, workflow) {
+// Initiate a workflow execution against a deployment
+exports.initiateWorkflowExecution = initiateWorkflowExecution;
- // Set up the HTTP POST request
- var reqOptions = {
- method : "POST",
- uri : cfyAPI + "/executions",
- headers : {
- "Content-Type" : "application/json",
- "Accept" : "*/*"
- }
- };
- if (cfyAuth) {
- reqOptions.auth = cfyAuth;
- }
- var body = {
- deployment_id : dpid,
- workflow_id : workflow
- };
+// Get the status of a workflow execution
+exports.getWorkflowExecutionStatus = getExecutionStatus;
- // Make the POST request
- return doRequest(reqOptions, JSON.stringify(body)).then(
- function(result) {
- logger.debug("Result from POSTing workflow start: " + JSON.stringify(result));
- if (result.json && result.json.id) {
- logger.debug("Waiting for workflow status: " + result.json.id);
- return getWorkflowResult(result.json.id);
- }
- else {
- logger.warn("Did not get expected JSON body from POST to start workflow");
- // TODO throw? we got an OK for workflow but no JSON?
- }
- });
+// Return a promise for the final result of a workflow execution
+exports.getWorkflowResult = getWorkflowResult;
+
+// Executes a workflow against a deployment and returns a promise for final result
+exports.executeWorkflow = function(dpid, workflow) {
+
+ // Initiate the workflow
+ return initiateWorkflowExecution(dpid, workflow)
+
+ // Wait for the result
+ .then (function(result) {
+ logger.debug("Result from initiating workflow: " + JSON.stringify(result));
+ return getWorkflowResult(result.executionId);
+ });
};
+// Wait for workflow to complete and get result
+exports.getWorkflowResult = getWorkflowResult;
+
// Retrieves outputs for a deployment
exports.getOutputs = function(dpid) {
var reqOptions = {
@@ -245,10 +303,7 @@ exports.setAPIAddress = function(addr) {
// Allow client to set Cloudify credentials
exports.setCredentials = function(user, password) {
- cfyAuth = {
- user : user,
- password : password
- };
+ cfyAuth = user + ':' + password;
};
// Set a logger