Browse Source

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 c9bb0e95ea)
changes/28/779128/4
Akihiro Motoki 4 months ago
committed by manchandavishal
parent
commit
c63e4c91ae
2 changed files with 63 additions and 33 deletions
  1. +21
    -8
      openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js
  2. +42
    -25
      openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js

+ 21
- 8
openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.js View File

@ -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) {


+ 42
- 25
openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js View File

@ -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);


Loading…
Cancel
Save