From 2ae49b6c1765633aeedc4e4a64e48c006897ea2b Mon Sep 17 00:00:00 2001 From: Merryl DMello Date: Fri, 22 Jun 2018 12:34:52 +0000 Subject: [PATCH] [CDN-1153] Add change to delete a change when it is in create_in_progress/under pending changes in CPS. poppy/distributed_task/taskflow/task/delete_service_tasks.py removed flavor_id as cert_obj will contain required details to be passed on. poppy/distributed_task/taskflow/task/delete_ssl_certificate_tasks.py removed flavor_id and added cert_obj_json which is passed from delete_service_tasks.py which will contain all required details including cert_details which is needed to delete pending change in progress. If cert_obj_json is present, the notification will be sent for an actual delete of a pending change or a deletion of an existing domain, which earlier would have thrown up an error through mailgun tests/unit/distributed_task/taskflow/test_flows.py added additional cert_details to the certificate object as we need to delete a pending change which would be present in the actual certificate object. --- .../taskflow/task/delete_service_tasks.py | 11 +++- .../task/delete_ssl_certificate_tasks.py | 52 +++++++++------ poppy/provider/akamai/certificates.py | 64 +++++++++++++++---- .../distributed_task/taskflow/test_flows.py | 4 +- .../unit/provider/akamai/test_certificates.py | 46 ++++++------- 5 files changed, 113 insertions(+), 64 deletions(-) diff --git a/poppy/distributed_task/taskflow/task/delete_service_tasks.py b/poppy/distributed_task/taskflow/task/delete_service_tasks.py index ab5a4053..9e3a3829 100644 --- a/poppy/distributed_task/taskflow/task/delete_service_tasks.py +++ b/poppy/distributed_task/taskflow/task/delete_service_tasks.py @@ -230,10 +230,11 @@ def execute(self, project_id, service_id): service_id ) + storage_cert_obj = service_controller.ssl_certificate_storage + kwargs = { 'project_id': project_id, 'context_dict': context_utils.get_current().to_dict(), - 'flavor_id': service_obj.flavor_id, 'providers_list': service_obj.provider_details.keys() } @@ -244,6 +245,14 @@ def execute(self, project_id, service_id): ): kwargs["domain_name"] = domain.domain kwargs["cert_type"] = domain.certificate + cert_obj = storage_cert_obj.get_certs_by_domain( + domain.domain, + cert_type=domain.certificate + ) + if cert_obj: + kwargs["cert_obj_json"] = cert_obj.to_dict() + else: + kwargs["cert_obj_json"] = dict() LOG.info( "Delete service submit task {0} cert delete " "domain {1}.".format( diff --git a/poppy/distributed_task/taskflow/task/delete_ssl_certificate_tasks.py b/poppy/distributed_task/taskflow/task/delete_ssl_certificate_tasks.py index d0cab884..0781a947 100644 --- a/poppy/distributed_task/taskflow/task/delete_ssl_certificate_tasks.py +++ b/poppy/distributed_task/taskflow/task/delete_ssl_certificate_tasks.py @@ -30,23 +30,30 @@ class DeleteProviderSSLCertificateTask(task.Task): default_provides = "responders" def execute(self, providers_list, domain_name, cert_type, - project_id, flavor_id): + project_id, cert_obj_json): service_controller = memoized_controllers.task_controllers('poppy') + flavor_id = cert_obj_json.get('flavor_id') + cert_details = cert_obj_json.get('cert_details') + cert_obj = ssl_certificate.SSLCertificate(flavor_id, domain_name, - cert_type, project_id) + cert_type, project_id, + cert_details) responders = [] # try to delete all certificates from each provider - for provider in providers_list: - LOG.info( - 'Starting to delete ssl certificate: {0} from {1}.'.format( - cert_obj.to_dict(), provider)) - responder = service_controller.provider_wrapper.delete_certificate( - service_controller._driver.providers[provider.lower()], - cert_obj, - ) - responders.append(responder) + if cert_obj_json: + for provider in providers_list: + LOG.info( + 'Starting to delete ssl certificate: {0} from {1}.'.format( + cert_obj.to_dict(), provider)) + responder = ( + service_controller.provider_wrapper.delete_certificate( + service_controller._driver.providers[provider.lower()], + cert_obj + ) + ) + responders.append(responder) return responders @@ -73,11 +80,12 @@ def execute(self, project_id, responders, domain_name, cert_type): ) ) - for n_driver in service_controller._driver.notification: - service_controller.notification_wrapper.send( - n_driver, - "Poppy Certificate Deleted", - notification_content) + if responders: + for n_driver in service_controller._driver.notification: + service_controller.notification_wrapper.send( + n_driver, + "Poppy Certificate Deleted", + notification_content) return @@ -90,11 +98,15 @@ def execute(self, project_id, domain_name, cert_type): self.storage_controller = self.ssl_certificate_manager.storage try: - self.storage_controller.delete_certificate( - project_id, - domain_name, - cert_type + cert = self.storage_controller.get_certs_by_domain( + domain_name, project_id=project_id ) + if cert: + self.storage_controller.delete_certificate( + project_id, + domain_name, + cert_type + ) except ValueError as e: LOG.exception(e) diff --git a/poppy/provider/akamai/certificates.py b/poppy/provider/akamai/certificates.py index 3e45b6ea..b971857c 100644 --- a/poppy/provider/akamai/certificates.py +++ b/poppy/provider/akamai/certificates.py @@ -565,12 +565,62 @@ def delete_certificate(self, cert_obj): ) if found is False: + try: + change_url = ( + cert_obj.cert_details["Akamai"] + ["extra_info"]["change_url"] + ) + except KeyError: + return self.responder.ssl_certificate_deleted( + cert_obj.domain_name, + { + 'status': 'failed', + 'reason': ( + 'Domain does not exist on any certificate ' + ) + } + ) + + headers = { + 'Accept': ( + 'application/vnd.akamai.cps.change-id.v1+' + 'json' + ) + } + + change_id = change_url.split('/')[-1] + enrollment_id = change_url.split('/')[-3] + + base_url = self.cps_api_base_url.format( + enrollmentId=enrollment_id) + change_url = '{0}/changes/{1}'.format( + base_url, change_id) + change_resp = self.cps_api_client.delete( + change_url, headers=headers) + + if change_resp.ok: + LOG.info( + "Deleting domain {0} from certificate" + .format(cert_obj.domain_name) + ) + return self.responder.ssl_certificate_deleted( + cert_obj.domain_name, + { + 'status': 'deleted', + 'deleted_at': str(datetime.datetime.now()), + 'reason': + 'Delete request for {0} succeeded.' + .format(cert_obj.domain_name) + } + ) + return self.responder.ssl_certificate_deleted( cert_obj.domain_name, { 'status': 'failed', 'reason': ( - 'Domain does not exist on any certificate ' + 'Delete pending change for {0} failed.' + .format(cert_obj.domain_name) ) } ) @@ -596,18 +646,6 @@ def delete_certificate(self, cert_obj): enrollment_id, resp.text)) resp_json = json.loads(resp.text) - # check enrollment does not have any pending changes - if len(resp_json['pendingChanges']) > 0: - LOG.info("{0} has pending changes, skipping...".format( - found_cert)) - return self.responder.ssl_certificate_deleted( - cert_obj.domain_name, - { - 'status': 'failed due to pending changes', - 'reason': 'Delete request for {0} failed' - .format(cert_obj.domain_name) - } - ) # remove domain name from sans resp_json['csr']['sans'].remove(cert_obj.domain_name) diff --git a/tests/unit/distributed_task/taskflow/test_flows.py b/tests/unit/distributed_task/taskflow/test_flows.py index ebb53c5e..a88c995c 100644 --- a/tests/unit/distributed_task/taskflow/test_flows.py +++ b/tests/unit/distributed_task/taskflow/test_flows.py @@ -1054,6 +1054,7 @@ def test_recreate_ssl_certificate(self): def test_delete_ssl_certificate_normal(self): providers = ['cdn_provider'] + cert_obj = ssl_certificate.SSLCertificate( 'cdn', 'mytestsite.com', @@ -1063,9 +1064,8 @@ def test_delete_ssl_certificate_normal(self): 'cert_type': "san", 'project_id': json.dumps(str(uuid.uuid4())), 'domain_name': "mytestsite.com", - 'cert_obj': json.dumps(cert_obj.to_dict()), + 'cert_obj_json': cert_obj.to_dict(), 'providers_list': json.dumps(providers), - 'flavor_id': "premium", 'context_dict': context_utils.RequestContext().to_dict() } diff --git a/tests/unit/provider/akamai/test_certificates.py b/tests/unit/provider/akamai/test_certificates.py index d7ca6599..8540834d 100644 --- a/tests/unit/provider/akamai/test_certificates.py +++ b/tests/unit/provider/akamai/test_certificates.py @@ -952,48 +952,38 @@ def test_cert_delete_sni_cert_pending_changes(self): "flavor_id": "flavor_id", "domain_name": "www.abc.com", "cert_type": "sni", - "project_id": "project_id" + "project_id": "project_id", + "cert_details": { + "Akamai": { + "cert_domain": "secured.sni.altcdn.com", + "extra_info": { + "status": "create_in_progress", + "change_url": "/cps/v2/enrollments/12345/changes/2345", + "created_at": "2018-05-02 16:11:55.671460", + "sni_cert": "secured.sni.altcdn.com" + } + } + } }) - self.mock_sans_alternate.return_value = cert_obj.domain_name + self.mock_sans_alternate.return_value = [] controller = certificates.CertificateController(self.driver) - controller.cert_info_storage.get_enrollment_id.return_value = 1234 + controller.cps_api_base_url = 'http://akab-abc.com' - controller.cps_api_client.get.return_value = mock.Mock( - status_code=200, - text=json.dumps({ - "csr": { - "cn": "www.example.com", - "c": "US", - "st": "MA", - "l": "Cambridge", - "o": "Akamai", - "ou": "WebEx", - "sans": [ - "example.com", - "test.example.com" - ] - }, - "pendingChanges": [ - "/cps/v2/enrollments/234/changes/10000" - ] - }) - ) - controller.cps_api_client.put.return_value = mock.Mock( - status_code=500, - text='INTERNAL SERVER ERROR' + controller.cps_api_client.delete.return_value = mock.Mock( + status_code=200 ) responder = controller.delete_certificate(cert_obj) self.assertEqual('www.abc.com', responder['Akamai']['cert_domain']) self.assertEqual( - 'failed due to pending changes', + 'deleted', responder['Akamai']['extra_info']['status'] ) self.assertEqual( - 'Delete request for www.abc.com failed', + 'Delete request for www.abc.com succeeded.', responder['Akamai']['extra_info']['reason'] )