From 4e30c82b172cf422ab5179e3c566ef01ca14cb3a Mon Sep 17 00:00:00 2001 From: Alex Shatov Date: Wed, 24 Jan 2018 13:35:28 -0500 Subject: unit tests coverage 68% and more info in audit * refactored the unit test - simpler dh server initialization * new unit tests for the dcae-deployments * new unit tests for healthcheck - info * new server_instance_uuid - unique per deployment-handler instance for logging and info * dragging req object over the stack to show req data in logging and audit and metrics * new feature variable collection of policies per component in DCAE Change-Id: I8388d7e5e11e3a6c871cf3d507bd8a07b09add29 Issue-ID: DCAEGEN2-249 Signed-off-by: Alex Shatov --- lib/cloudify.js | 58 ++++++++++--------- lib/config.js | 5 +- lib/consul.js | 6 +- lib/dcae-deployments.js | 107 ++++++++++++++++++----------------- lib/deploy.js | 144 +++++++++++++++++++++++++----------------------- lib/info.js | 40 +++++++------- lib/inventory.js | 24 ++++---- lib/logging.js | 4 +- lib/policy.js | 9 ++- lib/promise_request.js | 14 +++-- 10 files changed, 215 insertions(+), 196 deletions(-) (limited to 'lib') diff --git a/lib/cloudify.js b/lib/cloudify.js index 23e779a..b03ecac 100644 --- a/lib/cloudify.js +++ b/lib/cloudify.js @@ -1,5 +1,5 @@ /* -Copyright(c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -75,19 +75,17 @@ var delay = function(dtime) { }; // Get current status of a workflow execution -const getExecutionStatus = function(execution_id, mainReq) { - /* Defense: Some callers do not supply mainReq */ - mainReq = mainReq || {}; +const getExecutionStatus = function(req, execution_id) { var reqOptions = { method : "GET", uri : cfyAPI + "/executions/" + execution_id }; addAuthToOptions(reqOptions); - return doRequest(reqOptions, null, CLOUDIFY, mainReq); + return doRequest(req, reqOptions, null, CLOUDIFY); }; // Poll for the result of a workflow execution until it's done -var getWorkflowResult = function(execution_id, mainReq) { +const getWorkflowResult = function(mainReq, execution_id) { /* Defense: Some callers do not supply mainReq */ mainReq = mainReq || {}; logger.debug(mainReq.dcaeReqId, "Getting workflow result for execution id: " + execution_id); @@ -100,7 +98,7 @@ var getWorkflowResult = function(execution_id, mainReq) { }; // Create execution status checker function - var getExecStatus = function() {return getExecutionStatus(execution_id, mainReq);}; + var getExecStatus = function() {return getExecutionStatus(mainReq, execution_id);}; return repeat.repeatWhile(getExecStatus, checkStatus, MAX_TRIES, RETRY_INTERVAL) .then( @@ -181,18 +179,18 @@ const startWorkflowExecution = function(mainReq, deployment_id, workflow_id, par if (parameters) {body.parameters = parameters;} // Make the POST request - return doRequest(reqOptions, JSON.stringify(body), CLOUDIFY, mainReq); + return doRequest(mainReq, reqOptions, JSON.stringify(body), CLOUDIFY); }; //Initiate a workflow execution against a deployment -const initiateWorkflowExecution = function(deployment_id, workflow_id, parameters) { - return startWorkflowExecution(null, deployment_id, workflow_id, parameters) +const initiateWorkflowExecution = function(req, deployment_id, workflow_id, parameters) { + return startWorkflowExecution(req, deployment_id, workflow_id, parameters) .then(function(result) { - logger.debug(null, "Result from POSTing workflow execution start: " + JSON.stringify(result)); + logger.debug(req.dcaeReqId, "Result from POSTing workflow execution start: " + JSON.stringify(result)); if (result.json && result.json.id) { return {deploymentId: deployment_id, workflowType: workflow_id, executionId: result.json.id}; } - logger.debug(null,"Did not get expected JSON body from POST to start workflow"); + logger.debug(req.dcaeReqId,"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; throw err; @@ -200,7 +198,7 @@ const initiateWorkflowExecution = function(deployment_id, workflow_id, parameter }; // Uploads a blueprint via the Cloudify API -exports.uploadBlueprint = function(bpid, blueprint) { +exports.uploadBlueprint = function(req, bpid, blueprint) { // Cloudify API wants a gzipped tar of a directory, not the blueprint text var zip = new admzip(); @@ -220,11 +218,11 @@ exports.uploadBlueprint = function(bpid, blueprint) { addAuthToOptions(reqOptions); // Initiate PUT request and return the promise for a result - return doRequest(reqOptions, src, CLOUDIFY); + return doRequest(req, reqOptions, src, CLOUDIFY); }; // Creates a deployment from a blueprint -exports.createDeployment = function(dpid, bpid, inputs) { +exports.createDeployment = function(req, dpid, bpid, inputs) { // Set up the HTTP PUT request var reqOptions = { @@ -245,7 +243,7 @@ exports.createDeployment = function(dpid, bpid, inputs) { } // Make the PUT request to create the deployment - return doRequest(reqOptions, JSON.stringify(body), CLOUDIFY); + return doRequest(req, reqOptions, JSON.stringify(body), CLOUDIFY); }; // Initiate a workflow execution against a deployment @@ -258,19 +256,19 @@ exports.getWorkflowExecutionStatus = getExecutionStatus; exports.getWorkflowResult = getWorkflowResult; // Executes a workflow against a deployment and returns a promise for final result -exports.executeWorkflow = function(deployment_id, workflow_id, parameters) { - return initiateWorkflowExecution(deployment_id, workflow_id, parameters) +exports.executeWorkflow = function(req, deployment_id, workflow_id, parameters) { + return initiateWorkflowExecution(req, deployment_id, workflow_id, parameters) // Wait for the result .then (function(result) { - logger.debug(null, "Result from initiating workflow: " + JSON.stringify(result)); - return getWorkflowResult(result.executionId); + logger.debug(req.dcaeReqId, "Result from initiating workflow: " + JSON.stringify(result)); + return getWorkflowResult(req, result.executionId); }); }; // Retrieves outputs for a deployment -exports.getOutputs = function(dpid) { +exports.getOutputs = function(req, dpid) { var reqOptions = { method : "GET", uri : cfyAPI + "/deployments/" + dpid + "/outputs", @@ -280,11 +278,11 @@ exports.getOutputs = function(dpid) { }; addAuthToOptions(reqOptions); - return doRequest(reqOptions, null, CLOUDIFY); + return doRequest(req, reqOptions, null, CLOUDIFY); }; // Get the output descriptions for a deployment -exports.getOutputDescriptions = function(dpid) { +exports.getOutputDescriptions = function(req, dpid) { var reqOptions = { method : "GET", uri : cfyAPI + "/deployments/" + dpid + "?include=outputs", @@ -294,29 +292,29 @@ exports.getOutputDescriptions = function(dpid) { }; addAuthToOptions(reqOptions); - return doRequest(reqOptions, null, CLOUDIFY); + return doRequest(req, reqOptions, null, CLOUDIFY); }; // Deletes a deployment -exports.deleteDeployment = function(dpid) { +exports.deleteDeployment = function(req, dpid) { var reqOptions = { method : "DELETE", uri : cfyAPI + "/deployments/" + dpid }; addAuthToOptions(reqOptions); - return doRequest(reqOptions, null, CLOUDIFY); + return doRequest(req, reqOptions, null, CLOUDIFY); }; // Deletes a blueprint -exports.deleteBlueprint = function(bpid) { +exports.deleteBlueprint = function(req, bpid) { var reqOptions = { method : "DELETE", uri : cfyAPI + "/blueprints/" + bpid }; addAuthToOptions(reqOptions); - return doRequest(reqOptions, null, CLOUDIFY); + return doRequest(req, reqOptions, null, CLOUDIFY); }; // Allow client to set the Cloudify API root address @@ -349,7 +347,7 @@ exports.getNodeInstances = function (mainReq, on_next_node_instances, offset) { addAuthToOptions(reqOptions); logger.debug(mainReq.dcaeReqId, "getNodeInstances: " + JSON.stringify(reqOptions)); - return doRequest(reqOptions, null, CLOUDIFY, mainReq) + return doRequest(mainReq, reqOptions, null, CLOUDIFY) .then(function(cloudify_response) { logger.debug(mainReq.dcaeReqId, "getNodeInstances response: " + JSON.stringify(cloudify_response)); var response = {}; @@ -403,7 +401,7 @@ const runQueuedExecution = function(mainReq, deployment_id, workflow_id, paramet 553, "api", 553, CLOUDIFY); } exeQueue.setExecutionId(deployment_id, execution_id); - return getWorkflowResult(execution_id, mainReq); + return getWorkflowResult(mainReq, execution_id); }) .then(function(result) { logger.debug(mainReq.dcaeReqId, 'successfully finished execution: ' + execution_id + " for" + exe_deployment_str); diff --git a/lib/config.js b/lib/config.js index e44e9b5..b71199c 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,5 +1,5 @@ /* -Copyright(c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -148,7 +148,8 @@ const getTLSCredentials = function() { } exports.configure = function() { - var config = {}; + const config = {}; + config.server_instance_uuid = utils.generateId(); /* Get configuration from configuration store */ return getFileContents(PACKAGE_JSON_FILE) diff --git a/lib/consul.js b/lib/consul.js index 3a3257b..226291f 100644 --- a/lib/consul.js +++ b/lib/consul.js @@ -1,5 +1,5 @@ /* -Copyright(c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ module.exports = { * If there is no such key, resolve to null. */ getKey: function(key) { - return doRequest({method: 'GET', uri: CONSUL_URL + KEY + key + '?raw'}, null, CONSUL) + return doRequest(null, {method: 'GET', uri: CONSUL_URL + KEY + key + '?raw'}, null, CONSUL) .then(function(res) { return res.json || res.body; }) @@ -51,7 +51,7 @@ module.exports = { * If the service is not found, returns a zero-length array. */ getService: function(serviceId) { - return doRequest({method: 'GET', uri: CONSUL_URL + SERVICE + serviceId}, null, CONSUL) + return doRequest(null, {method: 'GET', uri: CONSUL_URL + SERVICE + serviceId}, null, CONSUL) .then(function(res){ return res.json.map(function(r) { /* Address for external service is in r.Address with r.ServiceAddress empty */ diff --git a/lib/dcae-deployments.js b/lib/dcae-deployments.js index 38dc3c4..9c1d918 100644 --- a/lib/dcae-deployments.js +++ b/lib/dcae-deployments.js @@ -1,16 +1,16 @@ /* -Copyright(c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ @@ -36,11 +36,18 @@ const inventory = inv({url: config.inventory.url}); /* Set up middleware stack for initial processing of request */ app.use(middleware.checkType('application/json')); // Validate type app.use(bodyParser.json({strict: true})); // Parse body as JSON +app.use(function(req, res, next) { + log.debug(req.dcaeReqId, + "new req: " + req.method + " " + req.originalUrl + + " from: " + req.ip + " body: " + JSON.stringify(req.body) + ); + next(); +}); /* Return a promise for a blueprint for the given service type ID */ -const getBlueprint = function(serviceTypeId) { - return inventory.getBlueprintByType(serviceTypeId) +const getBlueprint = function(req, serviceTypeId) { + return inventory.getBlueprintByType(req, serviceTypeId) .then(function (blueprintInfo) { if (!blueprintInfo.blueprint) { var e = new Error("No service type with ID " + serviceTypeId); @@ -48,7 +55,7 @@ const getBlueprint = function(serviceTypeId) { throw e; } return blueprintInfo; - }) + }) }; /* Generate self and status links object for responses */ @@ -57,7 +64,7 @@ const createLinks = function(req, deploymentId, executionId) { return { self: baseURL, status: baseURL + '/operation/' + executionId - }; + }; }; /* Generate a success response body for PUT and DELETE operations */ @@ -71,13 +78,11 @@ const createResponse = function(req, result) { /* Look up running (or in process of deploying) instances of the given service type */ app.get('/', function (req, res, next) { var services = [] - - - var searchTerm = {}; + var searchTerm; req.query['serviceTypeId'] && (searchTerm = {typeId: req.query['serviceTypeId']}); - - inventory.getServicesByType(searchTerm) + + inventory.getServicesByType(req, searchTerm) .then(function (result) { var deployments = result.map(function(service){ return { @@ -92,70 +97,68 @@ app.get('/', function (req, res, next) { /* Accept an incoming deployment request */ app.put('/:deploymentId', function(req, res, next) { - - log.debug(req.dcaeReqId, "body: " + JSON.stringify(req.body)); - + /* Make sure there's a serviceTypeId in the body */ if (!req.body['serviceTypeId']) { var e = new Error ('Missing required parameter serviceTypeId'); e.status = 400; throw e; } - + /* Make sure the deploymentId doesn't already exist */ - inventory.verifyUniqueDeploymentId(req.params['deploymentId']) + inventory.verifyUniqueDeploymentId(req, req.params['deploymentId']) /* Get the blueprint for this service type */ .then(function(res) { - return getBlueprint(req.body['serviceTypeId']); + return getBlueprint(req, req.body['serviceTypeId']); }) - - /* Add this new service instance to inventory - * Easier to remove from inventory if deployment fails than vice versa + + /* Add this new service instance to inventory + * Easier to remove from inventory if deployment fails than vice versa * Also lets client check for deployed/deploying instances if client wants to limit number of instances */ .then(function (blueprintInfo) { req.dcaeBlueprint = blueprintInfo.blueprint; - return inventory.addService(req.params['deploymentId'], blueprintInfo.typeId, "dummyVnfId", "dummyVnfType", "dummyLocation"); + return inventory.addService(req, req.params['deploymentId'], blueprintInfo.typeId, "dummyVnfId", "dummyVnfType", "dummyLocation"); }) - + /* Upload blueprint, create deployment and start install workflow (but don't wait for completion */ .then (function() { req.dcaeAddedToInventory = true; - return deploy.launchBlueprint(req.params['deploymentId'], req.dcaeBlueprint, req.body['inputs']); + return deploy.launchBlueprint(req, req.params['deploymentId'], req.dcaeBlueprint, req.body['inputs']); }) - + /* Send the HTTP response indicating workflow has started */ .then(function(result) { res.status(202).json(createResponse(req, result)); log.audit(req, 202, "Execution ID: " + result.executionId); return result; }) - + /* Finish deployment--wait for the install workflow to complete, retrieve and annotate outputs */ .then(function(result) { - return deploy.finishInstallation(result.deploymentId, result.executionId); + return deploy.finishInstallation(req, result.deploymentId, result.executionId); }) - + /* Log completion in audit log */ .then (function(result) { log.audit(req, 200, "Deployed id: " + req.params['deploymentId']); }) - + /* All errors show up here */ - .catch(function(error) { - + .catch(function(error) { + /* If we haven't already sent a response, let the error handler send response and log the error */ if (!res.headersSent) { - + /* If we made an inventory entry, remove it */ if (req.dcaeAddedToInventory) { - inventory.deleteService(req.params['deploymentId']) + inventory.deleteService(req, req.params['deploymentId']) .catch(function(error) { log.error(error, req); }); } - + next(error); } else { @@ -164,46 +167,46 @@ app.put('/:deploymentId', function(req, res, next) { error.message = "Error deploying deploymentId " + req.params['deploymentId'] + ": " + error.message log.error(error, req); log.audit(req, 500, error.message); - } + } }); }); /* Delete a running service instance */ app.delete('/:deploymentId', function(req, res, next) { - + /* Launch the uninstall workflow */ - deploy.launchUninstall(req.params['deploymentId']) - + deploy.launchUninstall(req, req.params['deploymentId']) + /* Delete the service from inventory */ .then(function(result) { - return inventory.deleteService(req.params['deploymentId']) + return inventory.deleteService(req, req.params['deploymentId']) .then (function() { return result; }); }) - + /* Send the HTTP response indicating workflow has started */ .then(function(result) { res.status(202).send(createResponse(req, result)); log.audit(req, 202, "ExecutionId: " + result.executionId); return result; }) - + /* Finish the delete processing--wait for the uninstall to complete, delete deployment, delete blueprint */ .then(function(result) { - return deploy.finishUninstall(result.deploymentId, result.executionId); + return deploy.finishUninstall(req, result.deploymentId, result.executionId); }) - + /* Log completion in audit log */ .then(function(result) { - log.audit(req, 200, "Undeployed id: " + req.params['deploymentId']); + log.audit(req, 200, "Undeployed id: " + req.params['deploymentId']); }) - + /* All errors show up here */ .catch(function(error) { /* If we haven't already sent a response, give it to the error handler to send response */ - if (!res.headersSent) { + if (!res.headersSent) { next(error); } else { @@ -217,8 +220,8 @@ app.delete('/:deploymentId', function(req, res, next) { /* Get the status of a workflow execution */ app.get('/:deploymentId/operation/:executionId', function(req, res, next){ - deploy.getExecutionStatus(req.params['executionId']) - + deploy.getExecutionStatus(req, req.params['executionId']) + /* Send success response */ .then(function(result) { result.requestId = req.dcaeReqId; @@ -226,9 +229,9 @@ app.get('/:deploymentId/operation/:executionId', function(req, res, next){ res.status(200).json(result); log.audit(req, 200, "Workflow type: " + result.operationType + " -- execution status: " + result.status); }) - + .catch(next); /* Let the error handler send the response and log the error */ - + }); -module.exports = app; \ No newline at end of file +module.exports = app; diff --git a/lib/deploy.js b/lib/deploy.js index 7f83620..e651773 100644 --- a/lib/deploy.js +++ b/lib/deploy.js @@ -1,16 +1,16 @@ /* -Copyright(c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ @@ -58,7 +58,7 @@ 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; - + if (err instanceof Error) { /* node.js system error */ e = createError("Error communicating with CM: " + err.message, 504, "system", 202, 'cloudify-manager'); @@ -71,7 +71,7 @@ var normalizeError = function (err) { var status = err.status || 502; var cfyCode = "UNKNOWN"; var cfyMessage; - + if (err.body) { var p = parseContent(err.body); if (p.json) { @@ -84,28 +84,28 @@ var normalizeError = function (err) { } 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; }; // Augment the raw outputs from a deployment with the descriptions from the blueprint -var annotateOutputs = function (id, rawOutputs) { +var annotateOutputs = function (req, id, rawOutputs) { return new Promise(function(resolve, reject) { - + var outItems = Object.keys(rawOutputs); - + if (outItems.length < 1) { // No output items, so obviously no descriptions, just return empty object resolve({}); } else { // Call Cloudify to get the descriptions - cfy.getOutputDescriptions(id) + cfy.getOutputDescriptions(req, id) .then(function(res) { // Assemble an outputs object with values from raw output and descriptions just obtained var p = parseContent(res.body); @@ -115,16 +115,16 @@ var annotateOutputs = function (id, rawOutputs) { outs[i] = {value: rawOutputs[i]}; if (p.content.outputs[i] && p.content.outputs[i].description) { outs[i].description = p.content.outputs[i].description; - } + } }); resolve(outs); } else { reject({code: "API_INVALID_RESPONSE", message: "Invalid response for output descriptions query"}); - } + } }); } - + }); }; @@ -137,41 +137,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(id, blueprint, inputs) { - logger.debug(null, "deploymentId: " + id + " starting blueprint upload"); +const launchBlueprint = function(req, id, blueprint, inputs) { + logger.debug(req.dcaeReqId, "deploymentId: " + id + " starting blueprint upload"); // Upload blueprint - return cfy.uploadBlueprint(id, blueprint) - + return cfy.uploadBlueprint(req, id, blueprint) + // Create deployment .then (function(result) { - logger.debug(null, "deploymentId: " + id + " blueprint uploaded"); + logger.debug(req.dcaeReqId, "deploymentId: " + id + " blueprint uploaded"); // Create deployment - return cfy.createDeployment(id, id, inputs); + return cfy.createDeployment(req, id, id, inputs); }) - + // Launch the workflow, but don't wait for it to complete .then(function(result){ - logger.debug(null, "deploymentId: " + id + " deployment created"); + logger.debug(req.dcaeReqId, "deploymentId: " + id + " deployment created"); return delay(DELAY_INSTALL_WORKFLOW) - .then(function(){ - return cfy.initiateWorkflowExecution(id, 'install'); + .then(function(){ + return cfy.initiateWorkflowExecution(req, id, 'install'); }); }) .catch(function(error) { - logger.debug(null, "Error: " + error + " for launch blueprint for deploymentId " + id); + logger.debug(req.dcaeReqId, "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(null, "finishInstallation: " + deploymentId + " -- executionId: " + executionId); - return cfy.getWorkflowResult(executionId) +const finishInstallation = function(req, deploymentId, executionId) { + logger.debug(req.dcaeReqId, "finishInstallation: " + deploymentId + " -- executionId: " + executionId); + return cfy.getWorkflowResult(req, executionId) .then (function(result){ - logger.debug(null, "deploymentId: " + deploymentId + " install workflow successfully executed"); + logger.debug(req.dcaeReqId, "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(deploymentId); }); + return delay(DELAY_RETRIEVE_OUTPUTS).then(function() { + return cfy.getOutputs(req, deploymentId); + }); }) .then(function(result) { // We have the raw outputs from the deployment but not annotated with the descriptions @@ -182,45 +184,49 @@ const finishInstallation = function(deploymentId, executionId) { if (p.content.outputs) { rawOutputs = p.content.outputs; } - } + } } - logger.debug(null, "output retrieval result for " + deploymentId + ": " + JSON.stringify(result)); - return annotateOutputs(deploymentId, rawOutputs); + logger.debug(req.dcaeReqId, "output retrieval result for " + deploymentId + ": " + JSON.stringify(result)); + return annotateOutputs(req, deploymentId, rawOutputs); }) .catch(function(err) { - logger.debug(null, "Error finishing install workflow: " + err + " -- " + JSON.stringify(err)); + logger.debug(req.dcaeReqId, "Error finishing install workflow: " + err + " -- " + JSON.stringify(err)); throw normalizeError(err); }); }; exports.finishInstallation = finishInstallation; // Initiate uninstall workflow against a deployment, but don't wait for workflow to finish -const launchUninstall = function(deploymentId) { - logger.debug(null, "deploymentId: " + deploymentId + " starting uninstall workflow"); +const launchUninstall = function(req, deploymentId) { + logger.debug(req.dcaeReqId, "deploymentId: " + deploymentId + " starting uninstall workflow"); // Run uninstall workflow - return cfy.initiateWorkflowExecution(deploymentId, 'uninstall') + return cfy.initiateWorkflowExecution(req, deploymentId, 'uninstall') .then(function(result) { return result; }) .catch(function(err) { - logger.debug(null, "Error initiating uninstall workflow: " + err + " -- " + JSON.stringify(err)); + logger.debug(req.dcaeReqId, "Error initiating uninstall workflow: " + err + " -- " + JSON.stringify(err)); throw normalizeError(err); - }); + }); }; exports.launchUninstall = launchUninstall; -const finishUninstall = function(deploymentId, executionId) { - logger.debug(null, "finishUninstall: " + deploymentId + " -- executionId: " + executionId); - return cfy.getWorkflowResult(executionId) +const finishUninstall = function(req, deploymentId, executionId) { + logger.debug(req.dcaeReqId, "finishUninstall: " + deploymentId + " -- executionId: " + executionId); + return cfy.getWorkflowResult(req, executionId) .then (function(result){ - logger.debug(null, "deploymentId: " + deploymentId + " uninstall workflow successfully executed"); + logger.debug(req.dcaeReqId, "deploymentId: " + deploymentId + " uninstall workflow successfully executed"); // Delete the deployment - return delay(DELAY_DELETE_DEPLOYMENT).then(function() {return cfy.deleteDeployment(deploymentId);}); + return delay(DELAY_DELETE_DEPLOYMENT).then(function() { + return cfy.deleteDeployment(req, deploymentId); + }); }) .then (function(result){ - logger.debug(null, "deploymentId: " + deploymentId + " deployment deleted"); + logger.debug(req.dcaeReqId, "deploymentId: " + deploymentId + " deployment deleted"); // Delete the blueprint - return delay(DELAY_DELETE_BLUEPRINT).then(function() {return cfy.deleteBlueprint(deploymentId);}); + return delay(DELAY_DELETE_BLUEPRINT).then(function() { + return cfy.deleteBlueprint(req, deploymentId); + }); }) .then (function(result){ return result; @@ -228,19 +234,19 @@ const finishUninstall = function(deploymentId, executionId) { .catch (function(err){ throw normalizeError(err); }); - + }; exports.finishUninstall = finishUninstall; // Get the status of a workflow execution -exports.getExecutionStatus = function (exid) { - return cfy.getWorkflowExecutionStatus(exid) +exports.getExecutionStatus = function (req, exid) { + return cfy.getWorkflowExecutionStatus(req, exid) .then(function(res){ - + var result = { operationType: res.json.workflow_id }; - + // Map execution status if (res.json.status === "terminated") { result.status = "succeeded"; @@ -254,11 +260,11 @@ exports.getExecutionStatus = function (exid) { else { result.status = "processing"; } - + if (res.json.error) { result.error = res.json.error; } - logger.debug(null, "getExecutionStatus result: " + JSON.stringify(result)); + logger.debug(req.dcaeReqId, "getExecutionStatus result: " + JSON.stringify(result)); return result; }) .catch(function(error) { @@ -267,37 +273,37 @@ exports.getExecutionStatus = function (exid) { }; // Go through the Cloudify API call sequence to do a deployment -exports.deployBlueprint = function(id, blueprint, inputs) { +exports.deployBlueprint = function(req, id, blueprint, inputs) { + + // Upload blueprint, create deployment, and initiate install workflow + return launchBlueprint(req, 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 + return finishInstallation(req, result.deploymentId, result.executionId); // Will throw normalized error if it fails }, - + // launchBlueprint promise rejected -- report error function(err) { - throw normalizeError(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(null, "deploymentId: " + id + " starting uninstall workflow"); - +exports.undeployDeployment = function(req, id) { + logger.debug(req.dcaeReqId, "deploymentId: " + id + " starting uninstall workflow"); + // Run launch uninstall workflow - return launchUninstall(id) - + return launchUninstall(req, id) + // launchUninstall promise fulfilled -- finish uninstall .then (function(result){ - return finishUninstall(result.deploymentId, result.executionId); // Will throw normalized error if it fails + return 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/info.js b/lib/info.js index f6b37a8..1c15349 100644 --- a/lib/info.js +++ b/lib/info.js @@ -1,5 +1,5 @@ /* -Copyright(c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,27 +19,27 @@ See the License for the specific language governing permissions and limitations "use strict"; const router = require('express').Router(); - -/* Pick up config exported by main */ -const config = process.mainModule.exports.config; +const logger = require('./logging').getLogger(); /* Accept an incoming event */ router.get('/', function(req, res) { - res.json( - { - "server" : { - "name": config.name, - "description": config.description, - "version": config.version, - "branch": config.branch, - "commit": config.commit, - "commit_datetime": config.commit_datetime - }, - "apiVersion": config.apiVersion, - "links": config.apiLinks - } - ); - require('./logging').getLogger().audit(req, 200); + /* Pick up config exported by main */ + const config = process.mainModule.exports.config; + const info = { + "server" : { + "name": config.name, + "description": config.description, + "version": config.version, + "branch": config.branch, + "commit": config.commit, + "commit_datetime": config.commit_datetime, + "server_instance_uuid": config.server_instance_uuid + }, + "apiVersion": config.apiVersion, + "links": config.apiLinks + }; + res.json(info); + logger.audit(req, 200, JSON.stringify(info)); }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/lib/inventory.js b/lib/inventory.js index c2e13c9..ecc790a 100644 --- a/lib/inventory.js +++ b/lib/inventory.js @@ -1,5 +1,5 @@ /* -Copyright(c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -55,7 +55,7 @@ module.exports = function(options) { return { /* Add a DCAE service to the inventory. Done after a deployment.*/ - addService: function(deploymentId, serviceType, vnfId, vnfType, vnfLocation, outputs) { + addService: function(req, deploymentId, serviceType, vnfId, vnfType, vnfLocation, outputs) { /* Create the service description */ var serviceDescription = @@ -83,23 +83,23 @@ module.exports = function(options) { json: serviceDescription }; - return doRequest(reqOptions, null, INVENTORY); + return doRequest(req, reqOptions, null, INVENTORY); }, /* Remove a DCAE service from the inventory. Done after an undeployment. */ - deleteService: function(serviceId) { - return doRequest({method: "DELETE", uri: url + INV_SERVICES + "/" + serviceId}, null, INVENTORY); + deleteService: function(req, serviceId) { + return doRequest(req, {method: "DELETE", uri: url + INV_SERVICES + "/" + serviceId}, null, INVENTORY); }, /* Find running/deploying instances of services (with a given type name, if specified) */ - getServicesByType: function(query) { + getServicesByType: function(req, query) { var options = { method: 'GET', uri: url + INV_SERVICES, - qs: query || {} + qs: query }; - return doRequest(options, null, INVENTORY) + return doRequest(req, options, null, INVENTORY) .then (function (result) { var services = []; var content = JSON.parse(result.body); @@ -113,8 +113,8 @@ module.exports = function(options) { }, /* Find a blueprint given the service type ID -- return blueprint and type ID */ - getBlueprintByType: function(serviceTypeId) { - return doRequest({ + getBlueprintByType: function(req, serviceTypeId) { + return doRequest(req, { method: "GET", uri: url + INV_SERV_TYPES + '/' + serviceTypeId }, null, INVENTORY) @@ -138,8 +138,8 @@ module.exports = function(options) { * deployment ID as service name. If it doesn't exist, the function * resolves its promise. If it *does* exist, then it throws an error. */ - verifyUniqueDeploymentId: function(deploymentId) { - return doRequest({ + verifyUniqueDeploymentId: function(req, deploymentId) { + return doRequest(req, { method: "GET", uri: url + INV_SERVICES + "/" + deploymentId }, null, INVENTORY) diff --git a/lib/logging.js b/lib/logging.js index a21f37e..cfd987d 100644 --- a/lib/logging.js +++ b/lib/logging.js @@ -1,5 +1,5 @@ /* -Copyright(c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -130,6 +130,7 @@ const DEBUG_MARKER = '^'; const formatAuditRecord = function(req, status, extra) { var rec = new Array(AUDIT_NFIELDS); const end = new Date(); + rec[AUDIT_INSTUUID] = (process.mainModule.exports.config || {}).server_instance_uuid || ""; rec[AUDIT_END] = end.toISOString(); rec[AUDIT_BEGIN] = req.startTime.toISOString(); rec[AUDIT_REQID] = req.dcaeReqId; @@ -161,6 +162,7 @@ const formatAuditRecord = function(req, status, extra) { const formatMetricsRecord = function(req, opInfo, extra) { var rec = new Array(METRICS_NFIELDS); const end = new Date(); + rec[METRICS_INSTUUID] = (process.mainModule.exports.config || {}).server_instance_uuid || ""; rec[METRICS_END] = end.toISOString(); rec[METRICS_BEGIN] = opInfo.startTime.toISOString(); diff --git a/lib/policy.js b/lib/policy.js index 482650a..89e5b6a 100644 --- a/lib/policy.js +++ b/lib/policy.js @@ -1,5 +1,5 @@ /* -Copyright(c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -324,6 +324,13 @@ app.set('x-powered-by', false); app.set('etag', false); app.use(require('./middleware').checkType('application/json')); app.use(require('body-parser').json({strict: true})); +app.use(function(req, res, next) { + logger.debug(req.dcaeReqId, + "new req: " + req.method + " " + req.originalUrl + + " from: " + req.ip + " body: " + JSON.stringify(req.body) + ); + next(); +}); app.post('/', policyUpdate); app.get('/components', getComponentPoliciesFromCloudify); diff --git a/lib/promise_request.js b/lib/promise_request.js index 0572ac4..975f12d 100644 --- a/lib/promise_request.js +++ b/lib/promise_request.js @@ -1,5 +1,5 @@ /* -Copyright(c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -31,11 +31,11 @@ const url = require('url'); const querystring = require('querystring'); const logger = require('./logging').getLogger(); -exports.doRequest = function(options, body, targetEntity, mainReq) { - +exports.doRequest = function(mainReq, options, body, targetEntity) { + /* Defense: for now, some callers don't provide mainReq */ mainReq = mainReq || {}; - + var opInfo = {"startTime":new Date(), "targetEntity": targetEntity}; return new Promise(function(resolve, reject) { @@ -56,10 +56,12 @@ exports.doRequest = function(options, body, targetEntity, mainReq) { options.hostname = parsed.hostname; options.port = parsed.port; options.path = parsed.path; + opInfo.targetService = options.method + " " + options.uri; if (options.qs) { - options.path += ('?' + querystring.stringify(options.qs)); + const qry = ('?' + querystring.stringify(options.qs)); + options.path += qry; + opInfo.targetService += qry; } - opInfo.targetService = options.method + " " + options.uri; } try { -- cgit 1.2.3-korg