From c7e5b64978b916b13dd9491b9a5c89cf36cf4e0c Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Mon, 14 Oct 2019 14:43:09 -0300 Subject: [PATCH] Set up the initial displayed values in all boot-sources Problem description =================== In the launch instance workflow while creating a virtual machine, when I select the Source tab, most of the time the default boot-source is `Image` and everything works just fine. However, some times, the default boot-source comes as Volume, due to some race condition (tested and reproduced in Chrome 77). In this case, the Available items table is empty, even if there are bootable volumes to be attached to the VM. This problem happens because the initial state of boot-sources is setting only the `displayedAvailable` of the Image source (that is the "default source"), instead of setting all the initial `displayedAvailable` elements. Proposal ======== The proposal is basically to set up the `displayedAvailable` in all boot-sources. Closes-Bug: #1848334 Change-Id: I1c9399bf3f6c859206c093452c2e637892aa9722 --- .../source/source.controller.js | 6 +- .../source/source.controller.spec.js | 71 +++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.js index 186a2c2136..2f3beedec7 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.js @@ -99,19 +99,19 @@ snapshot: { available: $scope.model.imageSnapshots, allocated: selection, - displayedAvailable: [], + displayedAvailable: $scope.model.imageSnapshots, displayedAllocated: selection }, volume: { available: $scope.model.volumes, allocated: selection, - displayedAvailable: [], + displayedAvailable: $scope.model.volumes, displayedAllocated: selection }, volume_snapshot: { available: $scope.model.volumeSnapshots, allocated: selection, - displayedAvailable: [], + displayedAvailable: $scope.model.volumeSnapshots, displayedAllocated: selection } }; diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.spec.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.spec.js index 6bdfb98af3..f90ad31a99 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.spec.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/source/source.controller.spec.js @@ -105,6 +105,77 @@ }); }); + var executeBootSourcesTest = function (expectedBootResources, allowedBootSources, testType) { + describe('LaunchInstanceSourceControllerUsing' + testType, function () { + beforeEach(inject(function ($controller, $rootScope, _$browser_, $q) { + scope = $rootScope.$new(); + spyOn(scope, '$watch').and.callThrough(); + spyOn(scope, '$watchCollection').and.callThrough(); + $browser = _$browser_; + deferred = $q.defer(); + scope.initPromise = deferred.promise; + + scope.model = { + allowedBootSources: allowedBootSources, + newInstanceSpec: {source: [], source_type: '', create_volume_default: true}, + images: [{id: 'image-1'}, {id: 'image-2'}], + imageSnapshots: [{id: 'imageSnapshot-1'}], + volumes: [{id: 'volume-1'}, {id: 'volume-2'}], + volumeSnapshots: [{id: 'snapshot-2'}], + novaLimits: { + maxTotalInstances: 10, + totalInstancesUsed: 0 + } + }; + + scope.launchInstanceSourceForm = { + 'boot-source-type': {$setValidity: noop} + }; + + ctrl = $controller('LaunchInstanceSourceController', {$scope: scope}); + + scope.$apply(); + })); + + var iterationName = 'initializes table data to reflect "{0}" selection'; + it(iterationName.replace("{0}", testType), function () { + var list = expectedBootResources; // Use scope's values. + var sel = []; // None selected. + + expect(ctrl.tableData).toEqual({ + available: list, + allocated: sel, + displayedAvailable: list, + displayedAllocated: sel + }); + }); + }); + }; + + executeBootSourcesTest( + [{id: 'imageSnapshot-1'}], + [{type: 'snapshot', label: 'ImageSnapshot'}, + {type: 'volume', label: 'Volume'}, + {type: 'image', label: 'Image'}, + {type: 'volume_snapshot', label: 'VolumeSnapshot'}], + "ImageSnapshot"); + + executeBootSourcesTest( + [{id: 'volume-1'}, {id: 'volume-2'}], + [{type: 'volume', label: 'Volume'}, + {type: 'image', label: 'Image'}, + {type: 'volume_snapshot', label: 'VolumeSnapshot'}, + {type: 'snapshot', label: 'ImageSnapshot'}], + "UsingVolume"); + + executeBootSourcesTest( + [{id: 'snapshot-2'}], + [{type: 'volume_snapshot', label: 'volumeSnapshots'}, + {type: 'volume', label: 'Volume'}, + {type: 'image', label: 'Image'}, + {type: 'snapshot', label: 'ImageSnapshot'}], + "VolumeSnapshot"); + it('defaults to first source type if none existing', function() { expect(scope.model.newInstanceSpec.source_type.type).toBe('image'); expect(ctrl.currentBootSource).toBe('image');