Add policy support to workflow steps

This updates the workflow decorator service to check policy rules to
determine whether or not to display a workflow step. Each workflow
step can have a policy property which specifies the policy rule to
check.

Related to blueprint add-scheduler-hints
Change-Id: Id9270a4f20d785283372c182d178fe9b59e3259b
This commit is contained in:
Justin Pomeroy 2016-02-28 20:45:41 -06:00
parent a59195914e
commit 52f988cd78
5 changed files with 128 additions and 10 deletions

View File

@ -29,7 +29,9 @@
}); });
return spec; return spec;
}; };
$provide.value('horizon.app.core.openstack-service-api.serviceCatalog', {}); $provide.value('horizon.app.core.openstack-service-api.serviceCatalog', {
ifTypeEnabled: angular.noop
});
$provide.value('horizon.framework.util.workflow.service', workflow); $provide.value('horizon.framework.util.workflow.service', workflow);
})); }));

View File

@ -1,5 +1,6 @@
/* /*
* (c) Copyright 2015 Hewlett-Packard Development Company, L.P. * (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
* Copyright 2016 IBM Corp.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,14 +27,16 @@
* @kind function * @kind function
* @description * @description
* *
* A workflow decorator function that adds checkReadiness method to step in * A workflow decorator function that looks for the requiredServiceTypes or policy
* the work flow. checkReadiness function will check if a bunch of certain * properties on each step in the workflow. If either of these properties exist then
* types of OpenStack services is enabled in the cloud for that step to show * the checkReadiness method is added to the step. The checkReadiness method will
* on the user interface. * make sure the necessary OpenStack services are enabled and the policy check passes
* in order for the step to be displayed.
* *
* Injected dependencies: * Injected dependencies:
* - $q * - $q
* - serviceCatalog horizon.app.core.openstack-service-api.serviceCatalog * - serviceCatalog horizon.app.core.openstack-service-api.serviceCatalog
* - policy horizon.app.core.openstack-service-api.policy
* *
* @param {Object} spec The input workflow specification object. * @param {Object} spec The input workflow specification object.
* @returns {Object} The decorated workflow specification object, the same * @returns {Object} The decorated workflow specification object, the same
@ -46,12 +49,13 @@
dashboardWorkflowDecorator.$inject = [ dashboardWorkflowDecorator.$inject = [
'$q', '$q',
'horizon.app.core.openstack-service-api.serviceCatalog' 'horizon.app.core.openstack-service-api.serviceCatalog',
'horizon.app.core.openstack-service-api.policy'
]; ];
///////////// /////////////
function dashboardWorkflowDecorator($q, serviceCatalog) { function dashboardWorkflowDecorator($q, serviceCatalog, policy) {
return decorator; return decorator;
function decorator(spec) { function decorator(spec) {
@ -64,12 +68,19 @@
} }
function decorateStep(step) { function decorateStep(step) {
var promises = [];
var types = step.requiredServiceTypes; var types = step.requiredServiceTypes;
if (types && types.length > 0) { if (types && types.length > 0) {
promises = promises.concat(types.map(function checkServiceEnabled(type) {
return serviceCatalog.ifTypeEnabled(type);
}));
}
if (step.policy) {
promises.push(policy.ifAllowed(step.policy));
}
if (promises.length > 0) {
step.checkReadiness = function () { step.checkReadiness = function () {
return $q.all(types.map(function (type) { return $q.all(promises);
return serviceCatalog.ifTypeEnabled(type);
}));
}; };
} }
} }

View File

@ -0,0 +1,71 @@
/*
* 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('Workflow Decorator', function () {
var decoratorService, catalogService, policyService, $scope, deferred;
var steps = [
{ id: '1' },
{ id: '2', requiredServiceTypes: ['foo-service'] },
{ id: '3', policy: 'foo-policy' }
];
var spec = { steps: steps };
beforeEach(module('horizon.app.core'));
beforeEach(module('horizon.framework.util'));
beforeEach(module('horizon.framework.conf'));
beforeEach(module('horizon.framework.widgets.toast'));
beforeEach(inject(function($injector) {
$scope = $injector.get('$rootScope').$new();
deferred = $injector.get('$q').defer();
decoratorService = $injector.get('horizon.app.core.workflow.decorator');
catalogService = $injector.get('horizon.app.core.openstack-service-api.serviceCatalog');
policyService = $injector.get('horizon.app.core.openstack-service-api.policy');
spyOn(catalogService, 'ifTypeEnabled').and.returnValue(deferred.promise);
spyOn(policyService, 'ifAllowed').and.returnValue(deferred.promise);
}));
it('is a function', function() {
expect(angular.isFunction(decoratorService)).toBe(true);
});
it('checks each step for required services and policies', function() {
decoratorService(spec);
expect(steps[0].checkReadiness).toBeUndefined();
expect(steps[1].checkReadiness).toBeDefined();
expect(steps[2].checkReadiness).toBeDefined();
expect(catalogService.ifTypeEnabled.calls.count()).toBe(1);
expect(catalogService.ifTypeEnabled).toHaveBeenCalledWith('foo-service');
expect(policyService.ifAllowed.calls.count()).toBe(1);
expect(policyService.ifAllowed).toHaveBeenCalledWith('foo-policy');
});
it('step checkReadiness function returns correct results', function() {
decoratorService(spec);
var readinessResult;
deferred.resolve('foo');
steps[1].checkReadiness().then(function(result) {
readinessResult = result;
});
$scope.$apply();
expect(readinessResult).toEqual(['foo']);
});
});
})();

View File

@ -28,6 +28,35 @@
* - dashboardWorkflowDecorator {@link horizon.app.core.workflow.factory * - dashboardWorkflowDecorator {@link horizon.app.core.workflow.factory
* :horizon.app.core.workflow.decorator `dashboardWorkflowDecorator`} * :horizon.app.core.workflow.decorator `dashboardWorkflowDecorator`}
* *
* @example
* ```
* var workflow = workflowService({
* title: gettext('Create Volume'),
* btnText: { finish: gettext('Create Volume') },
* steps: [{
* title: gettext('Step 1'),
* templateUrl: basePath + 'steps/create-volume/step1.html',
* helpUrl: basePath + 'steps/create-volume/step1.help.html',
* formName: 'step1Form'
* },{
* title: gettext('Step 2'),
* templateUrl: basePath + 'steps/create-volume/step2.html',
* helpUrl: basePath + 'steps/create-volume/step2.help.html',
* formName: 'step2Form',
* requiredServiceTypes: ['network']
* },{
* title: gettext('Step 3'),
* templateUrl: basePath + 'steps/create-volume/step3.html',
* helpUrl: basePath + 'steps/create-volume/step3.help.html',
* formName: 'step3Form',
* policy: { rules: [['compute', 'os_compute_api:os-scheduler-hints:discoverable']] }
* }]
* });
* ```
* For each step, the requiredServiceTypes property specifies the service types that must
* be available in the service catalog for the step to be displayed. The policy property
* specifies the policy check that must pass in order for the step to be displayed.
*
* @param {Object} The input workflow specification object * @param {Object} The input workflow specification object
* @returns {Object} The decorated workflow specification object, the same * @returns {Object} The decorated workflow specification object, the same
* reference to the input spec object. * reference to the input spec object.

View File

@ -0,0 +1,5 @@
---
features:
- Added policy support to the angular workflow service so each step in a
workflow can specify a policy check that must pass in order for the step
to be displayed.