Languagepack delete fixup to work with app
1) Modified language_pack_handler's delete method to accommodate plans and that are created in the background as part of app deployment invocations. 2) Added a check to ensure that languagepack is deleted only if it not being used by any apps and any plans 3) Changed a exception wrapper on the language_pack_controller's methods as it was obscuring the real error message and causing a spurious exception line to be generated in the solum-api log (Bug: 1498955) Change-Id: Iaa71159f9af319e4bab8cdee8d11558a8ab00650 Closes-Bug: #1497414 Closes-Bug: #1498955
This commit is contained in:
parent
090af4633f
commit
4d469df2d9
|
@ -0,0 +1,36 @@
|
|||
# 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.
|
||||
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
def get_sample_data():
|
||||
data = dict()
|
||||
s = string.lowercase
|
||||
data["name"] = "test_app" + ''.join(random.sample(s, 5))
|
||||
data["description"] = "descp"
|
||||
data["languagepack"] = "python"
|
||||
data["trigger_actions"] = ["test", "build", "deploy"]
|
||||
data["ports"] = [80]
|
||||
|
||||
source = {}
|
||||
source['repository'] = "https://github.com"
|
||||
source['revision'] = "master"
|
||||
data["source"] = source
|
||||
|
||||
workflow = {}
|
||||
workflow["test_cmd"] = "./unit_tests.sh"
|
||||
workflow["run_cmd"] = "python app.py"
|
||||
data["workflow_config"] = workflow
|
||||
|
||||
return data
|
|
@ -20,6 +20,7 @@ import time
|
|||
from tempest_lib import exceptions as tempest_exceptions
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.common import apputils
|
||||
|
||||
sample_plan = {"version": "1",
|
||||
"name": "test_plan",
|
||||
|
@ -108,17 +109,35 @@ class TestLanguagePackController(base.TestCase):
|
|||
self.assertRaises(tempest_exceptions.NotFound,
|
||||
self.client.delete, 'v1/language_packs/not_found')
|
||||
|
||||
def test_language_packs_delete_used_by_app(self):
|
||||
def test_language_packs_delete_used_by_plan(self):
|
||||
uuid, sample_lp = self._create_language_pack()
|
||||
|
||||
artifacts = sample_plan['artifacts']
|
||||
artifacts[0]['language_pack'] = sample_lp['name']
|
||||
sample_plan['artifacts'] = artifacts
|
||||
print("Sample plan")
|
||||
print(sample_plan)
|
||||
resp = self.client.create_plan(data=sample_plan)
|
||||
|
||||
self.assertRaises(tempest_exceptions.Conflict,
|
||||
self.client.delete, 'v1/language_packs/%s' % uuid)
|
||||
self.client.delete_plan(resp.uuid)
|
||||
# Sleep for a few seconds to make sure plans are deleted.
|
||||
time.sleep(5)
|
||||
self._delete_language_pack(uuid)
|
||||
|
||||
def test_language_packs_delete_used_by_app(self):
|
||||
uuid, sample_lp = self._create_language_pack()
|
||||
|
||||
sample_app = apputils.get_sample_data()
|
||||
|
||||
sample_app["languagepack"] = sample_lp["name"]
|
||||
|
||||
resp = self.client.create_app(data=sample_app)
|
||||
|
||||
self.assertRaises(tempest_exceptions.Conflict,
|
||||
self.client.delete, 'v1/language_packs/%s' % uuid)
|
||||
bdy = json.loads(resp.body)
|
||||
|
||||
self.client.delete_app(bdy["id"])
|
||||
# Sleep for a few seconds to make sure plans are deleted.
|
||||
time.sleep(5)
|
||||
self._delete_language_pack(uuid)
|
|
@ -38,7 +38,7 @@ class LanguagePackController(rest.RestController):
|
|||
logs = userlog_controller.UserlogsController(self._id)
|
||||
return logs, remainder
|
||||
|
||||
@exception.wrap_wsme_pecan_controller_exception
|
||||
@exception.wrap_pecan_controller_exception
|
||||
@wsme_pecan.wsexpose(language_pack.LanguagePack)
|
||||
def get(self):
|
||||
"""Return a languagepack."""
|
||||
|
@ -49,7 +49,7 @@ class LanguagePackController(rest.RestController):
|
|||
return language_pack.LanguagePack.from_db_model(handler.get(self._id),
|
||||
host_url)
|
||||
|
||||
@exception.wrap_wsme_pecan_controller_exception
|
||||
@exception.wrap_pecan_controller_exception
|
||||
@wsme_pecan.wsexpose(status_code=204)
|
||||
def delete(self):
|
||||
"""Delete a languagepack."""
|
||||
|
@ -67,7 +67,7 @@ class LanguagePacksController(rest.RestController):
|
|||
remainder = remainder[:-1]
|
||||
return LanguagePackController(lp_id), remainder
|
||||
|
||||
@exception.wrap_wsme_pecan_controller_exception
|
||||
@exception.wrap_pecan_controller_exception
|
||||
@wsme_pecan.wsexpose(language_pack.LanguagePack,
|
||||
body=language_pack.LanguagePack,
|
||||
status_code=201)
|
||||
|
@ -80,7 +80,7 @@ class LanguagePacksController(rest.RestController):
|
|||
handler.create(data.as_dict(objects.registry.Image),
|
||||
data.lp_metadata), host_url)
|
||||
|
||||
@exception.wrap_wsme_pecan_controller_exception
|
||||
@exception.wrap_pecan_controller_exception
|
||||
@wsme_pecan.wsexpose([language_pack.LanguagePack])
|
||||
def get_all(self):
|
||||
"""Return all languagepacks, based on the query provided."""
|
||||
|
|
|
@ -23,9 +23,11 @@ from solum.common import exception as exc
|
|||
from solum.common import solum_swiftclient
|
||||
from solum import objects
|
||||
from solum.objects import image
|
||||
from solum.objects.sqlalchemy import app
|
||||
from solum.openstack.common import log as logging
|
||||
from solum.worker import api
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# Register options for the service
|
||||
|
@ -42,6 +44,30 @@ CONF.register_opts(API_SERVICE_OPTS, group='api')
|
|||
class LanguagePackHandler(handler.Handler):
|
||||
"""Fulfills a request on the languagepack resource."""
|
||||
|
||||
def _check_lp_referenced_in_any_plan(self, ctxt, db_obj):
|
||||
plans = objects.registry.PlanList.get_all(ctxt)
|
||||
for plan in plans:
|
||||
# (devkulkarni): Adding check for raw_contents because
|
||||
# there are 'plans' in our db that don't have the
|
||||
# raw_contents field. These plans correspond to the
|
||||
# 'dummy' plans that we are creating corresponding
|
||||
# to 'app deploy' requests.
|
||||
if hasattr(plan, 'raw_content') and plan.raw_content:
|
||||
lp = plan.raw_content['artifacts'][0]['language_pack']
|
||||
if lp == db_obj.name or lp == db_obj.uuid:
|
||||
return True
|
||||
|
||||
def _check_lp_referenced_in_any_app(self, ctxt, db_obj):
|
||||
apps = app.App.get_all_by_lp(ctxt, db_obj.name)
|
||||
if len(apps) > 0:
|
||||
return True
|
||||
else:
|
||||
apps = app.App.get_all_by_lp(ctxt, db_obj.uuid)
|
||||
if len(apps) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get(self, id):
|
||||
"""Return a languagepack."""
|
||||
return objects.registry.Image.get_lp_by_name_or_uuid(
|
||||
|
@ -79,13 +105,10 @@ class LanguagePackHandler(handler.Handler):
|
|||
"""Delete a languagepack."""
|
||||
db_obj = objects.registry.Image.get_lp_by_name_or_uuid(self.context,
|
||||
uuid)
|
||||
|
||||
# Check if the languagepack is being used.
|
||||
plans = objects.registry.PlanList.get_all(self.context)
|
||||
for plan in plans:
|
||||
lp = plan.raw_content['artifacts'][0]['language_pack']
|
||||
if lp == db_obj.name or lp == db_obj.uuid:
|
||||
raise exc.LPStillReferenced(name=uuid)
|
||||
if (self._check_lp_referenced_in_any_plan(self.context, db_obj) or
|
||||
self._check_lp_referenced_in_any_app(self.context, db_obj)):
|
||||
raise exc.LPStillReferenced(name=uuid)
|
||||
|
||||
# Delete image file from swift
|
||||
if db_obj.docker_image_name:
|
||||
|
|
|
@ -54,6 +54,23 @@ class App(sql.Base, abstract.App):
|
|||
app = cls.get_by_id(context, item_uuid)
|
||||
return app
|
||||
|
||||
@classmethod
|
||||
def get_by_trigger_id(cls, context, trigger_id):
|
||||
try:
|
||||
session = sql.Base.get_session()
|
||||
return session.query(cls).filter_by(trigger_uuid=trigger_id).one()
|
||||
except sa.orm.exc.NoResultFound:
|
||||
cls._raise_trigger_not_found(trigger_id)
|
||||
|
||||
@classmethod
|
||||
def get_all_by_lp(cls, context, lp):
|
||||
session = sql.SolumBase.get_session()
|
||||
apps = []
|
||||
with session.begin():
|
||||
query = session.query(cls).filter_by(languagepack=lp)
|
||||
apps = sql.filter_by_project(context, query).all()
|
||||
return apps
|
||||
|
||||
@classmethod
|
||||
@sql.retry
|
||||
def update_and_save(cls, context, id_or_uuid, data):
|
||||
|
|
|
@ -60,13 +60,17 @@ class TestLanguagePackHandler(base.BaseTestCase):
|
|||
@mock.patch('solum.common.solum_swiftclient.SwiftClient.delete_object')
|
||||
@mock.patch('solum.api.handlers.userlog_handler.UserlogHandler')
|
||||
@mock.patch('solum.objects.registry.PlanList')
|
||||
def test_languagepack_delete(self, mock_planlist, mock_log_handler,
|
||||
@mock.patch('solum.objects.sqlalchemy.app.App')
|
||||
def test_languagepack_delete(self, mock_app, mock_planlist,
|
||||
mock_log_handler,
|
||||
mock_swift_delete, mock_img):
|
||||
fi = fakes.FakeImage()
|
||||
mock_img.get_lp_by_name_or_uuid.return_value = fi
|
||||
mock_img.destroy.return_value = {}
|
||||
mock_planlist.get_all.return_value = {}
|
||||
|
||||
mock_app.get_all_by_lp.return_value = {}
|
||||
|
||||
handler = language_pack_handler.LanguagePackHandler(self.ctx)
|
||||
handler.delete('test_lp')
|
||||
|
||||
|
@ -97,7 +101,9 @@ class TestLanguagePackHandler(base.BaseTestCase):
|
|||
@mock.patch('solum.common.solum_swiftclient.SwiftClient.delete_object')
|
||||
@mock.patch('solum.api.handlers.userlog_handler.UserlogHandler')
|
||||
@mock.patch('solum.objects.registry.PlanList')
|
||||
@mock.patch('solum.objects.sqlalchemy.app.App')
|
||||
def test_languagepack_delete_with_plan_not_using_lp(self,
|
||||
mock_app,
|
||||
mock_planlist,
|
||||
mock_log_handler,
|
||||
mock_swift_delete,
|
||||
|
@ -107,6 +113,7 @@ class TestLanguagePackHandler(base.BaseTestCase):
|
|||
mock_img.destroy.return_value = {}
|
||||
mock_planlist.get_all.return_value = [fakes.FakePlan()]
|
||||
|
||||
mock_app.get_all_by_lp.return_value = {}
|
||||
handler = language_pack_handler.LanguagePackHandler(self.ctx)
|
||||
handler.delete('lp_name')
|
||||
|
||||
|
|
Loading…
Reference in New Issue