Browse Source

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
Corentin Ardeois 2 years ago
parent
commit
c330bbdc62
4 changed files with 208 additions and 8 deletions
  1. 70
    0
      src/openstack.js
  2. 9
    0
      src/util/abstractService.js
  3. 23
    0
      test/functional/openstackTest.js
  4. 106
    8
      test/unit/openstackTest.js

+ 70
- 0
src/openstack.js View File

@@ -1,3 +1,6 @@
1
+import Keystone from "./keystone";
2
+import Neutron from "./neutron";
3
+
1 4
 export default class OpenStack {
2 5
   /**
3 6
    * Create wrapper class that takes clouds.yaml instance
@@ -15,8 +18,75 @@ export default class OpenStack {
15 18
 
16 19
     this.cloudConfig = cloudConfig;
17 20
   }
21
+
18 22
   getConfig() {
19 23
     // Returns the config instance
20 24
     return this.cloudConfig;
21 25
   }
26
+
27
+  /**
28
+   * List the networks available.
29
+   *
30
+   * @returns {Promise.<T>} A promise which will resolve with the list of networks.
31
+   */
32
+  networkList() {
33
+    return this._neutron
34
+      .then((neutron) => neutron.networkList(this._token));
35
+  }
36
+
37
+  /**
38
+   * Keystone component.
39
+   *
40
+   * @returns {Promise.<Keystone>} A promise which will resolve with Keystone instance.
41
+   * @private
42
+   */
43
+  get _keystone() {
44
+    if (!this._keystonePromise) {
45
+      this._keystonePromise = Promise.resolve(new Keystone(this.getConfig()));
46
+    }
47
+
48
+    return this._keystonePromise;
49
+  }
50
+
51
+  /**
52
+   * Neutron component.
53
+   *
54
+   * @returns {Promise.<Neutron>} A promise which will resolve with Neutron instance.
55
+   * @private
56
+   */
57
+  get _neutron() {
58
+    if (!this._neutronPromise) {
59
+      this._neutronPromise = this._getComponentConfigFor('neutron')
60
+        .then((componentConfig) => new Neutron(componentConfig));
61
+    }
62
+    return this._neutronPromise;
63
+  }
64
+
65
+  /**
66
+   * Token issued from Keystone.
67
+   *
68
+   * @returns {Promise.<T>} A promise which will resolve with the token.
69
+   * @private
70
+   */
71
+  get _token() {
72
+    if (!this._tokenPromise) {
73
+      this._tokenPromise = this._keystone.then((k) => k.tokenIssue());
74
+    }
75
+    return this._tokenPromise;
76
+  }
77
+
78
+  /**
79
+   * Return an component config from keystone catalog.
80
+   *
81
+   * @param {String} name A component name to find.
82
+   * @returns {Promise.<{}>} A promise which will resolve with the component config.
83
+   * @private
84
+   */
85
+  _getComponentConfigFor(name) {
86
+    return this._token
87
+      .then((token) => this._keystone.then((keystone) => keystone.catalogList(token)))
88
+      .then((catalog) => catalog.find((entry) => entry.name === name))
89
+      .then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public'));
90
+  }
91
+
22 92
 }

+ 9
- 0
src/util/abstractService.js View File

@@ -52,6 +52,15 @@ export default class AbstractService {
52 52
     return this._supportedVersions || [];
53 53
   }
54 54
 
