diff --git a/poppy/manager/default/background_job.py b/poppy/manager/default/background_job.py index 6d587e4a..13a9d884 100644 --- a/poppy/manager/default/background_job.py +++ b/poppy/manager/default/background_job.py @@ -18,6 +18,7 @@ import json from oslo_log import log from poppy.manager import base +from poppy.model import ssl_certificate from poppy.notification.mailgun import driver as n_driver from poppy.provider.akamai.background_jobs.check_cert_status_and_update \ import check_cert_status_and_update_flow @@ -40,6 +41,8 @@ class BackgroundJobController(base.BackgroundJobController): a_driver.AKAMAI_GROUP].san_cert_cnames self.notify_email_list = self.driver.conf[ n_driver.MAIL_NOTIFICATION_GROUP].recipients + self.cert_storage = self._driver.storage.certificates_controller + self.service_storage = self._driver.storage.services_controller def post_job(self, job_type, kwargs): queue_data = [] @@ -121,7 +124,58 @@ class BackgroundJobController(base.BackgroundJobController): cert_dict = dict() try: cert_dict = json.loads(cert) + # add validation that the domain still exists on a + # service and that it has a type of SAN + cert_obj = ssl_certificate.SSLCertificate( + cert_dict['flavor_id'], + cert_dict['domain_name'], + 'san', + project_id=cert_dict['project_id'] + ) + cert_for_domain = self.cert_storage.get_certs_by_domain( + cert_obj.domain_name, + project_id=cert_obj.project_id, + flavor_id=cert_obj.flavor_id, + cert_type=cert_obj.cert_type + ) + if cert_for_domain == []: + ignore_list.append(cert_dict) + LOG.info( + "Ignored property update because " + "certificate for {0} does not exist.".format( + cert_obj.domain_name + ) + ) + continue + + service_obj = self.service_storage.\ + get_service_details_by_domain_name( + cert_obj.domain_name, + cert_obj.project_id + ) + found = False + for domain in service_obj.domains: + if ( + domain.domain == cert_obj.domain_name and + domain.protocol == 'https' and + domain.certificate == 'san' + ): + found = True + if found is False: + # skip the task for current cert obj is the + # domain doesn't exist on a service with the + # same protocol and certificate. + ignore_list.append(cert_dict) + LOG.info( + "Ignored update property for a " + "domain '{0}' that no longer exists on a service " + "with the same protocol 'https' and certificate " + "type 'san'".format( + cert_obj.domain_name, + ) + ) + continue domain_name = cert_dict["domain_name"] san_cert = ( cert_dict["cert_details"] @@ -182,9 +236,20 @@ class BackgroundJobController(base.BackgroundJobController): "notify_email_list": self.notify_email_list } - self.distributed_task_controller.submit_task( - update_property_flow.update_property_flow, - **t_kwargs) + # check to see if there are changes to be made before submitting + # the task, avoids creating a new property version when there are + # no changes to be made. + if len(cname_host_info_list) > 0: + self.distributed_task_controller.submit_task( + update_property_flow.update_property_flow, + **t_kwargs) + else: + LOG.info( + "No tasks submitted to update_property_flow" + "update_info_list was empty: {0}".format( + update_info_list + ) + ) return run_list, ignore_list else: diff --git a/tests/unit/manager/default/test_background_job.py b/tests/unit/manager/default/test_background_job.py index fc32cb8d..53b491bc 100644 --- a/tests/unit/manager/default/test_background_job.py +++ b/tests/unit/manager/default/test_background_job.py @@ -21,6 +21,8 @@ from oslo_config import cfg from poppy.manager.default import background_job from poppy.manager.default import driver +from poppy.model.helpers import domain +from poppy.model import service from poppy.notification.mailgun import driver as n_driver from poppy.provider.akamai import driver as aka_driver from tests.unit import base @@ -89,6 +91,7 @@ class DefaultSSLCertificateControllerTests(base.TestCase): def provider_membership(key): return True if key in self.provider_mocks else False + self.mock_storage = mock_storage self.mock_providers = mock.MagicMock() self.mock_providers.__getitem__.side_effect = get_provider_by_name self.mock_providers.__contains__.side_effect = provider_membership @@ -220,6 +223,29 @@ class DefaultSSLCertificateControllerTests(base.TestCase): "akamai_update_papi_property_for_mod_san" ) def test_post_job_positive(self, job_type): + # mock ssl storage returning a cert + self.mock_storage.certificates_controller.\ + get_certs_by_domain.return_value = [ + mock.Mock() + ] + # mock service storage returning a service with domain with + # correct protocol + cert + self.mock_storage.services_controller. \ + get_service_details_by_domain_name.return_value = service.Service( + 'service_id', + 'name', + [ + domain.Domain( + "www.example.com", + protocol='https', + certificate='san' + ) + ], + [], + 'flavor_id', + project_id='project_id' + ) + san_mapping_queue = self.manager_driver.providers[ 'akamai'].obj.san_mapping_queue san_mapping_queue.traverse_queue.return_value = [ @@ -252,8 +278,118 @@ class DefaultSSLCertificateControllerTests(base.TestCase): self.bgc.distributed_task_controller.submit_task.called ) + def test_post_job_ignored_cert_no_longer_exists(self): + self.mock_storage.certificates_controller.\ + get_certs_by_domain.return_value = [] + + san_mapping_queue = self.manager_driver.providers[ + 'akamai'].obj.san_mapping_queue + san_mapping_queue.traverse_queue.return_value = [ + json.dumps({ + "domain_name": "www.example.com", + "flavor_id": "flavor_id", + "project_id": "project_id", + "cert_type": "san", + "cert_details": { + "Akamai": { + "extra_info": { + "san cert": "san.example.com", + "akamai_spsId": 1 + } + } + }, + 'property_activated': True + }) + ] + + run_list, ignore_list = self.bgc.post_job( + "akamai_update_papi_property_for_mod_san", + {'project_id': 'project_id'} + ) + self.assertEqual(0, len(run_list)) + self.assertEqual(1, len(ignore_list)) + + self.assertEqual( + False, + self.bgc.distributed_task_controller.submit_task.called + ) + + def test_post_job_domain_type_modified_on_service(self): + self.mock_storage.certificates_controller.\ + get_certs_by_domain.return_value = [ + mock.Mock() + ] + # simulate domain being changed from https+san to http + self.mock_storage.services_controller. \ + get_service_details_by_domain_name.return_value = service.Service( + 'service_id', + 'name', + [ + domain.Domain( + "www.example.com", + protocol='http', + ) + ], + [], + 'flavor_id', + project_id='project_id' + ) + san_mapping_queue = self.manager_driver.providers[ + 'akamai'].obj.san_mapping_queue + san_mapping_queue.traverse_queue.return_value = [ + json.dumps({ + "domain_name": "www.example.com", + "flavor_id": "flavor_id", + "project_id": "project_id", + "cert_type": "san", + "cert_details": { + "Akamai": { + "extra_info": { + "san cert": "san.example.com", + "akamai_spsId": 1 + } + } + }, + 'property_activated': True + }) + ] + + run_list, ignore_list = self.bgc.post_job( + "akamai_update_papi_property_for_mod_san", + {'project_id': 'project_id'} + ) + self.assertEqual(0, len(run_list)) + self.assertEqual(1, len(ignore_list)) + + self.assertEqual( + False, + self.bgc.distributed_task_controller.submit_task.called + ) + @ddt.data("akamai_update_papi_property_for_mod_san") def test_post_job_invalid_san_cert_cname(self, job_type): + # mock ssl storage returning a cert + self.mock_storage.certificates_controller.\ + get_certs_by_domain.return_value = [ + mock.Mock() + ] + # mock service storage returning a service with domain with + # correct protocol + cert + self.mock_storage.services_controller. \ + get_service_details_by_domain_name.return_value = service.Service( + 'service_id', + 'name', + [ + domain.Domain( + "www.example.com", + protocol='https', + certificate='san' + ) + ], + [], + 'flavor_id', + project_id='project_id' + ) san_mapping_queue = self.manager_driver.providers[ 'akamai'].obj.san_mapping_queue san_mapping_queue.traverse_queue.return_value = [ @@ -281,7 +417,7 @@ class DefaultSSLCertificateControllerTests(base.TestCase): self.assertEqual(1, len(ignore_list)) self.assertEqual( - True, + False, self.bgc.distributed_task_controller.submit_task.called )