From b033ad652e35d71e7baf3dd1238991e067d2ec2c Mon Sep 17 00:00:00 2001 From: Corentin Ardeois Date: Wed, 7 Sep 2016 14:43:58 -0400 Subject: [PATCH] Added Neutron Service This neutron service follows the same pattern as the glance service, and provides both version negotiation and endpoint discovery. Change-Id: Ia2f59213eedf6d7acbb02789ee921c13ff391d09 --- configure-devstack.js | 3 +- src/index.js | 1 + src/neutron.js | 56 +++++++++++++++++++++ test/functional/neutronTest.js | 83 +++++++++++++++++++++++++++++++ test/unit/helpers/data/neutron.js | 64 ++++++++++++++++++++++++ test/unit/neutronTest.js | 54 ++++++++++++++++++++ 6 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 src/neutron.js create mode 100644 test/functional/neutronTest.js create mode 100644 test/unit/helpers/data/neutron.js create mode 100644 test/unit/neutronTest.js diff --git a/configure-devstack.js b/configure-devstack.js index b59ce8f..b5d2661 100644 --- a/configure-devstack.js +++ b/configure-devstack.js @@ -7,7 +7,8 @@ function getDevstackConfig() { const karmaConfig = karma.parseConfig(path.resolve('./karma.conf.js')); return getCorsConfig('$KEYSTONE_CONF', karmaConfig) + - getCorsConfig('$GLANCE_API_CONF', karmaConfig); + getCorsConfig('$GLANCE_API_CONF', karmaConfig) + + getCorsConfig('$NEUTRON_CONF', karmaConfig); } diff --git a/src/index.js b/src/index.js index 7e548ad..bbff897 100644 --- a/src/index.js +++ b/src/index.js @@ -1,2 +1,3 @@ export {default as Keystone} from './keystone'; export {default as Glance} from './glance'; +export {default as Neutron} from './neutron'; diff --git a/src/neutron.js b/src/neutron.js new file mode 100644 index 0000000..220f2a9 --- /dev/null +++ b/src/neutron.js @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016 Internap. + * + * 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. + */ +import AbstractService from './util/abstractService'; + +/** + * A list of all supported versions. Please keep this array sorted by most recent. + * + * @type {Array} + * @ignore + */ +const supportedNeutronVersions = [ + 'v2.0' +]; + +export default class Neutron extends AbstractService { + + /** + * This class provides direct, idempotent, low-level access to the Neutron API of a specific + * cloud. The constructor requires that you provide a specific neutron interface endpoint + * descriptor, as received from keystone's catalog list. + * + * @example + * { + * region_id: "RegionOne", + * url: "http://127.0.0.1:9696", + * region: "RegionOne", + * interface: "admin", + * id: "4f08823e667345478b6e40fab8373c0f" + * } + * @param {{}} endpointConfig The configuration element for a specific glance endpoint. + */ + constructor(endpointConfig) { + // Sanity checks. + if (!endpointConfig || !endpointConfig.url) { + throw new Error('An endpoint configuration is required.'); + } + // Clone the config, so that this instance is immutable + // at runtime (no modifying the config after the fact). + endpointConfig = Object.assign({}, endpointConfig); + + super(endpointConfig.url, supportedNeutronVersions); + } +} diff --git a/test/functional/neutronTest.js b/test/functional/neutronTest.js new file mode 100644 index 0000000..e73b196 --- /dev/null +++ b/test/functional/neutronTest.js @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Internap. + * + * 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. + */ + +import config from "./helpers/cloudsConfig"; +import Version from '../../src/util/version'; +import Neutron from "../../src/neutron"; +import Keystone from "../../src/keystone"; +import log from 'loglevel'; + +log.setLevel("DEBUG"); + +describe("neutron", () => { + // Create a keystone instance and extract the neutron API endpoint. + let devstackConfig = config.clouds.devstack; + let keystone = new Keystone(devstackConfig); + let tokenPromise = keystone.tokenIssue(); + + let configPromise = tokenPromise + .then((token) => keystone.catalogList(token)) + .then((catalog) => catalog.find((entry) => entry.name === 'neutron')) + .then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public')); + + describe("versions()", () => { + it("should return a list of all versions available on this clouds' neutron", (done) => { + configPromise + .then((config) => new Neutron(config)) + .then((neutron) => neutron.versions()) + .then((versions) => { + // Quick sanity check. + expect(versions.length > 0).toBeTruthy(); + done(); + }) + .catch((error) => done.fail(error)); + }); + }); + + describe("version()", () => { + + const supportedApiVersions = [ + new Version('network 2.0') + ]; + + /** + * This test acts as a canary, to inform the SDK developers that the Neutron API + * has changed in a significant way. + */ + it("should return a supported version.", (done) => { + configPromise + .then((config) => new Neutron(config)) + .then((neutron) => neutron.version()) + .then((version) => { + + // Quick sanity check. + const apiVersion = new Version('network', version.id); + + for (let i = 0; i < supportedApiVersions.length; i++) { + let supportedVersion = supportedApiVersions[i]; + if (apiVersion.equals(supportedVersion)) { + done(); + return; + } + } + fail("Current devstack neutron version is not supported."); + done(); + }) + .catch((error) => done.fail(error)); + }); + }); + +}); diff --git a/test/unit/helpers/data/neutron.js b/test/unit/helpers/data/neutron.js new file mode 100644 index 0000000..48d090f --- /dev/null +++ b/test/unit/helpers/data/neutron.js @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Internap. + * + * 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. + */ + +/** + * This file contains test data for fetchMock, to simplify bootstrapping of unit tests for + * neutron. Most of these are functions, as FetchMock does not perform a safe clone of the + * instances, and may accidentally modify them at runtime. + */ + +/** + * Mock cloud configuration that matches our test data below. This is not a clouds.yaml + * format but a subsection of service endpoint return by keystone's catalog. + */ +const neutronConfig = { + region_id: "RegionOne", + url: "http://192.168.99.99:9696/", + region: "RegionOne", + interface: "public", + id: "0b8b5f0f14904136ab5a4f83f27ec49a" +}; + +/** + * Build a new FetchMock configuration for the root endpoint. + * + * @returns {{}} A full FetchMock configuration for Neutron's Root Resource. + */ +function rootResponse() { + return { + method: 'GET', + matcher: 'http://192.168.99.99:9696/', + response: { + versions: [ + { + status: 'CURRENT', + id: 'v2.0', + links: [ + { + href: 'http://192.168.99.99:9696/v2.0', + rel: 'self' + } + ] + } + ] + } + }; +} + +export { + neutronConfig as config, + rootResponse as root +}; diff --git a/test/unit/neutronTest.js b/test/unit/neutronTest.js new file mode 100644 index 0000000..e899d87 --- /dev/null +++ b/test/unit/neutronTest.js @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 Internap. + * + * 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. + */ + +import Neutron from '../../src/neutron.js'; +import * as mockData from './helpers/data/neutron'; +import fetchMock from 'fetch-mock'; + +describe('neutron', () => { + + afterEach(fetchMock.restore); + + it('should export a class', () => { + const neutron = new Neutron(mockData.config); + expect(neutron).toBeDefined(); + }); + + it('should throw an error for an empty config', () => { + try { + const neutron = new Neutron(); + neutron.versions(); + } catch (e) { + expect(e.message).toEqual('An endpoint configuration is required.'); + } + }); + + describe("versions()", () => { + it("Should return a list of all versions available on this clouds' NEUTRON", (done) => { + const neutron = new Neutron(mockData.config); + + fetchMock.mock(mockData.root()); + + neutron.versions() + .then((versions) => { + // Quick sanity check. + expect(versions.length).toBe(1); + done(); + }) + .catch((error) => done.fail(error)); + }); + }); +});