diff options
author | Shadi Haidar <sh1986@att.com> | 2018-09-07 22:05:43 -0400 |
---|---|---|
committer | Shadi Haidar <sh1986@att.com> | 2018-09-12 13:58:04 -0400 |
commit | ceda84d021dde70299f96984ca7aec16740854be (patch) | |
tree | 967b1c37a9e21721240d68e9b81dc59a0a593512 /lib | |
parent | 8ca8ddf40decb543e5ea634e29efe6972272a2f6 (diff) |
Check deployment creation before install
Unit Test Code Coverage:
Statements : 76.85% ( 893/1162 )
Branches : 52.99% ( 275/519 )
Functions : 78.68% ( 155/197 )
Lines : 77.21% ( 884/1145 )
Issue-ID: DCAEGEN2-754
Change-Id: Id8f3fa26e8ece7e9099145ea20034829a1ad7d13
Signed-off-by: Shadi Haidar <sh1986@att.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/cloudify.js | 97 | ||||
-rw-r--r-- | lib/deploy.js | 28 |
2 files changed, 111 insertions, 14 deletions
diff --git a/lib/cloudify.js b/lib/cloudify.js index 053bdb8..5ab0663 100644 --- a/lib/cloudify.js +++ b/lib/cloudify.js @@ -20,8 +20,11 @@ See the License for the specific language governing permissions and limitations const CLOUDIFY = "cloudify-manager"; const FINISHED = [ "terminated", "cancelled", "failed" ]; +const DEPLOYMENT_CREATION_FINISHED = [ "terminated" ]; const RETRY_INTERVAL = 5000; // Every 5 seconds const MAX_TRIES = 720; // Up to 1 hour +const DEP_CREATION_STATUS_RETRY_INTERVAL = 30000; // Every 30 seconds +const DEP_CREATION_STATUS_MAX_TRIES = 10; // Up to 5 minutes const DEFAULT_TENANT = "default_tenant"; const doRequest = require('./promise_request').doRequest; const repeat = require('./repeat'); @@ -79,6 +82,17 @@ const getExecutionStatus = function(req, execution_id) { return doRequest(req, reqOptions, null, CLOUDIFY); }; +// Get current status of a deployment creation +const getDeploymentCreationStatus = function(req, deployment_id) { + var reqOptions = { + method : "GET", + uri : cfyAPI + "/executions?deployment_id=" + deployment_id + "&workflow_id=create_deployment_environment&_include=id,status" + }; + addAuthToOptions(reqOptions, req); + + return doRequest(req, reqOptions, null, CLOUDIFY); +}; + // Poll for the result of a workflow execution until it's done const getWorkflowResult = function(mainReq, execution_id) { /* Defense: Some callers do not supply mainReq */ @@ -194,6 +208,83 @@ const initiateWorkflowExecution = function(req, deployment_id, workflow_id, para }); }; +// Poll for the deployment creation status +const getDeploymentCreationResult = function(mainReq, deployment_id) { + /* Defense: Some callers do not supply mainReq */ + mainReq = mainReq || {}; + logger.info(mainReq.dcaeReqId, "Getting status for deployment id: " + deployment_id); + + // Function for testing if deployment creation is complete + // Expects the result of getDepCrStatus + const checkDepStatus = function(cloudify_response) { + cloudify_response = cloudify_response && cloudify_response.json; + logger.info(mainReq.dcaeReqId, "Checking Deployment creation result: " + JSON.stringify(cloudify_response) + " ==> " + + (cloudify_response.items.length == 1 && DEPLOYMENT_CREATION_FINISHED.indexOf(cloudify_response.items[0].status) < 0)); + return cloudify_response.items.length == 1 && DEPLOYMENT_CREATION_FINISHED.indexOf(cloudify_response.items[0].status) < 0; + }; + + // Create deployment creation status checker function + const getDepCrStatus = function() {return getDeploymentCreationStatus(mainReq, deployment_id);}; + + return repeat.repeatWhile(getDepCrStatus, checkDepStatus, DEP_CREATION_STATUS_MAX_TRIES, DEP_CREATION_STATUS_RETRY_INTERVAL) + .then( + + /* Handle fulfilled promise from repeatWhile */ + function(res) { + + logger.info(mainReq.dcaeReqId, 'Deployment creation result: ' + JSON.stringify(res)); + + /* Successful completion */ + if (res.json && res.json.items.length == 1 && res.json.items[0].status === 'terminated') { + logger.info(mainReq.dcaeReqId, 'Deployment creation completed for deployment_id: ' + deployment_id); + 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.items.length == 1 && res.json.items[0].status) { + + /* Failure -- we need to return something that looks like the CM API failures */ + if (res.json.items[0].status === 'failed') { + error.body = 'Deployment creation failed: ' + deployment_id + ' -- ' + (res.json.error ? JSON.stringify(res.json.error) : 'no error information'); + } + + /* Cancellation -- don't really expect this */ + else if (res.json.items[0].status === 'canceled' || res.json.status === 'cancelled') { + error.body = 'Deployment creation canceled: ' + deployment_id; + } + + /* Don't expect anything else -- but if we get it, it's not a success! */ + else { + error.body = 'Deployment creation--unexpected status ' + res.json.items[0].status + ' for ' + deployment_id; + } + } + + /* The body of the response from the API call to get execution status is not what we expect at all */ + else { + error.body = 'Deployment creation--unexpected result body getting execution status from CM for ' + deployment_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; + + }); +}; + // Uploads a blueprint via the Cloudify API exports.uploadBlueprint = function(req, bpid, blueprint) { logger.info(req.dcaeReqId, "uploadBlueprint " + bpid); @@ -264,6 +355,11 @@ exports.executeWorkflow = function(req, deployment_id, workflow_id, parameters) }); }; +// Return a promise for the final result of a deployment update +exports.getDeploymentCreationResult = getDeploymentCreationResult; + +// Get the status of a deployment update +exports.getDeploymentCreationStatus = getDeploymentCreationStatus; // Retrieves outputs for a deployment exports.getOutputs = function(req, dpid) { @@ -469,3 +565,4 @@ exports.executeOperation = function (mainReq, deployment_id, operation, operatio exeQueue.queueUpExecution(mainReq, deployment_id, workflow_id, parameters); runQueuedExecution(mainReq, deployment_id, workflow_id, parameters); }; + diff --git a/lib/deploy.js b/lib/deploy.js index ee31fd3..2d75b52 100644 --- a/lib/deploy.js +++ b/lib/deploy.js @@ -144,23 +144,23 @@ const launchBlueprint = function(req, id, blueprint, inputs) { // Create deployment .then (function(result) { - logger.info(req.dcaeReqId, "deploymentId: " + id + " blueprint uploaded"); - // Create deployment - return cfy.createDeployment(req, id, id, inputs); + logger.info(req.dcaeReqId, "deploymentId: " + id + " blueprint uploaded"); + // Create deployment + return cfy.createDeployment(req, id, id, inputs); }) - // Launch the workflow, but don't wait for it to complete + // create the deployment and keep checking, for up to 5 minutes, until creation is complete .then(function(result){ - logger.info(req.dcaeReqId, "deploymentId: " + id + " deployment created"); - return delay(DELAY_INSTALL_WORKFLOW) - .then(function(){ - return cfy.initiateWorkflowExecution(req, id, 'install'); - }); - }) - .catch(function(error) { - logger.info(req.dcaeReqId, "Error: " + JSON.stringify(error) + " for launch blueprint for deploymentId " + id); - throw normalizeError(error); - }); + return cfy.getDeploymentCreationResult(req, id); + }) + .then(function(){ + logger.info(req.dcaeReqId, "deploymentId: " + id + " deployment created"); + return cfy.initiateWorkflowExecution(req, id, 'install'); + }) + .catch(function(error) { + logger.info(req.dcaeReqId, "Error: " + JSON.stringify(error) + " for launch blueprint/deployment creation for deploymentId " + id); + throw normalizeError(error); + }); }; exports.launchBlueprint = launchBlueprint; |