diff --git a/poppy/manager/default/background_job.py b/poppy/manager/default/background_job.py index 18ed5c04..8b92ce80 100644 --- a/poppy/manager/default/background_job.py +++ b/poppy/manager/default/background_job.py @@ -112,7 +112,10 @@ def post_job(self, job_type, kwargs): ) t_kwargs = { "cert_obj_json": json.dumps(cert_dict), - "project_id": cert_dict.get("project_id") + "project_id": cert_dict.get("project_id"), + "property_spec": kwargs.get( + "property_spec", + 'akamai_https_sni_config_numbers') } if cert_dict.get('property_activated', False) is True: self.distributed_task_controller.submit_task( diff --git a/poppy/provider/akamai/background_jobs/check_cert_status_and_update/check_cert_status_and_update_flow.py b/poppy/provider/akamai/background_jobs/check_cert_status_and_update/check_cert_status_and_update_flow.py index 414c8395..49e44f79 100644 --- a/poppy/provider/akamai/background_jobs/check_cert_status_and_update/check_cert_status_and_update_flow.py +++ b/poppy/provider/akamai/background_jobs/check_cert_status_and_update/check_cert_status_and_update_flow.py @@ -31,6 +31,8 @@ def check_cert_status_and_update_flow(): flow = linear_flow.Flow('Update Akamai Property').add( + check_cert_status_and_update_tasks.GetHostnamesFromProperty(), + check_cert_status_and_update_tasks.GetCertInfoTask(), check_cert_status_and_update_tasks.CheckCertStatusTask(), check_cert_status_and_update_tasks.UpdateCertStatusTask() ) diff --git a/poppy/provider/akamai/background_jobs/check_cert_status_and_update/check_cert_status_and_update_tasks.py b/poppy/provider/akamai/background_jobs/check_cert_status_and_update/check_cert_status_and_update_tasks.py index b656bba4..a65e693b 100644 --- a/poppy/provider/akamai/background_jobs/check_cert_status_and_update/check_cert_status_and_update_tasks.py +++ b/poppy/provider/akamai/background_jobs/check_cert_status_and_update/check_cert_status_and_update_tasks.py @@ -28,6 +28,54 @@ conf(project='poppy', prog='poppy', args=[]) +class GetHostnamesFromProperty(task.Task): + default_provides = 'hosts_in_latest_property' + + def __init__(self): + super(GetHostnamesFromProperty, self).__init__() + service_controller, self.providers = \ + memoized_controllers.task_controllers('poppy', 'providers') + self.akamai_driver = self.providers['akamai'].obj + self.existing_hosts = [] + + def execute(self, property_spec): + """Get the host names from the latest property""" + self.property_id = self.akamai_driver.papi_property_id(property_spec) + + LOG.info('Starting to get latest version for property: %s' + % self.property_id) + resp = self.akamai_driver.akamai_papi_api_client.get( + self.akamai_driver.akamai_papi_api_base_url.format( + middle_part='properties/%s' % self.property_id) + ) + if resp.status_code != 200: + raise RuntimeError('PAPI API request failed.' + 'Exception: %s' % resp.text) + else: + property = json.loads(resp.text)['properties']['items'][0] + latestVersion = property['latestVersion'] or 0 + production_version = property['productionVersion'] or -1 + staging_version = property['stagingVersion'] or -1 + + max_version = max(latestVersion, production_version, + staging_version) + resp = self.akamai_driver.akamai_papi_api_client.get( + self.akamai_driver.akamai_papi_api_base_url.format( + middle_part='properties/%s/versions/%s/hostnames' % + (self.property_id, + str(max_version))) + ) + if resp.status_code != 200: + raise RuntimeError('PAPI API request failed.' + 'Exception: %s' % resp.text) + self.existing_hosts = json.loads(resp.text)['hostnames'][ + 'items'] + self.existing_hosts = [str(host['cnameFrom']) + for host in self.existing_hosts] + + return self.existing_hosts + + class GetCertInfoTask(task.Task): default_provides = "cert_obj_json" @@ -55,7 +103,7 @@ def __init__(self): memoized_controllers.task_controllers('poppy', 'providers') self.akamai_driver = self.providers['akamai'].obj - def execute(self, cert_obj_json): + def execute(self, cert_obj_json, hosts_in_latest_property): if cert_obj_json != "": cert_obj = ssl_certificate.load_from_json( json.loads(cert_obj_json)) @@ -156,7 +204,16 @@ def execute(self, cert_obj_json): if change_url not in pending_changes: if cert_obj.domain_name in dns_names: - return "deployed" + if cert_obj.domain_name in hosts_in_latest_property: + return "deployed" + else: + # The hostname is still not on the latest + # property version. Return the item to the + # queue. Another attempt to update property + # should happen. + self.akamai_driver.san_mapping_queue.enqueue_san_mapping( + json.dumps(cert_obj.to_dict())) + return current_status else: return "failed" else: @@ -214,4 +271,4 @@ def execute(self, project_id, cert_obj_json, status_change_to): service_obj.provider_details ) else: - pass + pass \ No newline at end of file diff --git a/tests/unit/provider/akamai/background_jobs/akamai_mocks.py b/tests/unit/provider/akamai/background_jobs/akamai_mocks.py index 5af20c3c..14e4aace 100644 --- a/tests/unit/provider/akamai/background_jobs/akamai_mocks.py +++ b/tests/unit/provider/akamai/background_jobs/akamai_mocks.py @@ -70,6 +70,7 @@ def get_providers(): } akamai_mock_provider_obj.akamai_sps_api_client = MockSPSAPIClient() akamai_mock_provider_obj.akamai_papi_api_client = MockPapiAPIClient() + akamai_mock_provider_obj.akamai_cps_api_client = MockCPSAPIClient() akamai_mock_provider_obj.akamai_sps_api_base_url = ( 'https://mybaseurl.net/config-secure-provisioning-service/' 'v1/sps-requests/{spsId}?' @@ -77,6 +78,10 @@ def get_providers(): akamai_mock_provider_obj.akamai_papi_api_base_url = ( 'https://mybaseurl.net/papi/v1/{middle_part}/' '?contractId=ctr_None&groupId=grp_None') + akamai_mock_provider_obj.akamai_cps_api_base_url = ( + 'https://mybaseurl.net/' + '/cps/v2/enrollments/{enrollmentId}' + ) akamai_mock_provider_obj.san_mapping_queue.\ traverse_queue.return_value = [] akamai_mock_provider.obj = akamai_mock_provider_obj @@ -108,6 +113,26 @@ def get_certs_by_domain(self, domain_name, project_id=None, flavor_id=None, cert_type=None): + if cert_type == 'sni': + return ssl_certificate.SSLCertificate( + "premium", + "blog.testabcd.com", + "sni", + project_id=project_id, + cert_details={ + 'Akamai': { + u'cert_domain': u'secure2.san1.test_123.com', + u'extra_info': { + u'change_url': '/cps/v2/enrollments/10000/changes/10000', + u'action': u'Waiting for customer domain ' + 'validation for blog.testabc.com', + u'create_at': u'2015-09-29 16:09:12.429147', + u'san cert': u'secure2.san1.test_123.com', + u'status': u'create_in_progress'} + } + } + ) + return ssl_certificate.SSLCertificate( "premium", "blog.testabcd.com", @@ -165,6 +190,11 @@ def get(self, url): "edgeHostnameId": "ehn_1126816", "cnameFrom": "secure.san2.test_789.com", "cnameTo": "secure.test_456.com.edge_host_test.net" + }, { + "cnameType": "EDGE_HOSTNAME", + "edgeHostnameId": "ehn_1231234", + "cnameFrom": "blog.testabcd.com", + "cnameTo": "secure.test_456.com.edge_host_test.net" }] } }) @@ -341,3 +371,62 @@ def post(self, url, data=None, headers=None): def put(self, url, data=None, headers=None): return self.response_200 + +class MockCPSAPIClient(mock.Mock): + def __init__(self): + super(MockCPSAPIClient, self).__init__() + self.response_200 = mock.Mock(status_code=200) + + def get(self, url, headers): + self.response_200.text = json.dumps({ + "location": "/cps/v2/enrollments/10002", + "ra": "third-party", + "validationType": "third-party", + "certificateType": "third-party", + "certificateChainType": "default", + "networkConfiguration": { + "geography": "core", + "secureNetwork": "enhanced-tls", + "mustHaveCiphers": "ak-akamai-default", + "preferredCiphers": "ak-akamai-default-interim", + "disallowedTlsVersions": [], + "sniOnly": True, + "quicEnabled": False, + "sni": { + "cloneDnsNames": False, + "dnsNames": [ + "blog.testabcd.com", + "san1.example.com" + ] + }, + "ocspStapling": "not-set" + }, + "signatureAlgorithm": None, + "changeManagement": True, + "csr": { + "cn": "www.example.com", + "c": "US", + "st": "MA", + "l": "Cambridge", + "o": "Akamai", + "ou": "WebEx", + "sans": [ + "san1.example.com", + "san2.example.com", + "san3.example.com" + ] + }, + + "thirdParty": { + "excludeSans": False + }, + "enableMultiStackedCertificates": False, + "pendingChanges": [ + "/cps/v2/enrollments/10002/changes/10002" + ], + "maxAllowedSanNames": 100, + "maxAllowedWildcardSanNames": 100 + }) + + self.response_200.status_code = 200 + return self.response_200 diff --git a/tests/unit/provider/akamai/background_jobs/test_flows.py b/tests/unit/provider/akamai/background_jobs/test_flows.py index 7792baed..f4df97a0 100644 --- a/tests/unit/provider/akamai/background_jobs/test_flows.py +++ b/tests/unit/provider/akamai/background_jobs/test_flows.py @@ -72,24 +72,12 @@ def task_controllers_side_effect(*args, **kwargs): self.addCleanup(bootstrap_patcher.stop) def test_check_cert_status_and_update_flow(self): - cert_obj_json = json.dumps( - ssl_certificate.SSLCertificate( - 'cdn', - 'website.com', - 'san', - cert_details={ - 'Akamai': { - 'extra_info': { - 'san cert': 'secure1.san1.testcdn.com' - } - } - } - ).to_dict() - ) - kwargs = { - 'cert_obj_json': cert_obj_json, - 'project_id': "000" + 'domain_name': 'website.com', + 'cert_type': 'san', + 'flavor_id': 'cdn', + 'project_id': "000", + "property_spec": "akamai_https_san_config_numbers" } engines.run(check_cert_status_and_update_flow. check_cert_status_and_update_flow(), @@ -115,3 +103,15 @@ def test_update_papi_flow(self): } engines.run(update_property_flow.update_property_flow(), store=kwargs) + + def test_domain_exists_in_latest_property_hostnames(self): + kwargs = { + 'domain_name': 'website.com', + 'cert_type': 'sni', + 'flavor_id': 'flavor1', + 'project_id': "000", + "property_spec": "akamai_https_san_config_numbers" + } + engines.run(check_cert_status_and_update_flow. + check_cert_status_and_update_flow(), + store=kwargs)