diff --git a/src/openstack.js b/src/openstack.js index 46109d1..796af47 100644 --- a/src/openstack.js +++ b/src/openstack.js @@ -1,6 +1,7 @@ import Keystone from "./keystone"; import Neutron from "./neutron"; import Glance from "./glance"; +import Nova from "./nova"; export default class OpenStack { /** @@ -45,6 +46,16 @@ export default class OpenStack { .then((glance) => glance.imageList(this._token)); } + /** + * List the flavors available on nova. + * + * @returns {Promise.} A promise which will resolve with the list of flavors. + */ + flavorList() { + return this._nova + .then((nova) => nova.flavorList(this._token)); + } + /** * Keystone component. * @@ -87,6 +98,20 @@ export default class OpenStack { return this._glancePromise; } + /** + * Nova component. + * + * @returns {Promise.} A promise which will resolve with Nova instance. + * @private + */ + get _nova() { + if (!this._novaPromise) { + this._novaPromise = this._getComponentConfigFor('nova') + .then((componentConfig) => new Nova(componentConfig)); + } + return this._novaPromise; + } + /** * Token issued from Keystone. * diff --git a/test/functional/openstackTest.js b/test/functional/openstackTest.js index 4ccd7ba..76ff5be 100644 --- a/test/functional/openstackTest.js +++ b/test/functional/openstackTest.js @@ -33,4 +33,17 @@ describe("OpenStack", () => { }); }); + describe("flavorList()", () => { + it("should return the flavors as an array.", (done) => { + const openstack = new OpenStack(devstackConfig); + + openstack.flavorList() + .then((flavors) => { + expect(flavors.length > 0).toBeTruthy(); + done(); + }) + .catch((error) => done.fail(error)); + }); + }); + }); diff --git a/test/unit/openstackTest.js b/test/unit/openstackTest.js index 1b2b82b..de8ccb1 100644 --- a/test/unit/openstackTest.js +++ b/test/unit/openstackTest.js @@ -3,10 +3,12 @@ import * as openStackMockData from './helpers/data/openstack'; import * as neutronMockData from './helpers/data/neutron'; import * as keystoneMockData from './helpers/data/keystone'; import * as glanceMockData from './helpers/data/glance'; +import * as novaMockData from './helpers/data/nova'; import fetchMock from 'fetch-mock'; import Neutron from "../../src/neutron"; import Keystone from "../../src/keystone"; import Glance from "../../src/glance"; +import Nova from "../../src/nova"; describe("Simple test", () => { @@ -66,6 +68,23 @@ describe("Simple test", () => { }); }); + describe('flavorList', () => { + it('should fetch flavorList from nova', (done) => { + const openstack = new OpenStack(openStackMockData.config()); + const nova = mockNova(openstack); + const flavorsData = novaMockData.flavorList('token').response.flavors; + + spyOn(nova, 'flavorList').and.returnValue(Promise.resolve(flavorsData)); + + openstack.flavorList() + .then((flavors) => { + expect(flavors.length).toBe(12); + done(); + }) + .catch((error) => done.fail(error)); + }); + }); + describe('_neutron', () => { it('creates Neutron instance with the correct endpoint', (done) => { const token = 'test_token'; @@ -176,6 +195,52 @@ describe("Simple test", () => { }); }); + describe('_nova', () => { + it('creates Nova instance with the correct endpoint', (done) => { + const token = 'test_token'; + const openstack = new OpenStack(openStackMockData.config()); + const keystone = mockKeystone(openstack); + const catalogData = keystoneMockData.catalogList(token).response.catalog; + + spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve(token)); + spyOn(keystone, 'catalogList').and.returnValue(Promise.resolve(catalogData)); + + openstack._nova + .then((nova) => { + expect(keystone.catalogList).toHaveBeenCalledWith(token); + expect(nova).toEqual(jasmine.any(Nova)); + expect(nova.endpointUrl).toEqual('http://192.168.99.99:8774/v2.1'); + done(); + }) + .catch((error) => done.fail(error)); + }); + + it('should cache Nova instance and Keystone token', (done) => { + const openstack = new OpenStack(openStackMockData.config()); + const tokenIssueMock = keystoneMockData.tokenIssue(); + const catalogListMock = keystoneMockData.catalogList('test_token'); + + fetchMock.mock(keystoneMockData.root()); + fetchMock.mock(tokenIssueMock); + fetchMock.mock(catalogListMock); + + openstack._nova + .then((nova) => { + expect(nova).toEqual(jasmine.any(Nova)); + expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1); + expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1); + return openstack._nova; + }) + .then((nova) => { + expect(nova).toEqual(jasmine.any(Nova)); + expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1); + expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1); + done(); + }) + .catch((error) => done.fail(error)); + }); + }); + describe('_token', () => { it('should fetch the token and cache it', (done) => { const openstack = new OpenStack(openStackMockData.config()); @@ -216,4 +281,10 @@ describe("Simple test", () => { return glance; } + function mockNova(openstack) { + const nova = new Nova(novaMockData.config); + openstack._novaPromise = Promise.resolve(nova); + return nova; + } + });