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;
};
$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);
}));

View File

@ -1,5 +1,6 @@
/*
* (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
* 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.
@ -26,14 +27,16 @@
* @kind function
* @description
*
* A workflow decorator function that adds checkReadiness method to step in
* the work flow. checkReadiness function will check if a bunch of certain
* types of OpenStack services is enabled in the cloud for that step to show
* on the user interface.
* A workflow decorator function that looks for the requiredServiceTypes or policy
* properties on each step in the workflow. If either of these properties exist then
* the checkReadiness method is added to the step. The checkReadiness method will
* make sure the necessary OpenStack services are enabled and the policy check passes
* in order for the step to be displayed.
*
* Injected dependencies:
* - $q
* - serviceCatalog horizon.app.core.openstack-service-api.serviceCatalog
* - policy horizon.app.core.openstack-service-api.policy
*
* @param {Object} spec The input workflow specification object.
* @returns {Object} The decorated workflow specification object, the same
@ -46,12 +49,13 @@
dashboardWorkflowDecorator.$inject = [
'$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;
function decorator(spec) {
@ -64,12 +68,19 @@
}
function decorateStep(step) {
var promises = [];
var types = step.requiredServiceTypes;
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 () {
return $q.all(types.map(function (type) {
return serviceCatalog.ifTypeEnabled(type);
}));
return $q.all(promises);
};
}
}

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
* :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
* @returns {Object} The decorated workflow specification object, the same
* 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.