aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorShadi Haidar <sh1986@att.com>2018-09-07 22:05:43 -0400
committerShadi Haidar <sh1986@att.com>2018-09-12 13:58:04 -0400
commitceda84d021dde70299f96984ca7aec16740854be (patch)
tree967b1c37a9e21721240d68e9b81dc59a0a593512 /lib
parent8ca8ddf40decb543e5ea634e29efe6972272a2f6 (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.js97
-rw-r--r--lib/deploy.js28
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;