summaryrefslogtreecommitdiffstats
path: root/lib/dcae-deployments.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dcae-deployments.js')
-rw-r--r--lib/dcae-deployments.js234
1 files changed, 234 insertions, 0 deletions
diff --git a/lib/dcae-deployments.js b/lib/dcae-deployments.js
new file mode 100644
index 0000000..bcec0e5
--- /dev/null
+++ b/lib/dcae-deployments.js
@@ -0,0 +1,234 @@
+/*
+Copyright(c) 2017 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.
+
+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,
+software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and limitations under the License.
+*/
+
+/* Handle the /dcae-deployments API */
+
+"use strict";
+
+/* Set this code up as a "sub-app"--lets us get the mountpoint for creating links */
+const app = require('express')();
+app.set('x-powered-by', false);
+app.set('etag', false);
+
+const bodyParser = require('body-parser');
+const deploy = require('./deploy');
+const middleware = require('./middleware');
+const inv = require('./inventory');
+const log = require('./logging').getLogger();
+
+/* Pick up config exported by main */
+const config = process.mainModule.exports.config;
+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
+
+
+/* Return a promise for a blueprint for the given service type ID */
+const getBlueprint = function(serviceTypeId) {
+ return inventory.getBlueprintByType(serviceTypeId)
+ .then(function (blueprintInfo) {
+ if (!blueprintInfo.blueprint) {
+ var e = new Error("No service type with ID " + serviceTypeId);
+ e.status = 404;
+ throw e;
+ }
+ return blueprintInfo;
+ })
+};
+
+/* Generate self and status links object for responses */
+const createLinks = function(req, deploymentId, executionId) {
+ var baseURL = req.protocol + '://' + req.get('Host') + req.app.mountpath + '/' + deploymentId;
+ return {
+ self: baseURL,
+ status: baseURL + '/operation/' + executionId
+ };
+};
+
+/* Generate a success response body for PUT and DELETE operations */
+const createResponse = function(req, result) {
+ return {
+ requestId: req.dcaeReqId,
+ links: createLinks(req, result.deploymentId, result.executionId)
+ };
+};
+
+/* Look up running (or in process of deploying) instances of the given service type */
+app.get('/', function (req, res, next) {
+ var services = []
+
+
+ var searchTerm = {};
+
+ req.query['serviceTypeId'] && (searchTerm = {typeId: req.query['serviceTypeId']});
+
+ inventory.getServicesByType(searchTerm)
+ .then(function (result) {
+ var deployments = result.map(function(service){
+ return {
+ href: req.protocol + '://' + req.get('Host') + req.app.mountpath + '/' + service.deploymentId
+ };
+ })
+ res.status(200).json({requestId: req.dcaeReqId, deployments: deployments});
+ log.audit(req, 200);
+ })
+ .catch(next); /* Let the error handler send response and log the error */
+});
+
+/* Accept an incoming deployment request */
+app.put('/:deploymentId', function(req, res, next) {
+
+ log.debug(req.dcaeRequestId, "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'])
+
+ /* Get the blueprint for this service type */
+ .then(function(res) {
+ return getBlueprint(req.body['serviceTypeId']);
+ })
+
+ /* 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");
+ })
+
+ /* 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']);
+ })
+
+ /* 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);
+ })
+
+ /* 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) {
+
+ /* 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'])
+ .catch(function(error) {
+ log.error(error, req);
+ });
+ }
+
+ next(error);
+ }
+ else {
+ /* Already sent the response, so just log error */
+ /* Don't remove from inventory, because there is a deployment on CM that might need to be removed */
+ 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'])
+
+ /* Delete the service from inventory */
+ .then(function(result) {
+ return inventory.deleteService(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);
+ })
+
+ /* Log completion in audit log */
+ .then(function(result) {
+ 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) {
+ next(error);
+ }
+ else {
+ /* Error happened after we sent the response--log it */
+ error.message = "Error undeploying deploymentId " + req.params['deploymentId'] + ": " + error.message
+ log.error(error, req);
+ log.audit(req, 500, error.message);
+ }
+ });
+});
+
+/* Get the status of a workflow execution */
+app.get('/:deploymentId/operation/:executionId', function(req, res, next){
+ deploy.getExecutionStatus(req.params['executionId'])
+
+ /* Send success response */
+ .then(function(result) {
+ result.requestId = req.dcaeReqId;
+ result.links = createLinks(req, req.params['deploymentId'], req.params['executionId']);
+ 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