From ebda9cd07e0740be97ec5e24f8822c3a1c101130 Mon Sep 17 00:00:00 2001 From: Devdatta Kulkarni Date: Thu, 21 Jan 2016 19:36:17 -0600 Subject: [PATCH] Adds support to deploy already created DU This patch adds support to deploy a DU image that is available in glance. Change-Id: I8f3fddad005baaf054d8a41a2b14029dce6633ab Partial-Bug:#1535462 --- .../api/controllers/v1/datamodel/workflow.py | 1 + solum/api/controllers/v1/workflow.py | 32 +++++++++- solum/api/handlers/app_handler.py | 3 +- solum/api/handlers/workflow_handler.py | 11 ++-- solum/tests/api/handlers/test_workflow.py | 5 +- solum/tests/worker/handlers/test_shell.py | 62 +++++++++++++++++-- solum/worker/api.py | 4 +- solum/worker/handlers/shell.py | 22 ++++++- 8 files changed, 125 insertions(+), 15 deletions(-) diff --git a/solum/api/controllers/v1/datamodel/workflow.py b/solum/api/controllers/v1/datamodel/workflow.py index a93ec0f41..d74e4744f 100644 --- a/solum/api/controllers/v1/datamodel/workflow.py +++ b/solum/api/controllers/v1/datamodel/workflow.py @@ -51,6 +51,7 @@ class Workflow(wtypes.Base): source = {wtypes.text: wtypes.text} config = {wtypes.text: wtypes.text} actions = [wtypes.text] + du_id = wtypes.text status = wtypes.text result = wtypes.text scale_target = wtypes.text diff --git a/solum/api/controllers/v1/workflow.py b/solum/api/controllers/v1/workflow.py index f7fb3401b..0ceb8e19b 100644 --- a/solum/api/controllers/v1/workflow.py +++ b/solum/api/controllers/v1/workflow.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_config import cfg import pecan from pecan import rest import wsmeext.pecan as wsme_pecan @@ -20,12 +21,15 @@ from solum.api.controllers.v1.datamodel import workflow from solum.api.controllers.v1 import userlog as userlog_controller from solum.api.handlers import app_handler from solum.api.handlers import workflow_handler as wf_handler +from solum.common import clients from solum.common import exception from solum.common import request from solum.openstack.common import log as logging LOG = logging.getLogger(__name__) +cfg.CONF.import_opt('image_storage', 'solum.worker.config', group='worker') + class WorkflowController(rest.RestController): """Manages operations on a single workflow.""" @@ -87,9 +91,16 @@ class WorkflowsController(rest.RestController): data.source = app_model.source wf_data = data.as_dict(workflow.Workflow) + + du_id = None + if data.du_id: + du_id = data.du_id + self._verify_du_exists(pecan.request.security_context, du_id) + return workflow.Workflow.from_db_model(handler.create(wf_data, commit_sha='', - status_url=''), + status_url='', + du_id=du_id), pecan.request.host_url) @exception.wrap_pecan_controller_exception @@ -102,3 +113,22 @@ class WorkflowsController(rest.RestController): pecan.request.host_url) for obj in handler.get_all(app_id=self.app_id)] return all_wfs + + def _verify_du_exists(self, ctxt, du_id): + du_image_backend = cfg.CONF.worker.image_storage + if du_image_backend.lower() == 'glance': + self._verify_du_image_exists_in_glance(ctxt, du_id) + elif du_image_backend.lower() == 'swift': + self._verify_du_image_exists_in_swift(ctxt, du_id) + else: + raise exception.BadRequest(message="DU image id not recognized.") + return + + def _verify_du_image_exists_in_glance(self, ctxt, du_id): + osc = clients.OpenStackClients(ctxt) + osc.glance().images.get(du_id) + return + + def _verify_du_image_exists_in_swift(self, du_id): + # TODO(devkulkarni): Check if specified du_id exists in swift + return diff --git a/solum/api/handlers/app_handler.py b/solum/api/handlers/app_handler.py index 4f4fb5700..b8bf4747b 100644 --- a/solum/api/handlers/app_handler.py +++ b/solum/api/handlers/app_handler.py @@ -127,7 +127,8 @@ class AppHandler(handler.Handler): 'config': app.workflow_config, 'actions': wf } - wfhand.create(wfdata, commit_sha=commit_sha, status_url=status_url) + wfhand.create(wfdata, commit_sha=commit_sha, status_url=status_url, + du_id=None) def get_all(self): """Return all apps.""" diff --git a/solum/api/handlers/workflow_handler.py b/solum/api/handlers/workflow_handler.py index dd00c6fd4..a43ff0847 100644 --- a/solum/api/handlers/workflow_handler.py +++ b/solum/api/handlers/workflow_handler.py @@ -89,7 +89,7 @@ class WorkflowHandler(handler.Handler): db_obj = objects.registry.Workflow.get_by_uuid(self.context, id) db_obj.destroy(self.context) - def create(self, data, commit_sha, status_url): + def create(self, data, commit_sha, status_url, du_id): """Create a new workflow.""" db_obj = objects.registry.Workflow() db_obj.id = str(uuid.uuid4()) @@ -120,7 +120,8 @@ class WorkflowHandler(handler.Handler): workflow.Workflow.insert(self.context, db_obj) self._execute_workflow_actions(db_obj, app_obj, assem, - commit_sha=app_obj.source['revision']) + commit_sha=app_obj.source['revision'], + du_id=du_id) # TODO(devkulkarni): Update status of actions @@ -137,7 +138,8 @@ class WorkflowHandler(handler.Handler): def _execute_workflow_actions(self, wf_obj, app_obj, assem, verb='launch_workflow', - commit_sha='', status_url=None): + commit_sha='', status_url=None, + du_id=None): image = objects.registry.Image() image.name = app_obj.name @@ -182,7 +184,8 @@ class WorkflowHandler(handler.Handler): assembly_id=assem.id, workflow=assem.workflow, test_cmd=test_cmd, - run_cmd=run_cmd) + run_cmd=run_cmd, + du_id=du_id) class PlanAssemblyAdapter(): diff --git a/solum/tests/api/handlers/test_workflow.py b/solum/tests/api/handlers/test_workflow.py index 6da7293d6..214d2e39d 100644 --- a/solum/tests/api/handlers/test_workflow.py +++ b/solum/tests/api/handlers/test_workflow.py @@ -91,7 +91,8 @@ class TestWorkflowHandler(base.BaseTestCase): handler = workflow_handler.WorkflowHandler(self.ctx) - res = handler.create(workflow_data, commit_sha='', status_url='') + res = handler.create(workflow_data, commit_sha='', status_url='', + du_id='') self.assertEqual(wf_obj, res) git_info = { 'source_url': app_obj.source['repository'], @@ -105,4 +106,4 @@ class TestWorkflowHandler(base.BaseTestCase): git_info=git_info, test_cmd=test_cmd, ports=app_obj.ports, base_image_id=fi.base_image_id, source_format=fi.source_format, - image_format=fi.image_format, run_cmd=run_cmd) \ No newline at end of file + image_format=fi.image_format, run_cmd=run_cmd, du_id='') \ No newline at end of file diff --git a/solum/tests/worker/handlers/test_shell.py b/solum/tests/worker/handlers/test_shell.py index 8ff8d214f..652823900 100644 --- a/solum/tests/worker/handlers/test_shell.py +++ b/solum/tests/worker/handlers/test_shell.py @@ -20,6 +20,7 @@ import uuid import mock from oslo_config import cfg +from solum.common import exception from solum.openstack.common.gettextutils import _ from solum.tests import base from solum.tests import fakes @@ -89,6 +90,58 @@ class HandlerTest(base.BaseTestCase): super(HandlerTest, self).setUp() self.ctx = utils.dummy_context() + @mock.patch('solum.common.clients.OpenStackClients') + def test_get_du_details_glance(self, mock_client): + handler = shell_handler.Handler() + du_id = 'dummy_du_id' + cfg.CONF.set_override('image_storage', 'glance', + group='worker') + fake_du = fakes.FakeImage() + fake_du.id = 2 + fake_du.name = 'name' + + mock_glance = mock_client.return_value.glance + mock_get = mock_glance.return_value.images.get + mock_get.return_value = fake_du + + du_loc, du_name = handler.get_du_details(self.ctx, du_id) + + self.assertTrue(mock_get.called) + self.assertTrue(du_loc, 2) + self.assertTrue(du_name, 'name') + + @mock.patch('solum.common.clients.OpenStackClients') + def test_get_du_details_GLANCE(self, mock_client): + handler = shell_handler.Handler() + du_id = 'dummy_du_id' + cfg.CONF.set_override('image_storage', 'GLANCE', + group='worker') + fake_du = fakes.FakeImage() + fake_du.id = 2 + fake_du.name = 'name' + + mock_glance = mock_client.return_value.glance + mock_get = mock_glance.return_value.images.get + mock_get.return_value = fake_du + + du_loc, du_name = handler.get_du_details(self.ctx, du_id) + + self.assertTrue(mock_get.called) + self.assertTrue(du_loc, 2) + self.assertTrue(du_name, 'name') + + @mock.patch('solum.common.clients.OpenStackClients') + def test_get_du_details_swift(self, mock_client): + handler = shell_handler.Handler() + du_id = 'dummy_du_id' + cfg.CONF.set_override('image_storage', 'swift', + group='worker') + try: + handler.get_du_details(self.ctx, du_id) + self.assertTrue(False) + except exception.NotImplemented: + self.assertTrue(True) + @mock.patch('solum.worker.handlers.shell.Handler._get_environment') @mock.patch('solum.objects.registry') @mock.patch('solum.conductor.api.API.update_assembly') @@ -223,7 +276,7 @@ class HandlerTest(base.BaseTestCase): workflow=['unittest', 'build', 'deploy'], ports=[80], name='new_app', base_image_id=self.base_image_id, source_format='heroku', image_format='docker', assembly_id=44, - test_cmd=None, run_cmd=None) + test_cmd=None, run_cmd=None, du_id=None) proj_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..')) @@ -289,7 +342,7 @@ class HandlerTest(base.BaseTestCase): workflow=['unitetst', 'build', 'deploy'], ports=[80], name='new_app', base_image_id=self.base_image_id, source_format='heroku', image_format='docker', assembly_id=44, - test_cmd=None, run_cmd=None) + test_cmd=None, run_cmd=None, du_id=None) proj_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..')) @@ -497,7 +550,7 @@ class HandlerTest(base.BaseTestCase): workflow=['unittest', 'build', 'deploy'], ports=[80], name='new_app', base_image_id=self.base_image_id, source_format='heroku', image_format='docker', assembly_id=44, - test_cmd='faketests', run_cmd=None) + test_cmd='faketests', run_cmd=None, du_id=None) proj_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..')) @@ -553,7 +606,8 @@ class HandlerTest(base.BaseTestCase): self.ctx, build_id=5, git_info=git_info, name='new_app', base_image_id=self.base_image_id, source_format='chef', image_format='docker', assembly_id=44, ports=[80], - test_cmd='faketests', run_cmd=None, workflow=['unittest', 'build']) + test_cmd='faketests', run_cmd=None, workflow=['unittest', 'build'], + du_id=None) proj_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..')) diff --git a/solum/worker/api.py b/solum/worker/api.py index dc1a38257..55a3f0dd2 100644 --- a/solum/worker/api.py +++ b/solum/worker/api.py @@ -28,12 +28,12 @@ class API(service.API): def build_app(self, verb, build_id, git_info, ports, name, base_image_id, source_format, image_format, assembly_id, workflow, - test_cmd=None, run_cmd=None): + test_cmd=None, run_cmd=None, du_id=None): self._cast(verb, build_id=build_id, git_info=git_info, ports=ports, name=name, base_image_id=base_image_id, source_format=source_format, image_format=image_format, assembly_id=assembly_id, workflow=workflow, - test_cmd=test_cmd, run_cmd=run_cmd) + test_cmd=test_cmd, run_cmd=run_cmd, du_id=du_id) def build_lp(self, image_id, git_info, name, source_format, image_format, artifact_type): diff --git a/solum/worker/handlers/shell.py b/solum/worker/handlers/shell.py index 1951f2418..77f57416e 100644 --- a/solum/worker/handlers/shell.py +++ b/solum/worker/handlers/shell.py @@ -163,6 +163,23 @@ class Handler(object): def echo(self, ctxt, message): LOG.debug("%s" % message) + @exception.wrap_keystone_exception + def get_du_details(self, ctxt, du_id): + du_loc = None + du_name = None + du_image_backend = cfg.CONF.worker.image_storage + + if du_image_backend.lower() == 'glance': + img = clients.OpenStackClients(ctxt).glance().images.get(du_id) + du_loc = img.id + du_name = img.name + elif du_image_backend.lower() == 'swift': + raise exception.NotImplemented() + else: + LOG.error("Invalid image storage option.") + raise exception.ResourceNotFound() + return du_loc, du_name + @exception.wrap_keystone_exception def _get_environment(self, ctxt, source_uri, assembly_id=None, test_cmd=None, run_cmd=None, lp_access=None): @@ -328,7 +345,8 @@ class Handler(object): def launch_workflow(self, ctxt, build_id, git_info, ports, name, base_image_id, source_format, image_format, - assembly_id, workflow, test_cmd, run_cmd): + assembly_id, workflow, test_cmd, run_cmd, du_id): + if 'unittest' in workflow: if self._do_unittest(ctxt, build_id, git_info, name, base_image_id, source_format, image_format, assembly_id, @@ -343,6 +361,8 @@ class Handler(object): image_format, assembly_id, run_cmd) if 'deploy' in workflow: + if du_id: + du_image_loc, du_image_name = self.get_du_details(ctxt, du_id) if du_image_loc and du_image_name: self._do_deploy(ctxt, assembly_id, ports, du_image_loc, du_image_name)