55
+  /**
56
+   * Our endpoint URL for this service.
57
+   *
58
+   * @returns {string} The URL of our service.
59
+   */
60
+  get endpointUrl() {
61
+    return this._endpointUrl;
62
+  }
63
+
55 64
   /**
56 65
    * Retrieve all the API versions available.
57 66
    *

+ 23
- 0
test/functional/openstackTest.js View File

@@ -0,0 +1,23 @@
1
+import config from "./helpers/cloudsConfig";
2
+import OpenStack from "../../src/openstack";
3
+import log from 'loglevel';
4
+
5
+log.setLevel("DEBUG");
6
+
7
+describe("OpenStack", () => {
8
+  let devstackConfig = config.clouds.devstack;
9
+
10
+  describe("networkList()", () => {
11
+    it("should return the networks as an array.", (done) => {
12
+      const openstack = new OpenStack(devstackConfig);
13
+
14
+      openstack.networkList()
15
+        .then((networks) => {
16
+          expect(networks.length > 0).toBeTruthy();
17
+          done();
18
+        })
19
+        .catch((error) => done.fail(error));
20
+    });
21
+  });
22
+
23
+});

+ 106
- 8
test/unit/openstackTest.js View File

@@ -1,16 +1,17 @@
1 1
 import OpenStack from "../../src/openstack";
2
-import * as mockData from './helpers/data/openstack';
3
-
4
-const FetchMock = require('fetch-mock');
2
+import * as openStackMockData from './helpers/data/openstack';
3
+import * as neutronMockData from './helpers/data/neutron';
4
+import * as keystoneMockData from './helpers/data/keystone';
5
+import fetchMock from 'fetch-mock';
6
+import Neutron from "../../src/neutron";
7
+import Keystone from "../../src/keystone";
5 8
 
6 9
 describe("Simple test", () => {
7 10
 
8
-  afterEach(() => {
9
-    FetchMock.reset();
10
-  });
11
+  afterEach(fetchMock.restore);
11 12
 
12 13
   it("should export a class", () => {
13
-    let t = new OpenStack(mockData.config);
14
+    let t = new OpenStack(openStackMockData.config);
14 15
     expect(t).toBeDefined();
15 16
   });
16 17
 
@@ -24,9 +25,106 @@ describe("Simple test", () => {
24 25
   });
25 26
 
26 27
   it("getConfig should returns the config", () => {
27
-    let openstack = new OpenStack(mockData.config);
28
+    let openstack = new OpenStack(openStackMockData.config);
28 29
     let config = openstack.getConfig();
29 30
     expect(config.region_name).toEqual('Region1');
30 31
   });
31 32
 
33
+  describe('networkList', () => {
34
+    it('should fetch networkList from neutron', (done) => {
35
+      const openstack = new OpenStack(openStackMockData.config);
36
+      const neutron = mockNeutron(openstack);
37
+      const networksData = neutronMockData.networkList('token').response.networks;
38
+
39
+      spyOn(neutron, 'networkList').and.returnValue(Promise.resolve(networksData));
40
+
41
+      openstack.networkList()
42
+        .then((networks) => {
43
+          expect(networks.length).toBe(2);
44
+          done();
45
+        })
46
+        .catch((error) => done.fail(error));
47
+    });
48
+  });
49
+
50
+  describe('_neutron', () => {
51
+    it('creates Neutron instance with the correct endpoint', (done) => {
52
+      const token = 'test_token';
53
+      const openstack = new OpenStack(openStackMockData.config);
54
+      const keystone = mockKeystone(openstack);
55
+      const catalogData = keystoneMockData.catalogList(token).response.catalog;
56
+
57
+      spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve(token));
58
+      spyOn(keystone, 'catalogList').and.returnValue(Promise.resolve(catalogData));
59
+
60
+      openstack._neutron
61
+        .then((neutron) => {
62
+          expect(keystone.catalogList).toHaveBeenCalledWith(token);
63
+          expect(neutron).toEqual(jasmine.any(Neutron));
64
+          expect(neutron.endpointUrl).toEqual('http://192.168.99.99:9696/');
65
+          done();
66
+        })
67
+        .catch((error) => done.fail(error));
68
+    });
69
+
70
+    it('should cache Neutron instance and Keystone token', (done) => {
71
+      const openstack = new OpenStack(openStackMockData.config);
72
+      const tokenIssueMock = keystoneMockData.tokenIssue();
73
+      const catalogListMock = keystoneMockData.catalogList('test_token');
74
+
75
+      fetchMock.mock(keystoneMockData.root());
76
+      fetchMock.mock(tokenIssueMock);
77
+      fetchMock.mock(catalogListMock);
78
+
79
+      openstack._neutron
80
+        .then((neutron) => {
81
+          expect(neutron).toEqual(jasmine.any(Neutron));
82
+          expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1);
83
+          expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1);
84
+          return openstack._neutron;
85
+        })
86
+        .then((neutron) => {
87
+          expect(neutron).toEqual(jasmine.any(Neutron));
88
+          expect(fetchMock.calls(tokenIssueMock.matcher).length).toEqual(1);
89
+          expect(fetchMock.calls(catalogListMock.matcher).length).toEqual(1);
90
+          done();
91
+        })
92
+        .catch((error) => done.fail(error));
93
+    });
94
+  });
95
+
96
+  describe('_token', () => {
97
+    it('should fetch the token and cache it', (done) => {
98
+      const openstack = new OpenStack(openStackMockData.config);
99
+      const keystone = mockKeystone(openstack);
100
+
101
+      spyOn(keystone, 'tokenIssue').and.returnValue(Promise.resolve('test_token'));
102
+
103
+      openstack._token
104
+        .then((token) => {
105
+          expect(token).toEqual('test_token');
106
+          expect(keystone.tokenIssue.calls.count()).toEqual(1);
107
+          return openstack._token;
108
+        })
109
+        .then((token) => {
110
+          expect(token).toEqual('test_token');
111
+          expect(keystone.tokenIssue.calls.count()).toEqual(1);
112
+          done();
113
+        })
114
+        .catch((error) => done.fail(error));
115
+    });
116
+  });
117
+
118
+  function mockKeystone(openstack) {
119
+    const keystone = new Keystone(keystoneMockData.config);
120
+    openstack._keystonePromise = Promise.resolve(keystone);
121
+    return keystone;
122
+  }
123
+
124
+  function mockNeutron(openstack) {
125
+    const neutron = new Neutron(neutronMockData.config);
126
+    openstack._neutronPromise = Promise.resolve(neutron);
127
+    return neutron;
128
+  }
129
+
32 130
 });

Loading…
Cancel
Save