Support setting more visibility options for edit/create image

The edit and create image pages could only set public or private
visibility for an image. With Glance v2, we have 'shared' and
'community' available to us. This commit adds buttons for those
pages selectively when the policy allows it.
It also only displays 'public' visibility if policy allows.

Closes-Bug: #1792411

Depends-On: https://review.openstack.org/#/c/614688/

Change-Id: I193bc0580e4bf9351ad7f17b148a5062e95313ab
This commit is contained in:
Andy Botting 2018-08-29 12:12:07 +10:00
parent d58539fa49
commit 04fe1156bf
8 changed files with 69 additions and 33 deletions

View File

@ -54,7 +54,6 @@
ctrl.diskFormats = []; ctrl.diskFormats = [];
ctrl.prepareUpload = prepareUpload; ctrl.prepareUpload = prepareUpload;
ctrl.apiVersion = 0; ctrl.apiVersion = 0;
ctrl.allowPublicizeImage = true;
$scope.stepModels.imageForm = ctrl.image = { $scope.stepModels.imageForm = ctrl.image = {
source_type: '', source_type: '',
@ -66,7 +65,7 @@
min_ram: 0, min_ram: 0,
container_format: '', container_format: '',
disk_format: '', disk_format: '',
visibility: 'public' visibility: 'shared'
}; };
ctrl.uploadProgress = -1; ctrl.uploadProgress = -1;
@ -84,8 +83,8 @@
ctrl.imageSourceOptions = []; ctrl.imageSourceOptions = [];
ctrl.imageVisibilityOptions = [ ctrl.imageVisibilityOptions = [
{ label: gettext('Public'), value: 'public'}, { label: gettext('Private'), value: 'private' },
{ label: gettext('Private'), value: 'private' } { label: gettext('Shared'), value: 'shared'}
]; ];
ctrl.kernelImages = []; ctrl.kernelImages = [];
@ -148,11 +147,14 @@
function init() { function init() {
glance.getImages({paginate: false}).success(onGetImages); glance.getImages({paginate: false}).success(onGetImages);
policyAPI.ifAllowed({rules: [['image', 'publicize_image']]}).then( policyAPI.ifAllowed({rules: [['image', 'communitize_image']]}).then(
angular.noop,
function () { function () {
ctrl.image.visibility = "private"; ctrl.imageVisibilityOptions.push({ label: gettext('Community'), value: 'community' });
ctrl.allowPublicizeImage = false; }
);
policyAPI.ifAllowed({rules: [['image', 'publicize_image']]}).then(
function () {
ctrl.imageVisibilityOptions.push({ label: gettext('Public'), value: 'public' });
} }
); );
} }

View File

@ -97,7 +97,7 @@
it('should have options for visibility, protected and copying', function() { it('should have options for visibility, protected and copying', function() {
var ctrl = createController(); var ctrl = createController();
expect(ctrl.imageVisibilityOptions.length).toEqual(2); expect(ctrl.imageVisibilityOptions.length).toEqual(4);
expect(ctrl.imageProtectedOptions.length).toEqual(2); expect(ctrl.imageProtectedOptions.length).toEqual(2);
expect(ctrl.imageCopyOptions.length).toEqual(2); expect(ctrl.imageCopyOptions.length).toEqual(2);
}); });
@ -124,7 +124,7 @@
expect(ctrl.imageFormats).toBeDefined(); expect(ctrl.imageFormats).toBeDefined();
expect(ctrl.validationRules).toBeDefined(); expect(ctrl.validationRules).toBeDefined();
expect(ctrl.diskFormats).toEqual([]); expect(ctrl.diskFormats).toEqual([]);
expect(ctrl.image.visibility).toEqual('public'); expect(ctrl.image.visibility).toEqual('shared');
expect(ctrl.image.min_disk).toEqual(0); expect(ctrl.image.min_disk).toEqual(0);
expect(ctrl.image.min_ram).toEqual(0); expect(ctrl.image.min_ram).toEqual(0);
}); });
@ -214,6 +214,16 @@
$timeout.flush(); $timeout.flush();
expect(ctrl.imageSourceOptions).toEqual([fileSourceOption, urlSourceOption]); expect(ctrl.imageSourceOptions).toEqual([fileSourceOption, urlSourceOption]);
}); });
it('test image visibility is private if set as default', function() {
var ctrl = createController();
settingsCall.resolve({
OPENSTACK_IMAGE_FORMATS: [],
CREATE_IMAGE_DEFAULTS: { image_visibility: 'private' }
});
$timeout.flush();
expect(ctrl.image.visibility).toEqual('private');
});
}); });
}); });

