Added networkList() method to OpenStack wrapper

This patch adds the networkList() method to the OpenStack wrapper
class. This method will initialize `Neutron` instance if needed and
cache it. As neutron needs the URL, `Keystone` instance is also
initialize and cache if needed.

Change-Id: If12af29a86b9856a145c1f91de25eb3473938de5
This commit is contained in:
Corentin Ardeois 2016-10-05 18:32:20 -04:00
parent 70a98fb10c
commit c330bbdc62
4 changed files with 208 additions and 8 deletions

View File

@ -1,3 +1,6 @@
import Keystone from "./keystone";
import Neutron from "./neutron";
export default class OpenStack {
/**
* Create wrapper class that takes clouds.yaml instance
@ -15,8 +18,75 @@ export default class OpenStack {
this.cloudConfig = cloudConfig;
}
getConfig() {
// Returns the config instance
return this.cloudConfig;
}
/**
* List the networks available.
*
* @returns {Promise.<T>} A promise which will resolve with the list of networks.
*/
networkList() {
return this._neutron
.then((neutron) => neutron.networkList(this._token));
}
/**
* Keystone component.
*
* @returns {Promise.<Keystone>} A promise which will resolve with Keystone instance.
* @private
*/
get _keystone() {
if (!this._keystonePromise) {
this._keystonePromise = Promise.resolve(new Keystone(this.getConfig()));
}
return this._keystonePromise;
}
/**
* Neutron component.
*
* @returns {Promise.<Neutron>} A promise which will resolve with Neutron instance.
* @private
*/
get _neutron() {
if (!this._neutronPromise) {
this._neutronPromise = this._getComponentConfigFor('neutron')
.then((componentConfig) => new Neutron(componentConfig));
}
return this._neutronPromise;
}
/**
* Token issued from Keystone.
*
* @returns {Promise.<T>} A promise which will resolve with the token.
* @private
*/
get _token() {
if (!this._tokenPromise) {
this._tokenPromise = this._keystone.then((k) => k.tokenIssue());
}
return this._tokenPromise;
}
/**
* Return an component config from keystone catalog.
*
* @param {String} name A component name to find.
* @returns {Promise.<{}>} A promise which will resolve with the component config.
* @private
*/
_getComponentConfigFor(name) {
return this._token
.then((token) => this._keystone.then((keystone) => keystone.catalogList(token)))
.then((catalog) => catalog.find((entry) => entry.name === name))
.then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public'));
}
}

View File

@ -52,6 +52,15 @@ export default class AbstractService {
return this._supportedVersions || [];
}
/**
* Our endpoint URL for this service.
*
* @returns {string} The URL of our service.
*/
get endpointUrl() {
return this._endpointUrl;
}
/**
* Retrieve all the API versions available.
*

View File

@ -0,0 +1,23 @@
import config from "./helpers/cloudsConfig";
import OpenStack from "../../src/openstack";
import log from 'loglevel';
log.setLevel("DEBUG");
describe("OpenStack", () => {
let devstackConfig = config.clouds.devstack;
describe("networkList()", () => {
it("should return the networks as an array.", (done) => {
const openstack = new OpenStack(devstackConfig);
openstack.networkList()
.then((networks) => {
expect(networks.length > 0).toBeTruthy();
done();
})
.catch((error) => done.fail(error));
});
});
});

View File

@ -1,16 +1,17 @@
import OpenStack from "../../src/openstack";
import * as mockData from './helpers/data/openstack';
const FetchMock = require('fetch-mock');
import * as openStackMockData from './helpers/data/openstack';
import * as neutronMockData from './helpers/data/neutron';
import * as keystoneMockData from './helpers/data/keystone';
import fetchMock from 'fetch-mock';
import Neutron from "../../src/neutron";
import Keystone from "../../src/keystone";
describe("Simple test", () => {
afterEach(() => {
FetchMock.reset();
});
afterEach(fetchMock.restore);
it("should export a class", () => {
let t = new OpenStack(mockData.config);
let t = new OpenStack(openStackMockData.config);
expect(t).toBeDefined();
});
@ -24,9 +25,106 @@ describe("Simple test", () => {
});
it("getConfig should returns the config", () => {
let openstack = new OpenStack(mockData.config);
let openstack = new OpenStack(openStackMockData.config);
let config = openstack.getConfig();
expect(config.region_name).toEqual('Region1');
});
describe('networkList', () => {
it('should fetch networkList from neutron', (done) => {
const openstack = new OpenStack(openStackMockData.config);
const neutron = mockNeutron(openstack);
const networksData = neutronMockData.networkList('token').response.networks;
spyOn(neutron, 'networkList').and.returnValue(Promise.resolve(networksData));
openstack.networkList()
.then((networks) => {
expect(networks.length).toBe(2);
done();
})
.catch((error) => done.fail(error));
});
});
describe('_neutron', () => {
it('creates Neutron 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._neutron
.then((neutron) => {
expect(keystone.catalogList).toHaveBeenCalledWith(token);
expect(neutron).toEqual(jasmine.any(Neutron));
expect(neutron.endpointUrl).toEqual('http://192.168.99.99:9696/');
done();
})
.catch((error) => done.fail(error));
});
it('should cache Neutron 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._neutron
.then((neutron) => {
expect(neutron).toEqual(jasmine.any(Neutron));
expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1);
expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1);
return openstack._neutron;
})
.then((neutron) => {
expect(neutron).toEqual(jasmine.any(Neutron));
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);
const keystone = mockKeystone(openstack);
spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve('test_token'));
openstack._token
.then((token) => {
expect(token).toEqual('test_token');
expect(keystone.tokenIssue.calls.count()).toEqual(1);
return openstack._token;
})
.then((token) => {
expect(token).toEqual('test_token');
expect(keystone.tokenIssue.calls.count()).toEqual(1);
done();
})
.catch((error) => done.fail(error));
});
});
function mockKeystone(openstack) {
const keystone = new Keystone(keystoneMockData.config);
openstack._keystonePromise = Promise.resolve(keystone);
return keystone;
}
function mockNeutron(openstack) {
const neutron = new Neutron(neutronMockData.config);
openstack._neutronPromise = Promise.resolve(neutron);
return neutron;
}
});