Add Plan versioning management

* Version can now be passed in attribute of the Plan YAML File.
Version attribute is a mandatory field.
* According to the version specified, the appropriate WSME Plan
and Plan Handler are created and called.
* Plan validation is done by the WSME framework.
* How to add a new Plan version (e.g with version 2) ?
 - Add your Plan_v2 WSME object in datamodels
 - Add your Plan_v2_handler in handlers
 - Add init_plan_v2 method in Plan controller.

Change-Id: I7fcc6e11391b9f026768a4afafaaa638c43ec303
Closes-Bug: #1291114
This commit is contained in:
Pierre Padrixe
2014-06-05 18:07:50 +02:00
parent 4830e1c237
commit 2359f9b644
8 changed files with 61 additions and 15 deletions

View File

@@ -1,3 +1,4 @@
version: 1
name: ex1
description: Nodejs express.
artifacts:

View File

@@ -1,3 +1,4 @@
version: 1
name: ghost
description: ghost blogging platform
artifacts:

View File

@@ -19,7 +19,8 @@ from functionaltests.api import base
assembly_data = {'name': 'test_assembly',
'description': 'desc assembly'}
plan_data = {'name': 'test_plan',
plan_data = {'version': '1',
'name': 'test_plan',
'description': 'A test to create plan'}

View File

@@ -27,7 +27,8 @@ sample_data = {"name": "test_assembly",
"status": "status",
"application_uri": "http://localhost:5000"}
plan_sample_data = {"name": "test_plan",
plan_sample_data = {"version": "1",
"name": "test_plan",
"description": "A test to create plan",
"project_id": "project_id",
"user_id": "user_id"}

View File

@@ -26,7 +26,8 @@ sample_data = {'name': 'test_service',
assembly_sample_data = {'name': 'test_assembly',
'description': 'desc assembly'}
plan_sample_data = {'name': 'test_plan',
plan_sample_data = {'version': '1',
'name': 'test_plan',
'description': 'A test to create plan'}

View File

@@ -17,7 +17,8 @@ import yaml
from functionaltests.api import base
sample_data = {"name": "test_plan",
sample_data = {"version": "1",
"name": "test_plan",
"description": "A test to create plan",
"artifacts": [{
"name": "No_deus",
@@ -110,7 +111,8 @@ class TestPlanController(base.TestCase):
def test_plans_put(self):
uuid = self._create_plan()
updated_data = {"name": "test_plan_updated",
updated_data = {"version": "1",
"name": "test_plan_updated",
"description": "A test to create plan updated",
"type": "plan",
"artifacts": []}

View File

@@ -14,6 +14,7 @@
import pecan
from pecan import rest
import sys
import wsmeext.pecan as wsme_pecan
import yaml
@@ -24,6 +25,25 @@ from solum.common import yamlutils
from solum import objects
def init_plan_v1(yml_input_plan):
plan_handler_v1 = plan_handler.PlanHandler(
pecan.request.security_context)
plan_v1 = plan.Plan(**yml_input_plan)
return plan_handler_v1, plan_v1
def init_plan_by_version(yml_input_plan):
version = yml_input_plan.get('version')
if version is None:
raise exception.BadRequest(
reason='Version attribute is missing in Plan')
mod = sys.modules[__name__]
if not hasattr(mod, 'init_plan_v%s' % version):
raise exception.BadRequest(reason='Plan version %s is invalid.'
% version)
return getattr(mod, 'init_plan_v%s' % version)(yml_input_plan)
class PlanController(rest.RestController):
"""Manages operations on a single plan."""
@@ -44,14 +64,13 @@ class PlanController(rest.RestController):
@pecan.expose(content_type='application/x-yaml')
def put(self):
"""Modify this plan."""
handler = plan_handler.PlanHandler(pecan.request.security_context)
if not pecan.request.body or len(pecan.request.body) < 1:
raise exception.BadRequest
try:
yml_input_plan = yamlutils.load(pecan.request.body)
except ValueError as excp:
raise exception.BadRequest('Plan is invalid. ' + excp.message)
data = plan.Plan(**yml_input_plan)
handler, data = init_plan_by_version(yml_input_plan)
updated_plan_yml = yaml.dump(handler.update(
self._id, data.as_dict(objects.registry.Plan)).refined_content())
pecan.response.status = 200
@@ -78,14 +97,13 @@ class PlansController(rest.RestController):
@pecan.expose(content_type='application/x-yaml')
def post(self):
"""Create a new plan."""
handler = plan_handler.PlanHandler(pecan.request.security_context)
if not pecan.request.body or len(pecan.request.body) < 1:
raise exception.BadRequest
try:
yml_input_plan = yamlutils.load(pecan.request.body)
except ValueError as excp:
raise exception.BadRequest('Plan is invalid. ' + excp.message)
data = plan.Plan(**yml_input_plan)
handler, data = init_plan_by_version(yml_input_plan)
create_plan_yml = yaml.dump(handler.create(
data.as_dict(objects.registry.Plan)).refined_content())
pecan.response.status = 201

View File

@@ -60,26 +60,38 @@ class TestPlanController(base.BaseTestCase):
self.assertEqual(400, resp_mock.status)
def test_plan_put_not_found(self, PlanHandler, resp_mock, request_mock):
data = 'name: ex_plan1\ndescription: yaml plan1.'
data = 'version: 1\nname: ex_plan1\ndescription: dsc1.'
request_mock.body = data
request_mock.content_type = 'application/x-yaml'
hand_update = PlanHandler.return_value.update
hand_update.side_effect = exception.ResourceNotFound(
name='plan', plan_id='test_id')
plan.PlanController('test_id').put()
hand_update.assert_called_with('test_id', yaml.load(data))
hand_update.assert_called_with('test_id', {'name': 'ex_plan1',
'description': u'dsc1.'})
self.assertEqual(404, resp_mock.status)
def test_plan_put_ok(self, PlanHandler, resp_mock, request_mock):
data = 'name: ex_plan1\ndescription: yaml plan1.'
data = 'version: 1\nname: ex_plan1\ndescription: dsc1.'
request_mock.body = data
request_mock.content_type = 'application/x-yaml'
hand_update = PlanHandler.return_value.update
hand_update.return_value = fakes.FakePlan()
plan.PlanController('test_id').put()
hand_update.assert_called_with('test_id', yaml.load(data))
hand_update.assert_called_with('test_id', {'name': 'ex_plan1',
'description': u'dsc1.'})
self.assertEqual(200, resp_mock.status)
def test_plan_put_version_not_found(self, PlanHandler,
resp_mock, request_mock):
data = 'name: ex_plan1\ndescription: yaml plan1.\nversion: 2'
request_mock.body = data
request_mock.content_type = 'application/x-yaml'
hand_update = PlanHandler.return_value.update
hand_update.return_value = fakes.FakePlan()
plan.PlanController('test_id').put()
self.assertEqual(400, resp_mock.status)
def test_plan_delete_not_found(self, PlanHandler, resp_mock, request_mock):
hand_delete = PlanHandler.return_value.delete
hand_delete.side_effect = exception.ResourceNotFound(
@@ -118,15 +130,24 @@ class TestPlansController(base.BaseTestCase):
hand_get.assert_called_with()
def test_plans_post(self, PlanHandler, resp_mock, request_mock):
request_mock.body = 'name: ex_plan1\ndescription: yaml plan1.'
request_mock.body = 'version: 1\nname: ex_plan1\ndescription: dsc1.'
request_mock.content_type = 'application/x-yaml'
hand_create = PlanHandler.return_value.create
hand_create.return_value = fakes.FakePlan()
plan.PlansController().post()
hand_create.assert_called_with({'name': 'ex_plan1',
'description': 'yaml plan1.'})
'description': 'dsc1.'})
self.assertEqual(201, resp_mock.status)
def test_plans_post_version_not_found(self, PlanHandler,
resp_mock, request_mock):
request_mock.body = 'version: 2\nname: ex_plan1\ndescription: dsc1.'
request_mock.content_type = 'application/x-yaml'
hand_create = PlanHandler.return_value.create
hand_create.return_value = fakes.FakePlan()
plan.PlansController().post()
self.assertEqual(400, resp_mock.status)
def test_plans_post_nodata(self, handler_mock, resp_mock, request_mock):
request_mock.body = ''
request_mock.content_type = 'application/json'