From d43b71b443878f3a1459cab84a37f08d09844531 Mon Sep 17 00:00:00 2001 From: Lucas Palm Date: Thu, 3 Mar 2016 14:03:01 -0600 Subject: [PATCH] Add the Angular LBaaS V2 'Create Pool' workflow This change adds the Create Pool workflow action to the listeners table and the listeners detail page. This wizard allows you to create a new pool, as well as any of the pool's child resources, including members and a health monitor. Partially-Implements: blueprint horizon-lbaas-v2-ui Change-Id: I934921f207373311b8b65f2ba34546238e961ac9 --- neutron_lbaas_dashboard/api/rest/lbaasv2.py | 19 ++ .../openstack-service-api/lbaasv2.service.js | 16 ++ .../lbaasv2.service.spec.js | 8 + .../listeners/actions/row-actions.service.js | 12 +- .../actions/row-actions.service.spec.js | 5 +- .../actions/create/create.action.service.js | 89 ++++++ .../create/create.action.service.spec.js | 111 ++++++++ .../pools/actions/create/wizard.controller.js | 44 +++ .../actions/create/wizard.controller.spec.js | 63 +++++ .../project/lbaasv2/workflow/model.service.js | 59 ++-- .../lbaasv2/workflow/model.service.spec.js | 253 +++++++++++++++++- 11 files changed, 652 insertions(+), 27 deletions(-) create mode 100644 neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.js create mode 100644 neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.spec.js create mode 100644 neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.js create mode 100644 neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.spec.js diff --git a/neutron_lbaas_dashboard/api/rest/lbaasv2.py b/neutron_lbaas_dashboard/api/rest/lbaasv2.py index ffe3a0fa..86059a07 100644 --- a/neutron_lbaas_dashboard/api/rest/lbaasv2.py +++ b/neutron_lbaas_dashboard/api/rest/lbaasv2.py @@ -560,6 +560,25 @@ class Listener(generic.View): neutronclient(request).delete_listener(listener_id) +@urls.register +class Pools(generic.View): + """API for load balancer pools. + + """ + url_regex = r'lbaas/pools/$' + + @rest_utils.ajax() + def post(self, request): + """Create a new pool. + + Creates a new pool as well as other optional resources such as + members and health monitor. + """ + kwargs = {'loadbalancer_id': request.DATA.get('loadbalancer_id'), + 'listener_id': request.DATA.get('parentResourceId')} + return create_pool(request, **kwargs) + + @urls.register class Pool(generic.View): """API for retrieving a single pool. diff --git a/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js b/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js index 21d0d2fa..5860b7ad 100644 --- a/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js +++ b/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js @@ -48,6 +48,7 @@ deleteListener: deleteListener, getPool: getPool, deletePool: deletePool, + createPool: createPool, getMembers: getMembers, getMember: getMember, getHealthMonitor: getHealthMonitor @@ -249,6 +250,21 @@ }); } + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.createPool + * @description + * Create a new pool + * @param {object} spec + * Specifies the data used to create the new pool. + */ + + function createPool(spec) { + return apiService.post('/api/lbaas/pools/', spec) + .error(function () { + toastService.add('error', gettext('Unable to create pool.')); + }); + } + /** * @name horizon.app.core.openstack-service-api.lbaasv2.deletePool * @description diff --git a/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js b/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js index c620bb96..4c2bb19d 100644 --- a/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js +++ b/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js @@ -163,6 +163,14 @@ path: '/api/lbaas/listeners/1234', error: 'Unable to delete listener.', testInput: [ '1234' ] + }, + { + func: 'createPool', + method: 'post', + path: '/api/lbaas/pools/', + error: 'Unable to create pool.', + data: { name: 'pool-1' }, + testInput: [ { name: 'pool-1' } ] } ]; diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.js index bb6cc282..83d7a392 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.js @@ -28,7 +28,8 @@ 'horizon.app.core.openstack-service-api.policy', 'horizon.framework.util.i18n.gettext', 'horizon.dashboard.project.lbaasv2.loadbalancers.service', - 'horizon.dashboard.project.lbaasv2.listeners.actions.delete' + 'horizon.dashboard.project.lbaasv2.listeners.actions.delete', + 'horizon.dashboard.project.lbaasv2.pools.actions.create' ]; /** @@ -45,11 +46,13 @@ * @param gettext The horizon gettext function for translation. * @param loadBalancersService The LBaaS v2 load balancers service. * @param deleteService The LBaaS v2 listeners delete service. + * @param createPoolService The LBaaS v2 pools create service. * @returns Listeners row actions service object. */ function tableRowActions( - $q, $route, workflowModal, policy, gettext, loadBalancersService, deleteService + $q, $route, workflowModal, policy, gettext, loadBalancersService, deleteService, + createPoolService ) { var loadbalancerId, loadBalancerIsActionable, handler; @@ -82,6 +85,11 @@ template: { text: gettext('Edit') } + },{ + service: createPoolService.init(loadbalancerId, loadBalancerIsActionable).create, + template: { + text: gettext('Create Pool') + } },{ service: deleteService.init(loadbalancerId, loadBalancerIsActionable, handler), template: { diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.spec.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.spec.js index 4c7f40a8..f25d025e 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.spec.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/listeners/actions/row-actions.service.spec.js @@ -82,9 +82,10 @@ })); it('should define correct table row actions', function() { - expect(actions.length).toBe(2); + expect(actions.length).toBe(3); expect(actions[0].template.text).toBe('Edit'); - expect(actions[1].template.text).toBe('Delete Listener'); + expect(actions[1].template.text).toBe('Create Pool'); + expect(actions[2].template.text).toBe('Delete Listener'); }); it('should allow editing a listener of an ACTIVE load balancer', function() { diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.js new file mode 100644 index 00000000..25115f13 --- /dev/null +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.js @@ -0,0 +1,89 @@ +/* + * Copyright 2016 IBM Corp. + * + * 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. + */ +(function() { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.pools') + .factory('horizon.dashboard.project.lbaasv2.pools.actions.create', createService); + + createService.$inject = [ + '$q', + '$location', + 'horizon.dashboard.project.lbaasv2.workflow.modal', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.q.extensions', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngDoc factory + * @name horizon.dashboard.project.lbaasv2.listeners.actions.createService + * @description + * Provides the service for creating a pool resource. + * @param $q The angular service for promises. + * @param $location The angular $location service. + * @param workflowModal The LBaaS workflow modal service. + * @param policy The horizon policy service. + * @param qExtensions Horizon extensions to the $q service. + * @param gettext The horizon gettext function for translation. + * @returns The load balancers pool create service. + */ + + function createService( + $q, $location, workflowModal, policy, qExtensions, gettext + ) { + var loadbalancerId, listenerId, statePromise; + + var create = workflowModal.init({ + controller: 'CreatePoolWizardController', + message: gettext('A new pool is being created.'), + handle: onCreate, + allowed: allowed + }); + + var service = { + init: init, + create: create + }; + + return service; + + ////////////// + + function init(_loadbalancerId_, _statePromise_) { + loadbalancerId = _loadbalancerId_; + statePromise = _statePromise_; + return service; + } + + function allowed(item) { + listenerId = item.id; + return $q.all([ + statePromise, + qExtensions.booleanAsPromise(!item.default_pool_id), + policy.ifAllowed({ rules: [['neutron', 'create_pool']] }) + ]); + } + + function onCreate(response) { + var poolId = response.data.id; + $location.path('project/ngloadbalancersv2/' + loadbalancerId + '/listeners/' + + listenerId + '/pools/' + poolId); + } + + } +})(); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.spec.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.spec.js new file mode 100644 index 00000000..f31db715 --- /dev/null +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/create.action.service.spec.js @@ -0,0 +1,111 @@ +/* + * Copyright 2016 IBM Corp. + * + * 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. + */ +(function() { + 'use strict'; + + describe('LBaaS v2 Create Pool Action Service', function() { + var scope, $q, $location, policy, init, createPoolService, defer; + + function allowed(item) { + spyOn(policy, 'ifAllowed').and.returnValue(true); + var promise = createPoolService.create.allowed(item); + var allowed; + promise.then(function() { + allowed = true; + }, function() { + allowed = false; + }); + scope.$apply(); + expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'create_pool']]}); + return allowed; + } + + beforeEach(module('horizon.framework.util')); + beforeEach(module('horizon.framework.conf')); + beforeEach(module('horizon.framework.widgets')); + beforeEach(module('horizon.app.core.openstack-service-api')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + + beforeEach(module(function($provide) { + var response = { + data: { + id: '9012' + } + }; + var modal = { + open: function() { + return { + result: { + then: function(func) { + func(response); + } + } + }; + } + }; + $provide.value('$modal', modal); + })); + + beforeEach(inject(function ($injector) { + scope = $injector.get('$rootScope').$new(); + $q = $injector.get('$q'); + policy = $injector.get('horizon.app.core.openstack-service-api.policy'); + $location = $injector.get('$location'); + createPoolService = $injector.get( + 'horizon.dashboard.project.lbaasv2.pools.actions.create'); + init = createPoolService.init; + defer = $q.defer(); + })); + + it('should define the correct service properties', function() { + expect(createPoolService.init).toBeDefined(); + expect(createPoolService.create).toBeDefined(); + }); + + it('should have the "allowed" and "perform" functions', function() { + expect(createPoolService.create.allowed).toBeDefined(); + expect(createPoolService.create.perform).toBeDefined(); + }); + + it('should allow creating a pool under an ACTIVE load balancer', function() { + defer.resolve(); + init('active', defer.promise); + expect(allowed({default_pool_id: ''})).toBe(true); + }); + + it('should not allow creating a pool under an NON-ACTIVE load balancer', function() { + defer.reject(); + init('non-active', defer.promise); + expect(allowed({default_pool_id: ''})).toBe(false); + }); + + it('should not allow creating a pool if a listener pool already exists', function() { + defer.resolve(); + init('active', defer.promise); + expect(allowed({default_pool_id: '1234'})).toBe(false); + }); + + it('should redirect after create', function() { + defer.resolve(); + spyOn($location, 'path').and.callThrough(); + init('1234', defer.promise).create.allowed({id: '5678'}); + createPoolService.create.perform(); + expect($location.path).toHaveBeenCalledWith( + 'project/ngloadbalancersv2/1234/listeners/5678/pools/9012'); + }); + + }); +})(); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.js new file mode 100644 index 00000000..e1cccda0 --- /dev/null +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.js @@ -0,0 +1,44 @@ +/* + * Copyright 2016 IBM Corp. + * + * 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. + */ +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.pools') + .controller('CreatePoolWizardController', CreatePoolWizardController); + + CreatePoolWizardController.$inject = [ + '$scope', + '$routeParams', + 'horizon.dashboard.project.lbaasv2.workflow.model', + 'horizon.dashboard.project.lbaasv2.workflow.workflow', + 'horizon.framework.util.i18n.gettext' + ]; + + function CreatePoolWizardController($scope, $routeParams, model, workflowService, gettext) { + var loadbalancerId = $routeParams.loadbalancerId; + var scope = $scope; + scope.model = model; + scope.submit = scope.model.submit; + scope.workflow = workflowService( + gettext('Create Pool'), + 'fa fa-cloud-download', + ['pool', 'members', 'monitor'] + ); + scope.model.initialize('pool', false, loadbalancerId, scope.launchContext.id); + } + +})(); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.spec.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.spec.js new file mode 100644 index 00000000..88c89358 --- /dev/null +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/pools/actions/create/wizard.controller.spec.js @@ -0,0 +1,63 @@ +/* + * Copyright 2016 IBM Corp. + * + * 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. + */ +(function () { + 'use strict'; + + describe('LBaaS v2 Create Pool Wizard Controller', function() { + var ctrl; + var model = { + submit: function() { + return 'created'; + }, + initialize: angular.noop + }; + var workflow = function() { + return 'foo'; + }; + var scope = { + launchContext: {id: '1234'} + }; + + beforeEach(module('horizon.framework.util')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module(function ($provide) { + $provide.value('horizon.dashboard.project.lbaasv2.workflow.model', model); + $provide.value('horizon.dashboard.project.lbaasv2.workflow.workflow', workflow); + })); + beforeEach(inject(function ($controller) { + spyOn(model, 'initialize'); + ctrl = $controller('CreatePoolWizardController', { $scope: scope }); + })); + + it('defines the controller', function() { + expect(ctrl).toBeDefined(); + }); + + it('calls initialize on the given model', function() { + expect(model.initialize).toHaveBeenCalled(); + }); + + it('sets scope.workflow to the given workflow', function() { + expect(scope.workflow).toBe('foo'); + }); + + it('defines scope.submit', function() { + expect(scope.submit).toBeDefined(); + expect(scope.submit()).toBe('created'); + }); + }); + +})(); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js index fc33ea37..29eb2e66 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js @@ -117,7 +117,7 @@ * @param id ID of the resource being edited. */ - function initialize(resource, id, loadBalancerId) { + function initialize(resource, id, loadBalancerId, parentResourceId) { var promise; model.certificatesError = false; @@ -133,6 +133,7 @@ model.spec = { loadbalancer_id: loadBalancerId, + parentResourceId: parentResourceId, loadbalancer: { name: null, description: null, @@ -206,6 +207,17 @@ ]).then(initMemberAddresses); model.context.submit = createListener; break; + case 'createpool': + // We get the listener details here because we need to know the listener protocol + // in order to default the new pool's protocol to match. + promise = $q.all([ + lbaasv2API.getListener(model.spec.parentResourceId).then(onGetListener), + neutronAPI.getSubnets().then(onGetSubnets), + neutronAPI.getPorts().then(onGetPorts), + novaAPI.getServers().then(onGetServers) + ]).then(initMemberAddresses); + model.context.submit = createPool; + break; case 'editloadbalancer': promise = $q.all([ lbaasv2API.getLoadBalancer(model.context.id).then(onGetLoadBalancer), @@ -268,6 +280,10 @@ return lbaasv2API.createListener(spec); } + function createPool(spec) { + return lbaasv2API.createPool(spec); + } + function editLoadBalancer(spec) { return lbaasv2API.editLoadBalancer(model.context.id, spec); } @@ -469,36 +485,39 @@ } function onGetListener(response) { - var resources = response.data; + var result = response.data; - setListenerSpec(resources.listener); - model.visibleResources.push('listener'); - model.spec.loadbalancer_id = resources.listener.loadbalancers[0].id; + setListenerSpec(result.listener || result); - if (resources.listener.protocol === 'TERMINATED_HTTPS') { - keymanagerPromise.then(prepareCertificates).then(function addAvailableCertificates() { - resources.listener.sni_container_refs.forEach(function addAvailableCertificate(ref) { - model.certificates.filter(function matchCertificate(cert) { - return cert.id === ref; - }).forEach(function addCertificate(cert) { - model.spec.certificates.push(cert); + if (result.listener) { + model.visibleResources.push('listener'); + model.spec.loadbalancer_id = result.listener.loadbalancers[0].id; + + if (result.listener.protocol === 'TERMINATED_HTTPS') { + keymanagerPromise.then(prepareCertificates).then(function addAvailableCertificates() { + result.listener.sni_container_refs.forEach(function addAvailableCertificate(ref) { + model.certificates.filter(function matchCertificate(cert) { + return cert.id === ref; + }).forEach(function addCertificate(cert) { + model.spec.certificates.push(cert); + }); }); }); - }); - model.visibleResources.push('certificates'); + model.visibleResources.push('certificates'); + } } - if (resources.pool) { - setPoolSpec(resources.pool); + if (result.pool) { + setPoolSpec(result.pool); model.visibleResources.push('pool'); model.visibleResources.push('members'); - if (resources.members) { - setMembersSpec(resources.members); + if (result.members) { + setMembersSpec(result.members); } - if (resources.monitor) { - setMonitorSpec(resources.monitor); + if (result.monitor) { + setMonitorSpec(result.monitor); model.visibleResources.push('monitor'); } } diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js index 9dcec7b7..95444348 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js @@ -18,6 +18,7 @@ describe('LBaaS v2 Workflow Model Service', function() { var model, $q, scope, listenerResources, barbicanEnabled, certificatesError; + var includeChildResources = true; beforeEach(module('horizon.framework.util.i18n')); beforeEach(module('horizon.dashboard.project.lbaasv2')); @@ -111,7 +112,9 @@ }, getListener: function() { var deferred = $q.defer(); - deferred.resolve({ data: listenerResources }); + var listenerData; + listenerData = includeChildResources ? listenerResources : listenerResources.listener; + deferred.resolve({ data: listenerData }); return deferred.promise; }, getPool: function() { @@ -178,6 +181,9 @@ }, editListener: function(id, spec) { return spec; + }, + createPool: function(spec) { + return spec; } }); @@ -421,6 +427,44 @@ }); }); + describe('Post initialize model (create pool)', function() { + + beforeEach(function() { + includeChildResources = false; + model.initialize('pool', false, '1234', '5678'); + scope.$apply(); + }); + + it('should initialize model properties', function() { + expect(model.initializing).toBe(false); + expect(model.initialized).toBe(true); + expect(model.subnets.length).toBe(2); + expect(model.members.length).toBe(2); + expect(model.certificates.length).toBe(0); + expect(model.listenerPorts.length).toBe(0); + expect(model.spec).toBeDefined(); + expect(model.spec.loadbalancer_id).toBe('1234'); + expect(model.spec.parentResourceId).toBe('5678'); + expect(model.spec.loadbalancer).toBeDefined(); + expect(model.spec.listener).toBeDefined(); + expect(model.spec.pool).toBeDefined(); + expect(model.spec.members.length).toBe(0); + expect(model.spec.certificates).toEqual([]); + expect(model.spec.monitor).toBeDefined(); + expect(model.certificatesError).toBe(false); + }); + + it('should initialize names', function() { + expect(model.spec.pool.name).toBe('Pool 1'); + }); + + it('should initialize context properties', function() { + expect(model.context.resource).toBe('pool'); + expect(model.context.id).toBeFalsy(); + expect(model.context.submit).toBeDefined(); + }); + }); + describe('Post initialize model (edit loadbalancer)', function() { beforeEach(function() { @@ -526,6 +570,7 @@ describe('Post initialize model (edit listener)', function() { beforeEach(function() { + includeChildResources = true; model.initialize('listener', '1234'); scope.$apply(); }); @@ -644,7 +689,7 @@ // This is here to ensure that as people add/change spec properties, they don't forget // to implement tests for them. it('has the right number of properties', function() { - expect(Object.keys(model.spec).length).toBe(7); + expect(Object.keys(model.spec).length).toBe(8); expect(Object.keys(model.spec.loadbalancer).length).toBe(4); expect(Object.keys(model.spec.listener).length).toBe(5); expect(Object.keys(model.spec.pool).length).toBe(5); @@ -652,10 +697,14 @@ expect(model.spec.members).toEqual([]); }); - it('sets load balancer ID to null', function() { + it('sets load balancer ID to undefined', function() { expect(model.spec.loadbalancer_id).toBeUndefined(); }); + it('sets parent resource ID to undefined', function() { + expect(model.spec.parentResourceId).toBeUndefined(); + }); + it('sets load balancer name to null', function() { expect(model.spec.loadbalancer.name).toBeNull(); }); @@ -832,6 +881,7 @@ describe('context (edit listener)', function() { beforeEach(function() { + includeChildResources = true; model.initialize('listener', '1'); scope.$apply(); }); @@ -843,6 +893,20 @@ }); }); + describe('context (create pool)', function() { + + beforeEach(function() { + model.initialize('pool', false, '1234', '5678'); + scope.$apply(); + }); + + it('should initialize context', function() { + expect(model.context.resource).toBe('pool'); + expect(model.context.id).toBeFalsy(); + expect(model.context.submit.name).toBe('createPool'); + }); + }); + describe('Model submit function (create loadbalancer)', function() { beforeEach(function() { @@ -1297,9 +1361,192 @@ }); }); + describe('Model submit function (create pool)', function() { + + beforeEach(function() { + includeChildResources = false; + model.initialize('pool', false, '1234', '5678'); + scope.$apply(); + }); + + it('should set final spec properties', function() { + model.spec.listener.protocol = 'TCP'; + model.spec.pool.name = 'pool name'; + model.spec.pool.description = 'pool description'; + model.spec.pool.method = 'LEAST_CONNECTIONS'; + model.spec.members = [{ + address: { ip: '1.2.3.4', subnet: '1' }, + addresses: [{ ip: '1.2.3.4', subnet: '1' }, + { ip: '2.3.4.5', subnet: '2' }], + id: '1', + name: 'foo', + port: 80, + weight: 1 + }, { + id: 'external-member-0', + address: '2.3.4.5', + subnet: null, + port: 80, + weight: 1 + }, { + id: 'external-member-1', + address: null, + subnet: null, + port: 80, + weight: 1 + }, { + id: 'external-member-2', + address: '3.4.5.6', + subnet: { id: '1' }, + port: 80, + weight: 1 + }]; + model.spec.monitor.type = 'PING'; + model.spec.monitor.interval = 1; + model.spec.monitor.retry = 1; + model.spec.monitor.timeout = 1; + model.spec.certificates = [{ + id: 'container1', + name: 'foo', + expiration: '2015-03-26T21:10:45.417835' + }]; + + var finalSpec = model.submit(); + + expect(finalSpec.pool.name).toBe('pool name'); + expect(finalSpec.pool.description).toBe('pool description'); + expect(finalSpec.pool.protocol).toBe('TCP'); + expect(finalSpec.pool.method).toBe('LEAST_CONNECTIONS'); + + expect(finalSpec.members.length).toBe(3); + expect(finalSpec.members[0].address).toBe('1.2.3.4'); + expect(finalSpec.members[0].subnet).toBe('1'); + expect(finalSpec.members[0].port).toBe(80); + expect(finalSpec.members[0].weight).toBe(1); + expect(finalSpec.members[0].id).toBe('1'); + expect(finalSpec.members[0].addresses).toBeUndefined(); + expect(finalSpec.members[0].name).toBeUndefined(); + expect(finalSpec.members[0].allocatedMember).toBeUndefined(); + expect(finalSpec.members[1].id).toBe('external-member-0'); + expect(finalSpec.members[1].address).toBe('2.3.4.5'); + expect(finalSpec.members[1].subnet).toBeUndefined(); + expect(finalSpec.members[1].port).toBe(80); + expect(finalSpec.members[1].weight).toBe(1); + expect(finalSpec.members[1].allocatedMember).toBeUndefined(); + expect(finalSpec.members[2].id).toBe('external-member-2'); + expect(finalSpec.members[2].address).toBe('3.4.5.6'); + expect(finalSpec.members[2].subnet).toBe('1'); + expect(finalSpec.members[2].port).toBe(80); + expect(finalSpec.members[2].weight).toBe(1); + expect(finalSpec.members[2].allocatedMember).toBeUndefined(); + + expect(finalSpec.monitor.type).toBe('PING'); + expect(finalSpec.monitor.interval).toBe(1); + expect(finalSpec.monitor.retry).toBe(1); + expect(finalSpec.monitor.timeout).toBe(1); + expect(finalSpec.certificates).toBeUndefined(); + }); + + it('should delete listener if any required property is not set', function() { + model.spec.loadbalancer.ip = '1.2.3.4'; + model.spec.loadbalancer.subnet = model.subnets[0]; + model.spec.listener.protocol = 'HTTP'; + model.spec.listener.port = ''; + + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer).toBeDefined(); + expect(finalSpec.listener).toBeUndefined(); + expect(finalSpec.pool).toBeUndefined(); + }); + + it('should delete certificates if not using TERMINATED_HTTPS', function() { + model.spec.loadbalancer.ip = '1.2.3.4'; + model.spec.loadbalancer.subnet = model.subnets[0]; + model.spec.listener.protocol = 'HTTP'; + model.spec.listener.port = 80; + model.spec.certificates = [{id: '1'}]; + + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer).toBeDefined(); + expect(finalSpec.listener).toBeDefined(); + expect(finalSpec.certificates).toBeUndefined(); + }); + + it('should delete pool if any required property is not set', function() { + model.spec.loadbalancer.ip = '1.2.3.4'; + model.spec.loadbalancer.subnet = model.subnets[0]; + model.spec.listener.protocol = 'HTTP'; + model.spec.listener.port = 80; + + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer).toBeDefined(); + expect(finalSpec.listener).toBeDefined(); + expect(finalSpec.pool).toBeUndefined(); + }); + + it('should delete members if none selected', function() { + model.spec.loadbalancer.ip = '1.2.3.4'; + model.spec.loadbalancer.subnet = model.subnets[0]; + model.spec.listener.protocol = 'HTTP'; + model.spec.listener.port = 80; + model.spec.pool.method = 'LEAST_CONNECTIONS'; + + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer).toBeDefined(); + expect(finalSpec.listener).toBeDefined(); + expect(finalSpec.pool).toBeDefined(); + expect(finalSpec.members).toBeUndefined(); + }); + + it('should delete members if no members are valid', function() { + model.spec.loadbalancer.ip = '1.2.3.4'; + model.spec.loadbalancer.subnet = model.subnets[0]; + model.spec.listener.protocol = 'HTTP'; + model.spec.listener.port = 80; + model.spec.pool.method = 'LEAST_CONNECTIONS'; + model.spec.members = [{ + id: 'foo', + address: '2.3.4.5', + weight: 1 + }]; + + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer).toBeDefined(); + expect(finalSpec.listener).toBeDefined(); + expect(finalSpec.pool).toBeDefined(); + expect(finalSpec.members).toBeUndefined(); + }); + + it('should delete monitor if any required property not set', function() { + model.spec.loadbalancer.ip = '1.2.3.4'; + model.spec.loadbalancer.subnet = model.subnets[0]; + model.spec.listener.protocol = 'HTTP'; + model.spec.listener.port = 80; + model.spec.pool.method = 'LEAST_CONNECTIONS'; + model.spec.monitor.type = 'PING'; + model.spec.monitor.interval = 1; + model.spec.monitor.retry = 1; + model.spec.monitor.timeout = null; + + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer).toBeDefined(); + expect(finalSpec.listener).toBeDefined(); + expect(finalSpec.pool).toBeDefined(); + expect(finalSpec.members).toBeUndefined(); + expect(finalSpec.monitor).toBeUndefined(); + }); + }); + describe('Model submit function (edit listener)', function() { beforeEach(function() { + includeChildResources = true; model.initialize('listener', '1234'); scope.$apply(); });