From 50ec0f20006ac066e7eedcf6c7a92ac09ca746c2 Mon Sep 17 00:00:00 2001 From: Andre Keedy Date: Thu, 3 Mar 2016 11:59:05 -0500 Subject: [PATCH] Add support to run an ansible playbook - Add an API to upload a playbook - Add an API to run an ansible playbook on a rackHD node - fix new eslint errors - Add unit tests Change-Id: Ibe623c228c6ac13cab0f2726e267878bec1bdb6e Implements: blueprint shovel-deployment-capability --- api/swagger.json | 55 +++++++++- controllers/Shovel.js | 134 +++++++++++++++++++------ index.js | 2 +- lib/api/client.js | 16 +-- lib/api/monorail/monorail.js | 16 ++- lib/api/openstack/ironic.js | 4 +- lib/api/openstack/keystone.js | 18 ++-- lib/services/poller.js | 6 +- package.json | 5 +- test/api/monorail.js | 14 +++ test/controllers/Shovel.js | 182 +++++++++++++++++++++++++++++++--- test/helper.js | 2 +- 12 files changed, 381 insertions(+), 73 deletions(-) diff --git a/api/swagger.json b/api/swagger.json index 1a6333c..893e55a 100644 --- a/api/swagger.json +++ b/api/swagger.json @@ -589,6 +589,40 @@ } } } + }, + "/run/ansible-playbook/{identifier}": { + "post": { + "x-swagger-router-controller": "Shovel", + "tags": [ "Nodes Provisionning" ], + "operationId": "runAnsible", + "summary": "run the uploaded ansible playbook", + "parameters": [ + { + "name": "identifier", + "in": "path", + "description": "rackHD node ID", + "required": true, + "type": "string" + }, + { + "name": "Config", + "in": "body", + "description": "OS Configuration", + "required": true, + "schema": { + "$ref": "#/definitions/run-ansible" + } + } + ], + "responses": { + "200": { + "description": "Not Implemented" + }, + "default": { + "description": "unexpected error" + } + } + } } }, "definitions": { @@ -753,7 +787,7 @@ "properties": { "name": { "type": "string", - "example":"Graph.InstallCentOS" + "example": "Graph.InstallCentOS" }, "options": { "type": "object", @@ -823,6 +857,25 @@ } } } + }, + "run-ansible": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "example": "runExample" + }, + "vars": { + "type": "object" + }, + "playbookPath": { + "type": "string", + "example": "files/extract/main.yml" + } + } } } } diff --git a/controllers/Shovel.js b/controllers/Shovel.js index a0b2f7f..3db0363 100644 --- a/controllers/Shovel.js +++ b/controllers/Shovel.js @@ -278,20 +278,20 @@ module.exports.registerpost = function registerpost(req, res) { userEntry = req.body; if (userEntry.driver === 'pxe_ipmitool') { info = { - 'ipmi_address': userEntry.ipmihost, - 'ipmi_username': userEntry.ipmiuser, - 'ipmi_password': userEntry.ipmipass, - 'deploy_kernel': userEntry.kernel, - 'deploy_ramdisk': userEntry.ramdisk + ipmi_address: userEntry.ipmihost, + ipmi_username: userEntry.ipmiuser, + ipmi_password: userEntry.ipmipass, + deploy_kernel: userEntry.kernel, + deploy_ramdisk: userEntry.ramdisk }; } else if (userEntry.driver === 'pxe_ssh') { info = { - 'ssh_address': userEntry.sshhost, - 'ssh_username': userEntry.sshuser, - 'ssh_password': userEntry.sshpass, - 'ssh_port': userEntry.sshport, - 'deploy_kernel': userEntry.kernel, - 'deploy_ramdisk': userEntry.ramdisk + ssh_address: userEntry.sshhost, + ssh_username: userEntry.sshuser, + ssh_password: userEntry.sshpass, + ssh_port: userEntry.sshport, + deploy_kernel: userEntry.kernel, + deploy_ramdisk: userEntry.ramdisk }; } else { info = {}; @@ -299,11 +299,11 @@ module.exports.registerpost = function registerpost(req, res) { /* Fill in the extra meta data with some failover and event data */ extra = { - 'nodeid': userEntry.uuid, - 'name': userEntry.name, - 'lsevents': { 'time': 0 }, - 'eventcnt': 0, - 'timer': {} + nodeid: userEntry.uuid, + name: userEntry.name, + lsevents: { time: 0 }, + eventcnt: 0, + timer: {} }; if (typeof userEntry.failovernode !== 'undefined') { extra.failover = userEntry.failovernode; @@ -315,7 +315,7 @@ module.exports.registerpost = function registerpost(req, res) { localGb = 0.0; return monorail.request_node_get(userEntry.uuid). then(function (result) { - if (!JSON.parse(result).name) { + if (!JSON.parse(result).hasOwnProperty('name')) { var error = { error_message: { message: 'failed to find required node in RackHD' } }; logger.error(error); throw error; @@ -349,16 +349,16 @@ module.exports.registerpost = function registerpost(req, res) { throw error; } propreties = { - 'cpus': dmiData.cpus, - 'memory_mb': dmiData.memory, - 'local_gb': localGb + cpus: dmiData.cpus, + memory_mb: dmiData.memory, + local_gb: localGb }; node = { - 'name': userEntry.uuid, - 'driver': userEntry.driver, - 'driver_info': info, - 'properties': propreties, - 'extra': extra + name: userEntry.uuid, + driver: userEntry.driver, + driver_info: info, + properties: propreties, + extra: extra }; return keystone.authenticatePassword(ironicConfig.os_tenant_name, ironicConfig.os_username, ironicConfig.os_password); @@ -373,10 +373,13 @@ module.exports.registerpost = function registerpost(req, res) { throw JSON.parse(ret); } ironicNode = JSON.parse(ret); - port = { 'address': userEntry.port, 'node_uuid': ironicNode.uuid }; - return ironic.createPort(ironicToken, JSON.stringify(port)); + port = { address: userEntry.port, node_uuid: ironicNode.uuid }; + return ironic.create_port(ironicToken, JSON.stringify(port)); }). then(function (createPort) { + if (createPort && JSON.parse(createPort).error_message) { + throw JSON.parse(createPort); + } logger.info('\r\nCreate port:\r\n' + JSON.stringify(createPort)); return ironic.set_power_state(ironicToken, ironicNode.uuid, 'on'); }). @@ -392,7 +395,7 @@ module.exports.registerpost = function registerpost(req, res) { timer.stop = false; timer.timeInterval = 15000; timer.isDone = true; - var data = [{ 'path': '/extra/timer', 'value': timer, 'op': 'replace' }]; + var data = [{ path: '/extra/timer', value: timer, op: 'replace' }]; return ironic.patch_node(ironicToken, ironicNode.uuid, JSON.stringify(data)); }). then(function (result) { @@ -637,8 +640,8 @@ module.exports.deployOS = function deployOS(req, res) { res.setHeader('Content-Type', 'application/json'); return monorail.runWorkFlow(req.swagger.params.identifier.value, req.body.name,req.body) - .then(function(data) { - res.end(data); + .then(function(result) { + res.end(result); }) .catch(function(err) { res.end(JSON.stringify(err)); @@ -654,12 +657,79 @@ module.exports.workflowStatus = function workflowStatus(req,res) { return monorail.getWorkFlowActive(req.swagger.params.identifier.value) .then(function(data) { if (data) { - res.end(JSON.stringify({'jobStatus':'Running'})); + res.end(JSON.stringify({jobStatus:'Running'})); } else { - res.end(JSON.stringify({'jobStatus':'Currently there is no job running on this node'})); + res.end(JSON.stringify({jobStatus:'Currently there is no job running on this node'})); } }) .catch(function(err) { res.end(JSON.stringify(err)); }); }; +/* +* @api {put} /api/1.1/uploadFiles/filename / PUT / +* @apiDescription uploaded ansible playbook in tar form and extract it +*/ +//Code for tar file uploads +// module.exports.uploadFiles = function uploadFiles(req, res) { +// 'use strict'; +// res.setHeader('content-type', 'text/plain'); +// var tar = require('tar'); +// var extractor = tar.Extract({path: 'files/extract'}) +// .on('error', function(err) { +// logger.error(err); +// res.status(500); +// res.end('error'); +// }) +// .on('end', function() { +// res.status(202); +// res.end('success'); +// }); +// var stream = require('stream'); +// var bufferStream = new stream.PassThrough(); +// bufferStream.end(new Buffer(req.swagger.params.playbook.value.buffer)); +// bufferStream.pipe(extractor); +// }; + +/* +* @api {post} /api/1.1/runAnsible/{identifier} / POST / +* @apiDescription run uploaded ansible playbook in tar form and extract it +*/ +module.exports.runAnsible = function runAnsible(req, res) { + 'use strict'; + res.setHeader('Content-Type', 'application/json'); + var ansibleTask = { + friendlyName: req.body.name, + injectableName: 'Task.Ansible.' + req.body.name, + implementsTask: 'Task.Base.Ansible', + options: { + playbook: req.body.playbookPath, + vars : req.body.vars + }, + properties: { } + }; + var ansibleWorkflow = { + friendlyName: 'Graph ' + req.body.name, + injectableName: 'Graph.Ansible.' + req.body.name, + tasks : [ + { + label: 'ansible-job', + taskName: 'Task.Ansible.' + req.body.name + } + ] + }; + return monorail.createTask(ansibleTask) + .then(function() { + return monorail.createWorkflow(ansibleWorkflow); + }) + .then(function() { + return monorail.runWorkFlow(req.swagger.params.identifier.value, + 'Graph.Ansible.' + req.body.name,null); + }) + .then(function(result) { + res.end(result); + }) + .catch(function(err) { + res.end(JSON.stringify(err)); + }); +}; diff --git a/index.js b/index.js index 414968b..0f3d497 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ /* global process */ 'use strict'; -var app = require('connect')(); +var app = require('express')(); var http = require('http'); var swaggerTools = require('swagger-tools'); var config = require('./config.json'); diff --git a/lib/api/client.js b/lib/api/client.js index ee7f2f7..6502292 100644 --- a/lib/api/client.js +++ b/lib/api/client.js @@ -50,7 +50,7 @@ var HttpClient = { method: 'POST', headers: { 'Content-type': 'application/json', - 'Accept': 'application/json', + Accept: 'application/json', 'Content-Length': Buffer.byteLength(msg.data), 'User-Agent': 'shovel-client' } @@ -140,16 +140,16 @@ var HttpClient = { method: 'PUT', headers: { 'Content-type': 'application/json', - 'Accept': 'application/json', + Accept: 'application/json', 'Content-Length': Buffer.byteLength(msg.data), 'User-Agent': 'shovel-client' } }; + /*Update the request header with special fields*/ if (Buffer.byteLength(msg.token)) { options.headers['X-Auth-Token'] = msg.token; } - if (Buffer.byteLength(JSON.stringify(msg.api))) { options.headers[msg.api.name] = msg.api.version; } @@ -159,8 +159,8 @@ var HttpClient = { response.on('data', function (chunk) { body += chunk; }); - response.on('error', function (err) { - var errorMessage = { errorMessage: { hostname: msg.host, message: err } }; + response.on('error', function (e) { + var errorMessage = { errorMessage: { hostname: msg.host, message: e } }; output(errorMessage); }); response.on('end', function () { @@ -174,7 +174,9 @@ var HttpClient = { output(errorMessage); }); - request.write(msg.data); + if (Buffer.byteLength(msg.data)) { + request.write(msg.data); + } request.end(); }, Patch: function (msg, output) { @@ -187,7 +189,7 @@ var HttpClient = { method: 'PATCH', headers: { 'Content-type': 'application/json', - 'Accept': 'application/json', + Accept: 'application/json', 'Content-Length': Buffer.byteLength(msg.data), 'User-Agent': 'shovel-client' } diff --git a/lib/api/monorail/monorail.js b/lib/api/monorail/monorail.js index 1eb11d0..fc506e2 100644 --- a/lib/api/monorail/monorail.js +++ b/lib/api/monorail/monorail.js @@ -149,7 +149,9 @@ var MonorailWrapper = { runWorkFlow: function runWorkFlow(hwaddr,graphName,content) { 'use strict'; request.path = pfx + '/nodes/' + hwaddr + '/workflows/?name=' + graphName; - request.data = JSON.stringify(content); + if (content !== null) { + request.data = JSON.stringify(content); + } return client.PostAsync(request); }, getWorkFlowActive: function getWorkFlowActive(hwaddr) { @@ -161,6 +163,18 @@ var MonorailWrapper = { 'use strict'; request.path = pfx + '/nodes/' + hwaddr + '/workflows/active'; return client.DeleteAsync(request); + }, + createTask: function createTask(content) { + 'use strict'; + request.path = pfx + '/workflows/tasks/'; + request.data = JSON.stringify(content); + return client.PutAsync(request); + }, + createWorkflow: function createWorkflow(content) { + 'use strict'; + request.path = pfx + '/workflows'; + request.data = JSON.stringify(content); + return client.PutAsync(request); } }; module.exports = Object.create(MonorailWrapper); diff --git a/lib/api/openstack/ironic.js b/lib/api/openstack/ironic.js index 967b928..57425a3 100644 --- a/lib/api/openstack/ironic.js +++ b/lib/api/openstack/ironic.js @@ -13,8 +13,8 @@ var request = { token: '', data: '', api: { - 'name': 'X-OpenStack-Ironic-API-Version', - 'version': '1.6' + name: 'X-OpenStack-Ironic-API-Version', + version: '1.6' } }; diff --git a/lib/api/openstack/keystone.js b/lib/api/openstack/keystone.js index 3d22d01..d0eb8ba 100644 --- a/lib/api/openstack/keystone.js +++ b/lib/api/openstack/keystone.js @@ -33,11 +33,11 @@ var KeystoneAuthentication = { } request.data = JSON.stringify( { - 'auth': { - 'tenantName': tenantName, - 'passwordCredentials': { - 'username': username, - 'password': decrypted + auth: { + tenantName: tenantName, + passwordCredentials: { + username: username, + password: decrypted } } @@ -49,10 +49,10 @@ var KeystoneAuthentication = { 'use strict'; request.data = JSON.stringify( { - 'auth': { - 'tenantName': tenantName, - 'token': { - 'id': token + auth: { + tenantName: tenantName, + token: { + id: token } } }); diff --git a/lib/services/poller.js b/lib/services/poller.js index 2e3fb15..67be447 100644 --- a/lib/services/poller.js +++ b/lib/services/poller.js @@ -135,9 +135,9 @@ function Poller(timeInterval) { nodeData.extra.events = lastEvent; var data = [ { - 'path': '/extra', - 'value': nodeData.extra, - 'op': 'replace' + path: '/extra', + value: nodeData.extra, + op: 'replace' }]; return data; }) diff --git a/package.json b/package.json index bb1480b..3c551a3 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "license": "Apache-2.0", "dependencies": { "bluebird": "3.1.1", - "connect": "^3.2.0", "swagger-tools": "0.8.*", "should": "~7.0.1", "mocha": "^2.1.0", @@ -23,7 +22,9 @@ "istanbul": "0.4.1", "nock": "3.6.0", "eslint-config-openstack": "~1.2.3", - "eslint": "~1.10.3" + "eslint": "~1.10.3", + "express": "~4.13.4", + "tar": "~2.2.1" }, "scripts": { "start": "start shovel", diff --git a/test/api/monorail.js b/test/api/monorail.js index 95e47b6..4a5a8ba 100644 --- a/test/api/monorail.js +++ b/test/api/monorail.js @@ -185,5 +185,19 @@ describe('****Monorail Lib****',function(){ done(); }); }); + it('monorail.createWorkflow return data from monorail', function (done) { + return monorail.createWorkflow({}) + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('monorail.createTask return data from monorail', function (done) { + return monorail.createTask({}) + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); }); }); \ No newline at end of file diff --git a/test/controllers/Shovel.js b/test/controllers/Shovel.js index 04d1510..3ab824f 100644 --- a/test/controllers/Shovel.js +++ b/test/controllers/Shovel.js @@ -56,6 +56,8 @@ describe('****SHOVEL API Interface****', function () { sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[0]))); sinon.stub(monorail, 'runWorkFlow').returns(Promise.resolve('{"definition":{}}')); getWorkflow = sinon.stub(monorail,'getWorkFlowActive'); + sinon.stub(monorail, 'createTask').returns(Promise.resolve()); + sinon.stub(monorail, 'createWorkflow').returns(Promise.resolve()); //glance sinon.stub(glance, 'get_images').returns(Promise.resolve(JSON.stringify(glanceImages))); //keystone @@ -83,6 +85,8 @@ describe('****SHOVEL API Interface****', function () { monorail['get_catalog_data_by_source'].restore(); monorail['runWorkFlow'].restore(); monorail['getWorkFlowActive'].restore(); + monorail['createTask'].restore(); + monorail['createWorkflow'].restore(); //ironic ironic['patch_node'].restore(); ironic['get_node_list'].restore(); @@ -334,10 +338,8 @@ describe('****SHOVEL API Interface****', function () { .send({"name": "Graph.InstallCentOS","options": { "defaults": {"obmServiceName": "ipmi-obm-service"}}}) .end(function (err, res) { if (err) { - console.log('hey yo'); throw err; } - console.log('hello' + res.text) JSON.parse(res.text).should.have.property('definition'); done(); }); @@ -350,11 +352,22 @@ describe('****SHOVEL API Interface****', function () { if (err) { throw err; } - console.log(res.text); JSON.parse(res.text).should.have.property('jobStatus'); done(); }); }); + it('/api/1.1/run/ansible-playbook/{id} should return property definition', function (done) { + request(url) + .post('/api/1.1/run/ansible-playbook/123') + .send({"name": "Graph.Example","options": {}}) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('definition'); + done(); + }); + }); it('/api/1.1/worflow-status/{identifier} should return property jobStatus even if no job is running', function (done) { getWorkflow.returns(Promise.resolve()); request(url) @@ -379,10 +392,12 @@ describe('****SHOVEL API Interface****', function () { var output = ({ error: 'error_message' }); sinon.stub(client, 'GetAsync').returns(Promise.reject(output)); sinon.stub(client, 'PostAsync').returns(Promise.reject(output)); + sinon.stub(client, 'PutAsync').returns(Promise.reject(output)); }); after('teardown mocks', function () { client['GetAsync'].restore(); client['PostAsync'].restore(); + client['PutAsync'].restore(); }); it('/api/1.1/nodes/identifier should return error message', function (done) { @@ -558,34 +573,92 @@ describe('****SHOVEL API Interface****', function () { done(); }); }); + it('api/1.1/run/ansible-playbook/{id} should return error message', function (done) { + request(url) + .post('/api/1.1/run/ansible-playbook/123') + .send({name: 'runExample',vars: {}, + playbookPath: 'main.yml' + }) + .expect(200) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); }); describe('Shovel api unit test for register', function () { - var error_message = '{"error_message": "{\\"debuginfo\\": null, \\"faultcode\\": \\"Client\\", \\"faultstring\\": \\"A node with name 5668b42d8bee16a10989e4e4 already exists.\\"}"}'; + var error_message = '{"error_message": "{\\"debuginfo\\": null, \\"faultcode\\": \\"Client\\", \\"faultstring\\": \\"some error\\"}"}'; var body = { "id": identifier, "driver": "string", "ipmihost": "string", "ipmiusername": "string", "ipmipasswd": "string" }; - + var getNode, diskSize, memoryCpu, ironicNodeCreate, + ironicCreatePort, ironicPowerState, ironicPatch; beforeEach('set up mocks', function () { //monorail + getNode = sinon.stub(monorail, 'request_node_get'); + diskSize = sinon.stub(monorail, 'nodeDiskSize'); + memoryCpu = sinon.stub(monorail, 'getNodeMemoryCpu'); + monorailWhiteList = sinon.stub(monorail,'request_whitelist_set'); //keystone sinon.stub(keystone, 'authenticatePassword').returns(Promise.resolve(JSON.stringify(keyToken))); //ironic - sinon.stub(ironic, 'create_node').returns(Promise.resolve(error_message)); + ironicNodeCreate = sinon.stub(ironic, 'create_node'); + ironicCreatePort = sinon.stub(ironic,'create_port'); + ironicPowerState = sinon.stub(ironic,'set_power_state'); + ironicPatch = sinon.stub(ironic, 'patch_node'); }); afterEach('teardown mocks', function () { //monorail monorail['nodeDiskSize'].restore(); monorail['getNodeMemoryCpu'].restore(); monorail['request_node_get'].restore(); + monorail['request_whitelist_set'].restore(); //keystone keystone['authenticatePassword'].restore(); //ironic ironic['create_node'].restore(); - + ironic['create_port'].restore(); + ironic['set_power_state'].restore(); + ironic['patch_node'].restore(); + }); + it('response in register should have property error_message when node returns empty ', function (done) { + getNode.returns(Promise.resolve('{}')); + request(url) + .post('/api/1.1/register') + .send(body) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error_message'); + done(); + }); + }); + it('response in register should have property error_message when diskSize has an exception ', function (done) { + var output = {error_message: { message: 'failed to get compute node Disk Size' }}; + getNode.returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + diskSize.returns(Promise.reject(output)); + request(url) + .post('/api/1.1/register') + .send(body) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error_message'); + done(); + }); }); it('response in register should have property error_message when any of node info equal to 0 ', function (done) { - sinon.stub(monorail, 'request_node_get').returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); - sinon.stub(monorail, 'nodeDiskSize').returns(Promise.resolve(0)); - sinon.stub(monorail, 'getNodeMemoryCpu').returns(Promise.resolve({ cpus: 0, memory: 0 })); + getNode.returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + diskSize.returns(Promise.resolve(0)); + memoryCpu.returns(Promise.resolve({ cpus: 0, memory: 0 })); request(url) .post('/api/1.1/register') @@ -601,10 +674,10 @@ describe('****SHOVEL API Interface****', function () { }); }); it('response in register should have property error_message create node return error in ironic', function (done) { - sinon.stub(monorail, 'request_node_get').returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); - sinon.stub(monorail, 'nodeDiskSize').returns(Promise.resolve(1)); - sinon.stub(monorail, 'getNodeMemoryCpu').returns(Promise.resolve({ cpus: 1, memory: 1 })); - + getNode.returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + diskSize.returns(Promise.resolve(1)); + memoryCpu.returns(Promise.resolve({ cpus: 1, memory: 1 })); + ironicNodeCreate.returns(Promise.resolve(error_message)); request(url) .post('/api/1.1/register') .send(body) @@ -618,5 +691,86 @@ describe('****SHOVEL API Interface****', function () { done(); }); }); + it('response in register should have property error_message create port return error in ironic', function (done) { + getNode.returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + diskSize.returns(Promise.resolve(1)); + memoryCpu.returns(Promise.resolve({ cpus: 1, memory: 1 })); + ironicNodeCreate.returns(Promise.resolve(JSON.stringify(ironic_node_list[0]))); + ironicCreatePort.returns(Promise.resolve(error_message)); + request(url) + .post('/api/1.1/register') + .send(body) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error_message'); + done(); + }); + }); + it('response in register should have property error_message set power state return error in ironic', function (done) { + getNode.returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + diskSize.returns(Promise.resolve(1)); + memoryCpu.returns(Promise.resolve({ cpus: 1, memory: 1 })); + ironicNodeCreate.returns(Promise.resolve(JSON.stringify(ironic_node_list[0]))); + ironicCreatePort.returns(Promise.resolve()); + ironicPowerState.returns(Promise.resolve(error_message)); + request(url) + .post('/api/1.1/register') + .send(body) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error_message'); + done(); + }); + }); + it('response in register should have property error_message ironic patch node return error in ironic', function (done) { + getNode.returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + diskSize.returns(Promise.resolve(1)); + memoryCpu.returns(Promise.resolve({ cpus: 1, memory: 1 })); + ironicNodeCreate.returns(Promise.resolve(JSON.stringify(ironic_node_list[0]))); + ironicCreatePort.returns(Promise.resolve()); + ironicPowerState.returns(Promise.resolve()); + ironicPatch.returns(Promise.reject({error_message:'some error'})); + request(url) + .post('/api/1.1/register') + .send(body) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error_message'); + done(); + }); + }); + it('response in register should have property result on success', function (done) { + getNode.returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + diskSize.returns(Promise.resolve(1)); + memoryCpu.returns(Promise.resolve({ cpus: 1, memory: 1 })); + ironicNodeCreate.returns(Promise.resolve(JSON.stringify(ironic_node_list[0]))); + ironicCreatePort.returns(Promise.resolve()); + ironicPowerState.returns(Promise.resolve()); + ironicPatch.returns(Promise.resolve()); + request(url) + .post('/api/1.1/register') + .send(body) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('result'); + done(); + }); + }); }); }); diff --git a/test/helper.js b/test/helper.js index c4ff2a7..5a66edf 100644 --- a/test/helper.js +++ b/test/helper.js @@ -1,6 +1,6 @@ // Copyright 2015, EMC, Inc. -var app = require('connect')(); +var app = require('express')(); var http = require('http'); var swaggerTools = require('swagger-tools'); var sinon = require('sinon');