summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkihiro Motoki <amotoki@gmail.com>2021-02-26 07:59:59 +0900
committermanchandavishal <manchandavishal143@gmail.com>2021-05-13 11:16:17 +0530
commit9467071b6d37e523659dcf62335ac7f7d54c4e0f (patch)
treec079999d1d0ec17354bf633ea222565b021bcd53
parent96f403311891e35c62c83b85fb394af97c25f102 (diff)
downloadhorizon-9467071b6d37e523659dcf62335ac7f7d54c4e0f.tar.gz
Fix community image handling in launch instance form
Community images which are not owned by a focused project are not included in the normal image list API (i.e., without specifying "community" as visibility), so we need an extra call of the image list API specifying "community" visibility in a filter. It was originally fixed by [1] but was dropped in [2]. The main motivation of [2] is to address "Duplicates in a repeater are not allowed" error in the JavaScript console. The approach of [2] was to drop multiple glance API calls, but the right solution would be to check duplicated images included in both API calls. This situation with duplicated images happens when a community image is owned by a focused project. In such case, the community image is included in the responses of both API calls. This commit adds a logic to check image IDs already processed. [1] https://review.opendev.org/c/openstack/horizon/+/614688 [2] https://review.opendev.org/c/openstack/horizon/+/640762 Closes-Bug: #1914045 Related-Bug: #1779250 Related-Bug: #1818508 Change-Id: I2ed1b6064ddd6f62818d6112e98e5d5a98beae9d (cherry picked from commit c9bb0e95eaa7ae4f0cce21bc0a246a9b0cd2f880)
-rw-r--r--openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js29
-rw-r--r--openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js67
2 files changed, 63 insertions, 33 deletions
diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js
index c8878c411..91d03a323 100644
--- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js
+++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js
@@ -567,7 +567,12 @@
if (enabledImage || enabledSnapshot) {
var filter = {status: 'active', sort_key: 'name', sort_dir: 'asc'};
- glanceAPI.getImages(filter).then(
+ var filterCommunity = angular.merge({}, filter, {visibility: 'community'});
+ var imagePromises = [
+ glanceAPI.getImages(filter),
+ glanceAPI.getImages(filterCommunity)
+ ];
+ $q.all(imagePromises).then(
function(data) {
onGetImageSources(data, enabledImage, enabledSnapshot);
}
@@ -678,13 +683,21 @@
model.imageSnapshots.length = 0;
model.images.length = 0;
- angular.forEach(data.data.items, function(image) {
- if (isValidSnapshot(image) && enabledSnapshot) {
- model.imageSnapshots.push(image);
- } else if (isValidImage(image) && enabledImage) {
- image.name_or_id = image.name || image.id;
- model.images.push(image);
- }
+ var imageIdsProcessed = [];
+
+ angular.forEach(data, function(data) {
+ angular.forEach(data.data.items, function(image) {
+ if (imageIdsProcessed.includes(image.id)) {
+ return;
+ }
+ imageIdsProcessed.push(image.id);
+ if (isValidSnapshot(image) && enabledSnapshot) {
+ model.imageSnapshots.push(image);
+ } else if (isValidImage(image) && enabledImage) {
+ image.name_or_id = image.name || image.id;
+ model.images.push(image);
+ }
+ });
});
if (enabledImage) {
diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js
index 8a18f82ae..dac67d5d5 100644
--- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js
+++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js
@@ -131,19 +131,35 @@
beforeEach(module(function($provide) {
$provide.value('horizon.app.core.openstack-service-api.glance', {
- getImages: function () {
- var images = [
- {container_format: 'aki', properties: {}},
- {container_format: 'ari', properties: {}},
- {container_format: 'ami', properties: {}, name: 'ami_image'},
- {container_format: 'raw', properties: {}, name: 'raw_image'},
- {container_format: 'ami', properties: {image_type: 'image'}, id: '1'},
- {container_format: 'raw', properties: {image_type: 'image'}, id: '2'},
- {container_format: 'ami', properties: {
- block_device_mapping: '[{"source_type": "snapshot"}]'}},
- {container_format: 'raw', properties: {
- block_device_mapping: '[{"source_type": "snapshot"}]'}}
- ];
+ getImages: function (params) {
+ var images;
+ if (params.visibility === 'community') {
+ images = [
+ {id: '10', container_format: 'raw', properties: {image_type: 'image'},
+ visibility: 'community'},
+ // ID 6 is already returned by the first call (below), so this should be ignored.
+ // To clarify the difference, the content here is different intentionally.
+ {id: '6', container_format: 'raw', properties: {image_type: 'image'},
+ visibility: 'community'}
+ ];
+ } else {
+ images = [
+ // container_format aki and ari are not considered as bootable
+ {id: '1', container_format: 'aki', properties: {}},
+ {id: '2', container_format: 'ari', properties: {}},
+ // The following images are considered as "image" sources.
+ {id: '3', container_format: 'ami', properties: {}, name: 'ami_image'},
+ {id: '4', container_format: 'raw', properties: {}, name: 'raw_image'},
+ {id: '5', container_format: 'ami', properties: {image_type: 'image'}},
+ {id: '6', container_format: 'raw', properties: {image_type: 'image'}},
+ // The following images are considered as "snapshot" sources.
+ {id: '7', container_format: 'ami',
+ properties: {block_device_mapping: '[{"source_type": "snapshot"}]'}},
+ {id: '8', container_format: 'raw',
+ properties: {block_device_mapping: '[{"source_type": "snapshot"}]'}},
+ {id: '9', container_format: 'raw', properties: {image_type: 'snapshot'}}
+ ];
+ }
var deferred = $q.defer();
deferred.resolve({data: {items: images}});
@@ -387,22 +403,23 @@
expect(model.newInstanceSpec).toBeDefined();
var expectedImages = [
- {container_format: 'ami', properties: {}, name: 'ami_image', name_or_id: 'ami_image'},
- {container_format: 'raw', properties: {}, name: 'raw_image', name_or_id: 'raw_image'},
- {container_format: 'ami', properties: {image_type: 'image'}, id: '1', name_or_id: '1'},
- {container_format: 'raw', properties: {image_type: 'image'}, id: '2', name_or_id: '2'}
+ {id: '3', container_format: 'ami', properties: {}, name: 'ami_image',
+ name_or_id: 'ami_image'},
+ {id: '4', container_format: 'raw', properties: {}, name: 'raw_image',
+ name_or_id: 'raw_image'},
+ {id: '5', container_format: 'ami', properties: {image_type: 'image'}, name_or_id: '5'},
+ {id: '6', container_format: 'raw', properties: {image_type: 'image'}, name_or_id: '6'},
+ {id: '10', container_format: 'raw', properties: {image_type: 'image'}, name_or_id: '10',
+ visibility: 'community'}
];
expect(model.images).toEqual(expectedImages);
var expectedSnapshots = [
- {
- container_format: 'ami',
- properties: {block_device_mapping: '[{"source_type": "snapshot"}]'}
- },
- {
- container_format: 'raw',
- properties: {block_device_mapping: '[{"source_type": "snapshot"}]'}
- }
+ {id: '7', container_format: 'ami',
+ properties: {block_device_mapping: '[{"source_type": "snapshot"}]'}},
+ {id: '8', container_format: 'raw',
+ properties: {block_device_mapping: '[{"source_type": "snapshot"}]'}},
+ {id: '9', container_format: 'raw', properties: {image_type: 'snapshot'}}
];
expect(model.imageSnapshots).toEqual(expectedSnapshots);