aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Shatov <alexs@att.com>2018-12-06 10:16:43 -0500
committerAlex Shatov <alexs@att.com>2018-12-06 10:16:43 -0500
commitca00a932eae5e706f01519612fce1015c9ff9d58 (patch)
tree796210ad1dfb8e4e717c06264f01fb64faa0fda6
parent84d5646b3f0250079c386ed2e1eb9fd7d551004e (diff)
3.1.0/5.1.0 - check for finished deployment
- external version 3.1.0 - internal version 5.1.0 for code change - no API change - check for finished deployment creation - success or failure - stop querying cloudify manager on failed deployment creation - use optional $CONSUL_URL to get url of consul - improved info used for audit and logging and responses - added more unit tests unit test coverage summary Statements : 80.99% ( 946/1168 ) Branches : 58.22% ( 294/505 ) Functions : 81.28% ( 165/203 ) Lines : 81.53% ( 936/1148 ) Change-Id: I831cd0db0d2e148e6da4c9190495aacf72e2d39c Signed-off-by: Alex Shatov <alexs@att.com> Issue-ID: DCAEGEN2-929
-rw-r--r--lib/cloudify.js268
-rw-r--r--lib/config.js7
-rw-r--r--lib/consul.js2
-rw-r--r--lib/deploy.js88
-rw-r--r--lib/policy.js36
-rw-r--r--package.json2
-rw-r--r--pom.xml2
-rw-r--r--tests/test_dcae-deployments.js71
-rw-r--r--tests/test_policy.js34
-rw-r--r--version.properties4
10 files changed, 269 insertions, 245 deletions
diff --git a/lib/cloudify.js b/lib/cloudify.js
index 5c5c658..138b986 100644
--- a/lib/cloudify.js
+++ b/lib/cloudify.js
@@ -19,8 +19,7 @@ See the License for the specific language governing permissions and limitations
"use strict";
const CLOUDIFY = "cloudify-manager";
-const FINISHED = [ "terminated", "cancelled", "failed" ];
-const DEPLOYMENT_CREATION_FINISHED = [ "terminated" ];
+const FINISHED = [ "terminated", "cancelled", "canceled", "failed" ];
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
@@ -71,8 +70,8 @@ const exeQueue = new ExeQueue();
exports.exeQueue = exeQueue;
// Get current status of a workflow execution
-const getExecutionStatus = function(req, execution_id) {
- var reqOptions = {
+exports.getExecutionStatus = function(req, execution_id) {
+ const reqOptions = {
method : "GET",
uri : cfyAPI + "/executions/" + execution_id
};
@@ -84,7 +83,7 @@ const getExecutionStatus = function(req, execution_id) {
// Get current status of a deployment creation
const getDeploymentCreationStatus = function(req, deployment_id) {
- var reqOptions = {
+ const reqOptions = {
method : "GET",
uri : cfyAPI + "/executions?deployment_id=" + deployment_id + "&workflow_id=create_deployment_environment&_include=id,status"
};
@@ -94,77 +93,29 @@ const getDeploymentCreationStatus = function(req, deployment_id) {
};
// Poll for the result of a workflow execution until it's done
-const getWorkflowResult = function(mainReq, execution_id) {
+// Return a promise for the final result of a workflow execution
+exports.waitForWorkflowExecution = function(mainReq, execution_id) {
/* Defense: Some callers do not supply mainReq */
mainReq = mainReq || {};
- logger.info(mainReq.dcaeReqId, "Getting workflow result for execution id: " + execution_id);
-
- // Function for testing if workflow is finished
- // Expects the result of getExecStatus
- var checkStatus = function(res) {
- logger.info(mainReq.dcaeReqId, "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;
- };
-
- // Create execution status checker function
- var getExecStatus = function() {return getExecutionStatus(mainReq, execution_id);};
-
- return repeat.repeatWhile(getExecStatus, checkStatus, MAX_TRIES, RETRY_INTERVAL)
- .then(
-
- /* Handle fulfilled promise from repeatWhile */
- function(res) {
-
- logger.info(mainReq.dcaeReqId, '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;
-
- });
+ const log_title = "execution_id(" + execution_id + "): workflow execution";
+ logger.info(mainReq.dcaeReqId, log_title + ": waiting for completion");
+
+ const getStatus = function(res) {return res && res.json && res.json.status;};
+
+ return repeat.repeatWhile(function() {return exports.getExecutionStatus(mainReq, execution_id);},
+ function(res) {return checkExecutionRunning(mainReq, res, log_title, getStatus);},
+ MAX_TRIES, RETRY_INTERVAL)
+ .then(function (res) {return onFinishedExecution(mainReq, res, log_title, getStatus);},
+ /* 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;
+ });
};
// bare start of a workflow execution against a deployment
@@ -172,7 +123,7 @@ const startWorkflowExecution = function(mainReq, deployment_id, workflow_id, par
/* Defense: Some callers do not supply mainReq */
mainReq = mainReq || {};
// Set up the HTTP POST request
- var reqOptions = {
+ const reqOptions = {
method : "POST",
uri : cfyAPI + "/executions",
headers : {
@@ -183,7 +134,7 @@ const startWorkflowExecution = function(mainReq, deployment_id, workflow_id, par
addAuthToOptions(reqOptions, mainReq);
- var body = {
+ const body = {
"deployment_id" : deployment_id,
"workflow_id" : workflow_id
};
@@ -194,7 +145,7 @@ const startWorkflowExecution = function(mainReq, deployment_id, workflow_id, par
};
//Initiate a workflow execution against a deployment
-const initiateWorkflowExecution = function(req, deployment_id, workflow_id, parameters) {
+exports.initiateWorkflowExecution = function(req, deployment_id, workflow_id, parameters) {
return startWorkflowExecution(req, deployment_id, workflow_id, parameters)
.then(function(result) {
logger.info(req.dcaeReqId, "Result from POSTing workflow execution start: " + JSON.stringify(result));
@@ -208,81 +159,70 @@ const initiateWorkflowExecution = function(req, deployment_id, workflow_id, para
});
};
+// Function for testing if workflow execution or deployment creation has finished or still running
+// Expects the result of getExecStatus
+const checkExecutionRunning = function(mainReq, res, log_title, getStatus) {
+ const still_running = !FINISHED.includes(getStatus(res));
+ logger.info(mainReq.dcaeReqId, log_title + ": checking status: " + JSON.stringify(res) + " ==> " + still_running);
+ return still_running;
+};
+
+const onFinishedExecution = function(mainReq, res, log_title, getStatus) {
+ logger.info(mainReq.dcaeReqId, log_title + " result: " + JSON.stringify(res));
+ const status = getStatus(res);
+ /* Successful completion */
+ if (status === 'terminated') {
+ logger.info(mainReq.dcaeReqId, log_title + ' completed');
+ return res;
+ }
+ /* If we get here, we don't have a success and we're going to throw something */
+ const error = { "body": log_title + " " + status };
+ /* We expect a JSON object with a status */
+ if (status) {
+ /* Failure -- we need to return something that looks like the CM API failures */
+ if (status === 'failed') {
+ error.body += ' -- ' + (res.json.error ? JSON.stringify(res.json.error) : 'no error information');
+ }
+ /* Cancellation -- don't really expect this */
+ else if (status === 'canceled' || status === 'cancelled') { }
+ /* Don't expect anything else -- but if we get it, it's not a success! */
+ else {
+ error.body += ' -- unexpected status';
+ }
+ }
+ /* The body of the response from the API call to get execution status is not what we expect at all */
+ else {
+ error.body += ' -- unexpected result body from Cloudify Manager';
+ }
+ throw error;
+};
+
// Poll for the deployment creation status
-const getDeploymentCreationResult = function(mainReq, deployment_id) {
+const waitForDeploymentCreation = 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');
- }
+ const log_title = "deployment_id(" + deployment_id + "): deployment creation";
+ logger.info(mainReq.dcaeReqId, log_title + ": waiting for completion");
- /* 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;
+ const getStatus = function(res) {
+ return res && res.json && Array.isArray(res.json.items)
+ && res.json.items.length == 1 && res.json.items[0].status;
+ };
- });
+ return repeat.repeatWhile(function() {return getDeploymentCreationStatus(mainReq, deployment_id);},
+ function(res) {return checkExecutionRunning(mainReq, res, log_title, getStatus);},
+ DEP_CREATION_STATUS_MAX_TRIES, DEP_CREATION_STATUS_RETRY_INTERVAL)
+ .then(function (res) {return onFinishedExecution(mainReq, res, log_title, getStatus);},
+ /* 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
@@ -314,7 +254,7 @@ exports.uploadBlueprint = function(req, bpid, blueprint) {
exports.createDeployment = function(req, dpid, bpid, inputs) {
// Set up the HTTP PUT request
- var reqOptions = {
+ const reqOptions = {
method : "PUT",
uri : cfyAPI + "/deployments/" + dpid,
headers : {
@@ -324,7 +264,7 @@ exports.createDeployment = function(req, dpid, bpid, inputs) {
};
addAuthToOptions(reqOptions, req);
- var body = {
+ const body = {
blueprint_id : bpid
};
if (inputs) {
@@ -335,35 +275,23 @@ exports.createDeployment = function(req, dpid, bpid, inputs) {
return doRequest(req, reqOptions, JSON.stringify(body), CLOUDIFY);
};
-// Initiate a workflow execution against a deployment
-exports.initiateWorkflowExecution = initiateWorkflowExecution;
-
-// Get the status of a workflow execution
-exports.getWorkflowExecutionStatus = getExecutionStatus;
-
-// 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(req, deployment_id, workflow_id, parameters) {
- return initiateWorkflowExecution(req, deployment_id, workflow_id, parameters)
+ return exports.initiateWorkflowExecution(req, deployment_id, workflow_id, parameters)
// Wait for the result
.then (function(result) {
logger.info(req.dcaeReqId, "Result from initiating workflow: " + JSON.stringify(result));
- return getWorkflowResult(req, result.executionId);
+ return exports.waitForWorkflowExecution(req, result.executionId);
});
};
// Return a promise for the final result of a deployment update
-exports.getDeploymentCreationResult = getDeploymentCreationResult;
-
-// Get the status of a deployment update
-exports.getDeploymentCreationStatus = getDeploymentCreationStatus;
+exports.waitForDeploymentCreation = waitForDeploymentCreation;
// Retrieves outputs for a deployment
exports.getOutputs = function(req, dpid) {
- var reqOptions = {
+ const reqOptions = {
method : "GET",
uri : cfyAPI + "/deployments/" + dpid + "/outputs",
headers : {
@@ -378,7 +306,7 @@ exports.getOutputs = function(req, dpid) {
// Get the output descriptions for a deployment
exports.getOutputDescriptions = function(req, dpid) {
- var reqOptions = {
+ const reqOptions = {
method : "GET",
uri : cfyAPI + "/deployments/" + dpid + "?include=outputs",
headers : {
@@ -393,7 +321,7 @@ exports.getOutputDescriptions = function(req, dpid) {
// Deletes a deployment
exports.deleteDeployment = function(req, dpid) {
- var reqOptions = {
+ const reqOptions = {
method : "DELETE",
uri : cfyAPI + "/deployments/" + dpid
};
@@ -405,7 +333,7 @@ exports.deleteDeployment = function(req, dpid) {
// Deletes a blueprint
exports.deleteBlueprint = function(req, bpid) {
- var reqOptions = {
+ const reqOptions = {
method : "DELETE",
uri : cfyAPI + "/blueprints/" + bpid
};
@@ -444,7 +372,7 @@ exports.setLogger = function(log) {
exports.getNodeInstances = function (mainReq, on_next_node_instances, offset) {
offset = offset || 0;
- var reqOptions = {
+ const reqOptions = {
method : "GET",
uri : cfyAPI + "/node-instances?_include=id,deployment_id,runtime_properties&_size=1000&_offset=" + offset
};
@@ -506,7 +434,7 @@ const runQueuedExecution = function(mainReq, deployment_id, workflow_id, paramet
553, "api", 553, CLOUDIFY);
}
exeQueue.setExecutionId(deployment_id, execution_id);
- return getWorkflowResult(mainReq, execution_id);
+ return exports.waitForWorkflowExecution(mainReq, execution_id);
})
.then(function(result) {
logger.info(mainReq.dcaeReqId, 'successfully finished execution: ' + execution_id + " for" + exe_deployment_str);
diff --git a/lib/config.js b/lib/config.js
index 8daa87f..d4fd3e3 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -19,8 +19,11 @@ See the License for the specific language governing permissions and limitations
* Configuration may come from environment variables, a value in a Consul key-value store, or defaults,
* in that order of precedence.
*
- * The address of the Consul host is passed in an environment variable called CONSUL_HOST.
- * If present, the configuration value in the key-value store is a UTF-8 serialization of a JSON object.
+ * The url of the Consul is passed in an optional environment variable called CONSUL_URL.
+ * If $CONSUL_URL not provided, the deployment-handler looks at the Consul host that can be passed
+ * in as an environment variable called CONSUL_HOST, that corresponds to url "http://${CONSUL_HOST}:8500".
+ * If $CONSUL_HOST not provided as well, the consul url is "http://consul:8500".
+ * If present, the configuration value in the consul-key-value store is a UTF-8 serialization of a JSON object.
*
*
* --------------------------------------------------------------------------------------
diff --git a/lib/consul.js b/lib/consul.js
index 40de84b..a190093 100644
--- a/lib/consul.js
+++ b/lib/consul.js
@@ -19,7 +19,7 @@ See the License for the specific language governing permissions and limitations
const KEY = '/v1/kv/';
const SERVICE = '/v1/catalog/service/';
const CONSUL = 'consul';
-const CONSUL_URL = 'http://' + (process.env.CONSUL_HOST || CONSUL) + ':8500';
+const CONSUL_URL = process.env.CONSUL_URL || ('http://' + (process.env.CONSUL_HOST || CONSUL) + ':8500');
const doRequest = require('./promise_request').doRequest;
diff --git a/lib/deploy.js b/lib/deploy.js
index 2d75b52..4829040 100644
--- a/lib/deploy.js
+++ b/lib/deploy.js
@@ -21,7 +21,6 @@ See the License for the specific language governing permissions and limitations
const config = process.mainModule.exports.config;
/* Set delays between steps */
-const DELAY_INSTALL_WORKFLOW = 30000;
const DELAY_RETRIEVE_OUTPUTS = 5000;
const DELAY_DELETE_DEPLOYMENT = 30000;
const DELAY_DELETE_BLUEPRINT = 10000;
@@ -43,9 +42,9 @@ cfy.setLogger(logger);
// Try to parse a string as JSON
var parseContent = function(input) {
- var res = {json: false, content: input};
+ const res = {json: false, content: input};
try {
- var parsed = JSON.parse(input);
+ const parsed = JSON.parse(input);
res.json = true;
res.content = parsed;
}
@@ -96,8 +95,7 @@ var normalizeError = function (err) {
// Augment the raw outputs from a deployment with the descriptions from the blueprint
var annotateOutputs = function (req, id, rawOutputs) {
return new Promise(function(resolve, reject) {
-
- var outItems = Object.keys(rawOutputs);
+ const outItems = Object.keys(rawOutputs);
if (outItems.length < 1) {
// No output items, so obviously no descriptions, just return empty object
@@ -137,37 +135,43 @@ var delay = function(dtime) {
// 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(req, id, blueprint, inputs) {
- logger.info(req.dcaeReqId, "deploymentId: " + id + " starting blueprint upload");
- // Upload blueprint
+exports.launchBlueprint = function(req, id, blueprint, inputs) {
+ const log_deployment_id = "deploymentId(" + id + "): ";
+ var step_log = log_deployment_id + "uploading blueprint";
+ logger.info(req.dcaeReqId, step_log);
return cfy.uploadBlueprint(req, id, blueprint)
- // Create deployment
.then (function(result) {
- logger.info(req.dcaeReqId, "deploymentId: " + id + " blueprint uploaded");
- // Create deployment
- return cfy.createDeployment(req, id, id, inputs);
+ step_log = log_deployment_id + "creating deployment";
+ logger.info(req.dcaeReqId, step_log);
+ // Create deployment
+ return cfy.createDeployment(req, id, id, inputs);
})
// create the deployment and keep checking, for up to 5 minutes, until creation is complete
- .then(function(result){
- 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);
- });
+ .then(function(result) {
+ step_log = log_deployment_id + "waiting for deployment creation";
+ logger.info(req.dcaeReqId, step_log);
+ return cfy.waitForDeploymentCreation(req, id);
+ })
+ .then(function() {
+ step_log = log_deployment_id + "install";
+ logger.info(req.dcaeReqId, step_log);
+ return cfy.initiateWorkflowExecution(req, id, 'install');
+ })
+ .catch(function(error) {
+ step_log = " while " + step_log;
+ logger.info(req.dcaeReqId, "Error: " + JSON.stringify(error) + step_log);
+ error.message = (error.message && (step_log + ": " + error.message))
+ || ("failed: " + step_log);
+ throw normalizeError(error);
+ });
};
-exports.launchBlueprint = launchBlueprint;
// Finish installation launched with launchBlueprint
-const finishInstallation = function(req, deploymentId, executionId) {
+exports.finishInstallation = function(req, deploymentId, executionId) {
logger.info(req.dcaeReqId, "finishInstallation: " + deploymentId + " -- executionId: " + executionId);
- return cfy.getWorkflowResult(req, executionId)
+ return cfy.waitForWorkflowExecution(req, executionId)
.then (function(result){
logger.info(req.dcaeReqId, "deploymentId: " + deploymentId + " install workflow successfully executed");
// Retrieve the outputs from the deployment, as specified in the blueprint
@@ -194,10 +198,9 @@ const finishInstallation = function(req, deploymentId, executionId) {
throw normalizeError(err);
});
};
-exports.finishInstallation = finishInstallation;
// Initiate uninstall workflow against a deployment, but don't wait for workflow to finish
-const launchUninstall = function(req, deploymentId) {
+exports.launchUninstall = function(req, deploymentId) {
logger.info(req.dcaeReqId, "deploymentId: " + deploymentId + " starting uninstall workflow");
// Run uninstall workflow
return cfy.initiateWorkflowExecution(req, deploymentId, 'uninstall')
@@ -209,11 +212,10 @@ const launchUninstall = function(req, deploymentId) {
throw normalizeError(err);
});
};
-exports.launchUninstall = launchUninstall;
-const finishUninstall = function(req, deploymentId, executionId) {
+exports.finishUninstall = function(req, deploymentId, executionId) {
logger.info(req.dcaeReqId, "finishUninstall: " + deploymentId + " -- executionId: " + executionId);
- return cfy.getWorkflowResult(req, executionId)
+ return cfy.waitForWorkflowExecution(req, executionId)
.then (function(result){
logger.info(req.dcaeReqId, "deploymentId: " + deploymentId + " uninstall workflow successfully executed");
// Delete the deployment
@@ -236,11 +238,10 @@ const finishUninstall = function(req, deploymentId, executionId) {
});
};
-exports.finishUninstall = finishUninstall;
// Get the status of a workflow execution
-exports.getExecutionStatus = function (req, exid) {
- return cfy.getWorkflowExecutionStatus(req, exid)
+exports.getExecutionStatus = function (req, execution_id) {
+ return cfy.getExecutionStatus(req, execution_id)
.then(function(res){
var result = {
@@ -276,17 +277,12 @@ exports.getExecutionStatus = function (req, exid) {
exports.deployBlueprint = function(req, id, blueprint, inputs) {
// Upload blueprint, create deployment, and initiate install workflow
- return launchBlueprint(req, id, blueprint, inputs)
+ return exports.launchBlueprint(req, id, blueprint, inputs)
// Wait for the workflow to complete
- .then(
-
- // launchBlueprint promise fulfilled -- finish installation
- function(result){
- return finishInstallation(req, result.deploymentId, result.executionId); // Will throw normalized error if it fails
+ .then(function(result){
+ return exports.finishInstallation(req, result.deploymentId, result.executionId); // Will throw normalized error if it fails
},
-
- // launchBlueprint promise rejected -- report error
function(err) {
throw normalizeError(err);
});
@@ -297,14 +293,10 @@ exports.undeployDeployment = function(req, id) {
logger.info(req.dcaeReqId, "deploymentId: " + id + " starting uninstall workflow");
// Run launch uninstall workflow
- return launchUninstall(req, id)
-
- // launchUninstall promise fulfilled -- finish uninstall
+ return exports.launchUninstall(req, id)
.then (function(result){
- return finishUninstall(req, result.deploymentId, result.executionId); // Will throw normalized error if it fails
+ return exports.finishUninstall(req, result.deploymentId, result.executionId); // Will throw normalized error if it fails
},
-
- // launchUninstall promise rejected -- report error
function(err){
throw normalizeError(err);
});
diff --git a/lib/policy.js b/lib/policy.js
index d6a701f..098a4a4 100644
--- a/lib/policy.js
+++ b/lib/policy.js
@@ -222,7 +222,7 @@ function update_policies(req, res) {
* retrieve the unique set of policies and policy-filters from cloudify
*/
function get_policies_from_cloudify(req, res, next) {
- logger.info(req.dcaeReqId, "getPoliciesFromCloudify " + req.originalUrl);
+ logger.info(req.dcaeReqId, "get_policies_from_cloudify " + req.originalUrl);
const response = {"requestID": req.dcaeReqId};
response.started = new Date();
response.server_instance_uuid = process.mainModule.exports.config.server_instance_uuid;
@@ -263,13 +263,16 @@ function get_policies_from_cloudify(req, res, next) {
.then(function(result) {
response.ended = new Date();
response.status = result.status;
- response.message = result.message;
- logger.info(req.dcaeReqId, result.message);
- if (result.status !== 200) {
- logger.error(createError(result.message, result.status, "api", 502, 'cloudify-manager'), req);
+ response.message = result.message
+ + " deployed policies[" + Object.keys(response.policies).length
+ + "] policy_filters[" + Object.keys(response.policy_filters).length + "]";
+ logger.info(req.dcaeReqId, "response status " + response.status
+ + " body: " + JSON.stringify(response));
+ if (response.status !== 200) {
+ logger.error(createError(response.message, response.status, "api", 502, 'cloudify-manager'), req);
}
- res.status(result.status).json(response);
- logger.audit(req, result.status, result.message);
+ res.status(response.status).json(response);
+ logger.audit(req, response.status, response.message);
});
}
@@ -335,13 +338,20 @@ function getComponentPoliciesFromCloudify(req, res, next) {
.then(function(result) {
response.ended = new Date();
response.status = result.status;
- response.message = result.message;
- logger.info(req.dcaeReqId, result.message);
- if (result.status !== 200) {
- logger.error(createError(result.message, result.status, "api", 502, 'cloudify-manager'), req);
+ response.message = result.message
+ response.message = result.message
+ + " collected[" + response.node_instance_ids.length
+ + "] node_instance_ids[" + Object.keys(response.node_instance_ids).length
+ + "] component_policies[" + Object.keys(response.component_policies).length
+ + "] component_policy_filters[" + Object.keys(response.component_policy_filters).length + "]";
+
+ logger.info(req.dcaeReqId, "response status " + response.status
+ + " body: " + JSON.stringify(response));
+ if (response.status !== 200) {
+ logger.error(createError(response.message, response.status, "api", 502, 'cloudify-manager'), req);
}
- res.status(result.status).json(response);
- logger.audit(req, result.status, result.message);
+ res.status(response.status).json(response);
+ logger.audit(req, response.status, response.message);
});
}
diff --git a/package.json b/package.json
index 1cfc046..2c5f677 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "onap-dcae-deployment-handler",
- "version": "5.0.3",
+ "version": "5.1.0",
"description": "ONAP DCAE Deployment Handler",
"main": "deployment-handler.js",
"dependencies": {
diff --git a/pom.xml b/pom.xml
index f8be698..6c2dcca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property.
<groupId>org.onap.dcaegen2.platform</groupId>
<artifactId>deployment-handler</artifactId>
<name>dcaegen2-platform-deployment-handler</name>
- <version>3.0.3-SNAPSHOT</version>
+ <version>3.1.0-SNAPSHOT</version>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
diff --git a/tests/test_dcae-deployments.js b/tests/test_dcae-deployments.js
index 367a38d..7bf9831 100644
--- a/tests/test_dcae-deployments.js
+++ b/tests/test_dcae-deployments.js
@@ -174,19 +174,19 @@ const Cloudify = {
"blueprint_id": blueprint_id || deployment_id
};
},
- resp_dep_creation: function(deployment_id) {
+ resp_dep_creation: function(deployment_id, execution_id, status) {
return {
"items": [
{
- "status": "terminated",
- "id": "ee6b0d21-0257-46a3-bb83-6f61f9ab5f99"
+ "status": (status || "terminated"),
+ "id": (execution_id || "ee6b0d21-0257-46a3-bb83-6f61f9ab5f99")
}
],
"metadata": {
"pagination": {
"total": 1,
"offset": 0,
- "size": 10000
+ "size": 1000
}
}
};
@@ -385,6 +385,66 @@ function test_put_dcae_deployments_missing_input_error(dh_server) {
});
}
+function test_put_dcae_deployments_creation_failed(dh_server) {
+ const req_path = "/dcae-deployments/" + DEPLOYMENT_ID_JFL_1;
+ const message = create_main_message(INV_EXISTING_SERVICE_TYPE, true);
+ const test_txt = "fail deployment-creation PUT " + req_path + ": " + JSON.stringify(message);
+ const execution_id = "execution_" + DEPLOYMENT_ID_JFL_1;
+ describe(test_txt, () => {
+ it('fail deployment-creation', function(done) {
+ const action_timer = new utils.ActionTimer();
+ console.log(action_timer.step, test_txt);
+
+ nock(dh.INVENTORY_URL).get(INV_PATH_DCAE_SERVICES + "/" + DEPLOYMENT_ID_JFL_1)
+ .reply(404, function(uri) {
+ console.log(action_timer.step, "get", dh.INVENTORY_URL, uri);
+ return JSON.stringify(Inventory.resp_not_found_service(DEPLOYMENT_ID_JFL_1));
+ });
+ nock(dh.INVENTORY_URL).get(INV_PATH_DCAE_SERVICE_TYPES + INV_EXISTING_SERVICE_TYPE)
+ .reply(200, function(uri) {
+ console.log(action_timer.step, "get", dh.INVENTORY_URL, uri);
+ return JSON.stringify(Inventory.resp_existing_blueprint(INV_EXISTING_SERVICE_TYPE));
+ });
+ nock(dh.INVENTORY_URL).put(INV_PATH_DCAE_SERVICES + "/" + DEPLOYMENT_ID_JFL_1)
+ .reply(200, function(uri, requestBody) {
+ console.log(action_timer.step, "put", dh.INVENTORY_URL, uri, JSON.stringify(requestBody));
+ return JSON.stringify(Inventory.resp_put_service(DEPLOYMENT_ID_JFL_1, INV_EXISTING_SERVICE_TYPE));
+ });
+
+ nock(dh.CLOUDIFY_URL).put("/api/v2.1/blueprints/" + DEPLOYMENT_ID_JFL_1)
+ .reply(200, function(uri, requestBody) {
+ console.log(action_timer.step, "put", dh.CLOUDIFY_URL, uri, JSON.stringify(requestBody));
+ return JSON.stringify(Cloudify.resp_blueprint(DEPLOYMENT_ID_JFL_1));
+ });
+
+ nock(dh.CLOUDIFY_URL).put("/api/v2.1/deployments/" + DEPLOYMENT_ID_JFL_1)
+ .reply(201, function(uri, requestBody) {
+ console.log(action_timer.step, "put", dh.CLOUDIFY_URL, uri, JSON.stringify(requestBody));
+ return JSON.stringify(Cloudify.resp_deploy(DEPLOYMENT_ID_JFL_1, DEPLOYMENT_ID_JFL_1, message.inputs));
+ });
+
+ nock(dh.CLOUDIFY_URL).get("/api/v2.1/executions?deployment_id=" + DEPLOYMENT_ID_JFL_1 + "&workflow_id=create_deployment_environment&_include=id,status")
+ .reply(200, function(uri) {
+ console.log(action_timer.step, "get", dh.CLOUDIFY_URL, uri);
+ return JSON.stringify(Cloudify.resp_dep_creation(DEPLOYMENT_ID_JFL_1, execution_id, "failed"));
+ });
+
+ chai.request(dh_server.app).put(req_path)
+ .set('content-type', 'application/json')
+ .send(message)
+ .end(function(err, res) {
+ console.log(action_timer.step, "res for", test_txt, res.text);
+ expect(res).to.have.status(500);
+ expect(res.body).to.have.property('message');
+ expect(res.body.message).to.be.equal(
+ 'Status 502 from CM API -- error code: UNKNOWN -- message: deployment_id('
+ + DEPLOYMENT_ID_JFL_1 + '): deployment creation failed -- no error information');
+ done();
+ });
+ }).timeout(50000);
+ });
+}
+
function test_put_dcae_deployments_success(dh_server) {
const req_path = "/dcae-deployments/" + DEPLOYMENT_ID_JFL_1;
const message = create_main_message(INV_EXISTING_SERVICE_TYPE, true);
@@ -426,7 +486,7 @@ function test_put_dcae_deployments_success(dh_server) {
nock(dh.CLOUDIFY_URL).get("/api/v2.1/executions?deployment_id=" + DEPLOYMENT_ID_JFL_1 + "&workflow_id=create_deployment_environment&_include=id,status")
.reply(200, function(uri) {
console.log(action_timer.step, "get", dh.CLOUDIFY_URL, uri);
- return JSON.stringify(Cloudify.resp_dep_creation(DEPLOYMENT_ID_JFL_1));
+ return JSON.stringify(Cloudify.resp_dep_creation(DEPLOYMENT_ID_JFL_1, execution_id));
});
nock(dh.CLOUDIFY_URL).post("/api/v2.1/executions")
@@ -651,6 +711,7 @@ dh.add_tests([
test_put_dcae_deployments_missing_input_error,
test_get_dcae_deployments_operation,
test_get_dcae_deployments_service_type_deployed,
+ test_put_dcae_deployments_creation_failed,
test_put_dcae_deployments_success,
test_delete_dcae_deployments_success
]);
diff --git a/tests/test_policy.js b/tests/test_policy.js
index bc424e0..32e5ed4 100644
--- a/tests/test_policy.js
+++ b/tests/test_policy.js
@@ -211,7 +211,13 @@ const cloudify_node_instances = [
];
function nock_cfy_node_instances(action_timer) {
- nock(dh.CLOUDIFY_URL).get(CFY_API_NODE_INSTANCES).query(true)
+ // "/node-instances?_include=id,deployment_id,runtime_properties&_size=1000&_offset=0"
+ nock(dh.CLOUDIFY_URL).get(CFY_API_NODE_INSTANCES)
+ .query(params => {
+ console.log(action_timer.step, "get", dh.CLOUDIFY_URL, CFY_API_NODE_INSTANCES, JSON.stringify(params));
+ return !!(params._include === "id,deployment_id,runtime_properties"
+ && params._size === "1000" && params._offset === "0");
+ })
.reply(200, function(uri) {
console.log(action_timer.step, "get", dh.CLOUDIFY_URL, uri);
return JSON.stringify({
@@ -221,6 +227,29 @@ function nock_cfy_node_instances(action_timer) {
});
}
+function test_get_policy(dh_server) {
+ const req_path = "/policy";
+ const test_txt = "GET " + req_path;
+ describe(test_txt, () => {
+ it('GET all the policies and policy-filters from cloudify', function() {
+ const action_timer = new utils.ActionTimer();
+ console.log(action_timer.step, test_txt);
+ nock_cfy_node_instances(action_timer);
+
+ return chai.request(dh_server.app).get(req_path)
+ .then(function(res) {
+ console.log(action_timer.step, "res for", test_txt, res.text);
+ expect(res).to.have.status(200);
+ expect(res).to.be.json;
+ })
+ .catch(function(err) {
+ console.error(action_timer.step, "err for", test_txt, err);
+ throw err;
+ });
+ });
+ });
+}
+
function test_get_policy_components(dh_server) {
const req_path = "/policy/components";
const test_txt = "GET " + req_path;
@@ -309,7 +338,7 @@ function test_put_policy_catch_up(dh_server) {
return JSON.stringify(resp_to_exe);
});
- for (var extra_i = 1; extra_i <= 100000; extra_i++) {
+ for (var extra_i = 1; extra_i <= 10000; extra_i++) {
const policy_id = "extra_" + extra_i;
message.latest_policies[policy_id] = create_policy(policy_id, extra_i);
}
@@ -474,6 +503,7 @@ function test_fail_404_cfy_policy_catch_up(dh_server) {
}
dh.add_tests([
+ test_get_policy,
test_get_policy_components,
test_put_policy_catch_up,
test_fail_cfy_policy_catch_up,
diff --git a/version.properties b/version.properties
index 0453b60..decdfc4 100644
--- a/version.properties
+++ b/version.properties
@@ -1,6 +1,6 @@
major=3
-minor=0
-patch=3
+minor=1
+patch=0
base_version=${major}.${minor}.${patch}
release_version=${base_version}
snapshot_version=${base_version}-SNAPSHOT