From ff6ba434b6d91b6a4a4e9b3a7fbb8cadced229ad Mon Sep 17 00:00:00 2001 From: Jack Lucas Date: Wed, 10 May 2017 01:48:41 +0000 Subject: Post-R1 API & other updates. Change-Id: Id0e2e15b95a5713a25a746534fc40b56599a5f06 Signed-off-by: Jack Lucas --- lib/deploy.js | 184 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 147 insertions(+), 37 deletions(-) (limited to 'lib/deploy.js') diff --git a/lib/deploy.js b/lib/deploy.js index e807060..fcee43d 100644 --- a/lib/deploy.js +++ b/lib/deploy.js @@ -26,6 +26,8 @@ const DELAY_RETRIEVE_OUTPUTS = 5000; const DELAY_DELETE_DEPLOYMENT = 30000; const DELAY_DELETE_BLUEPRINT = 10000; +const createError = require('./dispatcher-error').createDispatcherError; + /* Set up the Cloudify low-level interface library */ var cfy = require("./cloudify.js"); /* Set config for deploy module */ @@ -52,36 +54,37 @@ var parseContent = function(input) { // create a normalized representation of errors, whether they're a node.js Error or a Cloudify API error var normalizeError = function (err) { - var e = {}; + var e; + if (err instanceof Error) { - e.message = err.message; - if (err.code) { - e.code = err.code; - } - if (err.status) { - e.status = err.status; - } + /* node.js system error */ + e = createError("Error communicating with CM: " + err.message, 504, "system", 202, 'cloudify-manager'); } else { // Try to populate error with information from a Cloudify API error // We expect to see err.body, which is a stringified JSON object // We can parse it and extract message and error_code - e.message = "unknown API error"; - e.code = "UNKNOWN"; - if (err.status) { - e.status = err.status; - } + var message = err.message || "unknown Cloudify Manager API error"; + var status = err.status || 502; + var cfyCode = "UNKNOWN"; + var cfyMessage; + if (err.body) { var p = parseContent(err.body); if (p.json) { - e.message = p.content.message ? p.content.message : "unknown API error"; - e.code = p.content.error_code ? p.content.error_code : "UNKNOWN"; + cfyMessage = p.content.message ? p.content.message : "unknown Cloudify API error"; + cfyCode = p.content.error_code ? p.content.error_code : "UNKNOWN"; } else { // if there's a body and we can't parse it just attach it as the message - e.message = err.body; + cfyMessage = err.body; } + message = "Status " + status + " from CM API -- error code: " + cfyCode + " -- message: " + cfyMessage; } + + /* Pass through 400-level status, recast 500-level */ + var returnStatus = (err.status > 499) ? 502 : err.status; + e = createError(message, returnStatus, "api", 502, 'cloudify-manager'); } return e; @@ -129,26 +132,43 @@ var delay = function(dtime) { }); }; -// Go through the Cloudify API call sequence to do a deployment -exports.deployBlueprint = function(id, blueprint, inputs) { - +// Go through the Cloudify API call sequence to upload blueprint, create deployment, and launch install workflow +// (but don't wait for the workflow to finish) +const launchBlueprint = function(id, blueprint, inputs) { logger.debug("deploymentId: " + id + " starting blueprint upload"); // Upload blueprint return cfy.uploadBlueprint(id, blueprint) + + // Create deployment .then (function(result) { logger.debug("deploymentId: " + id + " blueprint uploaded"); // Create deployment return cfy.createDeployment(id, id, inputs); }) + + // Launch the workflow, but don't wait for it to complete .then(function(result){ logger.debug("deploymentId: " + id + " deployment created"); - // Execute the install workflow - return delay(DELAY_INSTALL_WORKFLOW).then(function(){ return cfy.executeWorkflow(id, 'install');}); + return delay(DELAY_INSTALL_WORKFLOW) + .then(function(){ + return cfy.initiateWorkflowExecution(id, 'install'); + }); }) - .then(function(result) { - logger.debug("deploymentId: " + id + " install workflow successfully executed"); + .catch(function(error) { + logger.debug("Error: " + error + " for launch blueprint for deploymentId " + id); + throw normalizeError(error); + }) +}; +exports.launchBlueprint = launchBlueprint; + +// Finish installation launched with launchBlueprint +const finishInstallation = function(deploymentId, executionId) { + logger.debug("finishInstallation: " + deploymentId + " -- executionId: " + executionId); + return cfy.getWorkflowResult(executionId) + .then (function(result){ + logger.debug("deploymentId: " + deploymentId + " install workflow successfully executed"); // Retrieve the outputs from the deployment, as specified in the blueprint - return delay(DELAY_RETRIEVE_OUTPUTS).then(function() { return cfy.getOutputs(id); }); + return delay(DELAY_RETRIEVE_OUTPUTS).then(function() { return cfy.getOutputs(deploymentId); }); }) .then(function(result) { // We have the raw outputs from the deployment but not annotated with the descriptions @@ -161,35 +181,125 @@ exports.deployBlueprint = function(id, blueprint, inputs) { } } } - logger.debug("output retrieval result for " + id + ": " + JSON.stringify(result)); - logger.info("deploymentId " + id + " successfully deployed"); - return annotateOutputs(id, rawOutputs); + logger.debug("output retrieval result for " + deploymentId + ": " + JSON.stringify(result)); + logger.info("deploymentId " + deploymentId + " successfully deployed"); + return annotateOutputs(deploymentId, rawOutputs); }) .catch(function(err) { + logger.debug("Error finishing install workflow: " + err + " -- " + JSON.stringify(err)); throw normalizeError(err); }); -}; +} +exports.finishInstallation = finishInstallation; -// Go through the Cloudify API call sequence to do an undeployment of a previously deployed blueprint -exports.undeployDeployment = function(id) { - logger.debug("deploymentId: " + id + " starting uninstall workflow"); +// Initiate uninstall workflow against a deployment, but don't wait for workflow to finish +const launchUninstall = function(deploymentId) { + logger.debug("deploymentId: " + deploymentId + " starting uninstall workflow"); // Run uninstall workflow - return cfy.executeWorkflow(id, 'uninstall', 0) + return cfy.initiateWorkflowExecution(deploymentId, 'uninstall') + .then(function(result) { + return result; + }) + .catch(function(err) { + logger.debug("Error initiating uninstall workflow: " + err + " -- " + JSON.stringify(err)); + throw normalizeError(err); + }); +}; +exports.launchUninstall = launchUninstall; + +const finishUninstall = function(deploymentId, executionId) { + logger.debug("finishUninstall: " + deploymentId + " -- executionId: " + executionId); + return cfy.getWorkflowResult(executionId) .then (function(result){ - logger.debug("deploymentId: " + id + " uninstall workflow completed"); + logger.debug("deploymentId: " + deploymentId + " uninstall workflow successfully executed"); // Delete the deployment - return delay(DELAY_DELETE_DEPLOYMENT).then(function() {return cfy.deleteDeployment(id);}); + return delay(DELAY_DELETE_DEPLOYMENT).then(function() {return cfy.deleteDeployment(deploymentId);}); }) .then (function(result){ - logger.debug("deploymentId: " + id + " deployment deleted"); + logger.debug("deploymentId: " + deploymentId + " deployment deleted"); // Delete the blueprint - return delay(DELAY_DELETE_BLUEPRINT).then(function() {return cfy.deleteBlueprint(id);}); + return delay(DELAY_DELETE_BLUEPRINT).then(function() {return cfy.deleteBlueprint(deploymentId);}); }) .then (function(result){ - logger.info("deploymentId: " + id + " successfully undeployed"); + logger.info("deploymentId: " + deploymentId + " successfully undeployed"); return result; }) .catch (function(err){ throw normalizeError(err); }); + }; +exports.finishUninstall = finishUninstall; + +// Get the status of a workflow execution +exports.getExecutionStatus = function (exid) { + return cfy.getWorkflowExecutionStatus(exid) + .then(function(res){ + + var result = { + operationType: res.json.workflow_id + } + + // Map execution status + if (res.json.status === "terminated") { + result.status = "succeeded"; + } + else if (res.json.status === "failed") { + result.status = "failed"; + } + else if (res.json.status === "cancelled" || res.stats === "canceled") { + result.status = "canceled"; + } + else { + result.status = "processing"; + } + + if (res.json.error) { + result.error = res.json.error; + } + logger.debug("getExecutionStatus result: " + JSON.stringify(result)); + return result; + }) + .catch(function(error) { + throw normalizeError(error); + }); +}; + +// Go through the Cloudify API call sequence to do a deployment +exports.deployBlueprint = function(id, blueprint, inputs) { + + // Upload blueprint, create deployment, and initiate install workflow + return launchBlueprint(id, blueprint, inputs) + + // Wait for the workflow to complete + .then( + + // launchBlueprint promise fulfilled -- finish installation + function(result){ + return finishInstallation(result.deploymentId, result.executionId); // Will throw normalized error if it fails + }, + + // launchBlueprint promise rejected -- report error + function(err) { + throw normalizeError(err); + }); +}; + +// Go through the Cloudify API call sequence to do an undeployment of a previously deployed blueprint +exports.undeployDeployment = function(id) { + logger.debug("deploymentId: " + id + " starting uninstall workflow"); + + // Run launch uninstall workflow + return launchUninstall(id) + + // launchUninstall promise fulfilled -- finish uninstall + .then (function(result){ + return finishUninstall(result.deploymentId, result.executionId); // Will throw normalized error if it fails + }, + + // launchUninstall promise rejected -- report error + function(err){ + throw normalizeError(err); + }); +}; + -- cgit 1.2.3-korg