View File

@ -221,7 +221,7 @@
<div class="selected-source"> <div class="selected-source">
<div class="row"> <div class="row">
<div class="col-xs-6 col-sm-6" ng-show='ctrl.allowPublicizeImage'> <div class="col-xs-6 col-sm-6">
<div class="form-group"> <div class="form-group">
<label class="control-label required"> <label class="control-label required">
<translate>Visibility</translate> <translate>Visibility</translate>

View File

@ -25,7 +25,8 @@
'$scope', '$scope',
'horizon.app.core.images.imageFormats', 'horizon.app.core.images.imageFormats',
'horizon.app.core.images.validationRules', 'horizon.app.core.images.validationRules',
'horizon.app.core.openstack-service-api.settings' 'horizon.app.core.openstack-service-api.settings',
'horizon.app.core.openstack-service-api.policy'
]; ];
/** /**
@ -38,7 +39,8 @@
$scope, $scope,
imageFormats, imageFormats,
validationRules, validationRules,
settings settings,
policy
) { ) {
var ctrl = this; var ctrl = this;
@ -52,12 +54,11 @@
]; ];
ctrl.imageVisibilityOptions = [ ctrl.imageVisibilityOptions = [
{ label: gettext('Public'), value: 'public' }, { label: gettext('Private'), value: 'private' },
{ label: gettext('Private'), value: 'private' } { label: gettext('Shared'), value: 'shared' }
]; ];
ctrl.setFormats = setFormats; ctrl.setFormats = setFormats;
ctrl.allowPublicizeImage = { rules: [['image', 'image:publicize_image']] };
$scope.imagePromise.then(init); $scope.imagePromise.then(init);
@ -80,13 +81,26 @@
ctrl.image.kernel = ctrl.image.properties.kernel_id; ctrl.image.kernel = ctrl.image.properties.kernel_id;
ctrl.image.ramdisk = ctrl.image.properties.ramdisk_id; ctrl.image.ramdisk = ctrl.image.properties.ramdisk_id;
ctrl.image.architecture = ctrl.image.properties.architecture; ctrl.image.architecture = ctrl.image.properties.architecture;
ctrl.image.visibility = ctrl.image.is_public ? 'public' : 'private';
ctrl.image_format = ctrl.image.disk_format; ctrl.image_format = ctrl.image.disk_format;
if (ctrl.image.container_format === 'docker') { if (ctrl.image.container_format === 'docker') {
ctrl.image_format = 'docker'; ctrl.image_format = 'docker';
ctrl.image.disk_format = 'raw'; ctrl.image.disk_format = 'raw';
} }
setFormats(); setFormats();
getVisibilities();
}
function getVisibilities() {
policy.ifAllowed({rules: [['image', 'communitize_image']]}).then(
function() {
ctrl.imageVisibilityOptions.push({ label: gettext('Community'), value: 'community' });
}
);
policy.ifAllowed({rules: [['image', 'publicize_image']]}).then(
function() {
ctrl.imageVisibilityOptions.push({ label: gettext('Public'), value: 'public' });
}
);
} }
function setFormats() { function setFormats() {

View File

@ -19,7 +19,15 @@
describe('horizon.app.core.images edit image controller', function() { describe('horizon.app.core.images edit image controller', function() {
var controller, $scope, $q, settingsCall, $timeout; function policyIfAllowed() {
return {
then: function(callback) {
callback({allowed: true});
}
};
}
var controller, $scope, $q, settingsCall, $timeout, policy;
/////////////////////// ///////////////////////
@ -35,6 +43,9 @@
$timeout = _$timeout_; $timeout = _$timeout_;
controller = $injector.get('$controller'); controller = $injector.get('$controller');
policy = $injector.get('horizon.app.core.openstack-service-api.policy');
spyOn(policy, 'ifAllowed').and.callFake(policyIfAllowed);
})); }));
function createController() { function createController() {
@ -57,19 +68,12 @@
} }
it('should have options for visibility and protected', function() { it('should have options for visibility and protected', function() {
setImagePromise({id: '1', container_format: 'bare', is_public: false, properties: []}); setImagePromise({id: '1', container_format: 'bare', visibility: 'shared', properties: []});
var ctrl = createController();
expect(ctrl.imageVisibilityOptions.length).toEqual(2);
expect(ctrl.imageProtectedOptions.length).toEqual(2);
});
it('should map is_public', function() {
setImagePromise({id: '1', container_format: 'bare', is_public: false, properties: []});
var ctrl = createController(); var ctrl = createController();
$timeout.flush(); $timeout.flush();
expect(ctrl.image.visibility).toEqual('private'); expect(ctrl.imageVisibilityOptions.length).toEqual(4);
expect(ctrl.imageProtectedOptions.length).toEqual(2);
}); });
it('reads the data format settings', function() { it('reads the data format settings', function() {
@ -87,14 +91,14 @@
id: '1', id: '1',
container_format: 'bare', container_format: 'bare',
disk_format: 'ova', disk_format: 'ova',
is_public: true, visibility: 'private',
properties: [] properties: []
}); });
var ctrl = createController(); var ctrl = createController();
$timeout.flush(); $timeout.flush();
expect(ctrl.image.disk_format).toEqual('ova'); expect(ctrl.image.disk_format).toEqual('ova');
expect(ctrl.image.visibility).toEqual('public'); expect(ctrl.image.visibility).toEqual('private');
}); });
it('should set local image_format to docker when container is docker', function() { it('should set local image_format to docker when container is docker', function() {

View File

@ -123,7 +123,7 @@
<div class="selected-source clearfix"> <div class="selected-source clearfix">
<div class="row"> <div class="row">
<div class="col-xs-6 col-sm-6" hz-if-policies="ctrl.allowPublicizeImage"> <div class="col-xs-6 col-sm-6">
<div class="form-group"> <div class="form-group">
<label class="control-label required" translate>Visibility</label> <label class="control-label required" translate>Visibility</label>
<div class="form-field"> <div class="form-field">

View File

@ -121,7 +121,7 @@
* The minimum memory size required to boot the image. Optional. * The minimum memory size required to boot the image. Optional.
* *
* @param {boolean} image.visibility * @param {boolean} image.visibility
* values of 'public', 'private', and 'shared' are valid. Required. * values of 'public', 'private', 'shared' and 'community' are valid. Required.
* *
* @param {boolean} image.protected * @param {boolean} image.protected
* True if the image is protected, false otherwise. Required. * True if the image is protected, false otherwise. Required.
@ -217,7 +217,7 @@
* The minimum memory size required to boot the image. Optional. * The minimum memory size required to boot the image. Optional.
* *
* @param {boolean} image.visibility * @param {boolean} image.visibility
* Values of 'public', 'private', and 'shared' are valid. Required. * Values of 'public', 'private', 'shared' and 'community' are valid. Required.
* *
* @param {boolean} image.protected * @param {boolean} image.protected
* True if the image is protected, false otherwise. Required. * True if the image is protected, false otherwise. Required.

View File

@ -0,0 +1,6 @@
---
other:
- |
The default visibility when creating new images has been changed from
`private` to `shared` to bring it inline with the default changing in
Glance since Image API v2.5.