From c1cb52e8c85ba490c86f4345c5d9abeab367a5d4 Mon Sep 17 00:00:00 2001 From: Isaac Mungai Date: Tue, 26 Apr 2016 15:40:54 -0400 Subject: [PATCH] Refactor storage layer to separate cert controller Move all certificate logic into it's own controller in the storage layer. Change-Id: Icc7e457ea2ab8bc9a149e6dedda3e23a202dcf7c --- .../taskflow/task/create_service_tasks.py | 7 +- .../task/create_ssl_certificate_tasks.py | 25 +- .../task/delete_ssl_certificate_tasks.py | 14 +- poppy/manager/base/services.py | 38 +- poppy/manager/base/ssl_certificate.py | 29 +- poppy/manager/default/services.py | 90 +++-- poppy/manager/default/ssl_certificate.py | 37 +- .../check_cert_status_and_update_tasks.py | 35 +- poppy/storage/base/__init__.py | 2 + poppy/storage/base/certificates.py | 56 +++ poppy/storage/base/driver.py | 14 +- poppy/storage/base/services.py | 56 +-- poppy/storage/cassandra/certificates.py | 295 ++++++++++++++++ poppy/storage/cassandra/controllers.py | 4 +- poppy/storage/cassandra/driver.py | 16 +- poppy/storage/cassandra/services.py | 327 ++---------------- poppy/storage/mockdb/certificates.py | 77 +++++ poppy/storage/mockdb/controllers.py | 4 +- poppy/storage/mockdb/driver.py | 8 +- poppy/storage/mockdb/services.py | 89 ++--- poppy/transport/pecan/controllers/v1/admin.py | 9 +- .../pecan/controllers/v1/services.py | 16 +- tests/functional/transport/pecan/base.py | 30 +- .../test_get_ssl_certficate_by_status.py | 5 +- .../pecan/controllers/test_retry_list.py | 12 +- .../distributed_task/taskflow/test_flows.py | 199 ++++++++--- tests/unit/manager/default/test_services.py | 67 ++-- .../akamai/background_jobs/akamai_mocks.py | 6 + .../akamai/background_jobs/test_flows.py | 8 +- .../storage/cassandra/test_certificates.py | 106 ++++++ tests/unit/storage/cassandra/test_services.py | 83 ++--- 31 files changed, 1094 insertions(+), 670 deletions(-) create mode 100644 poppy/storage/base/certificates.py create mode 100644 poppy/storage/cassandra/certificates.py create mode 100644 poppy/storage/mockdb/certificates.py create mode 100644 tests/unit/storage/cassandra/test_certificates.py diff --git a/poppy/distributed_task/taskflow/task/create_service_tasks.py b/poppy/distributed_task/taskflow/task/create_service_tasks.py index 6605990d..4e9169d6 100644 --- a/poppy/distributed_task/taskflow/task/create_service_tasks.py +++ b/poppy/distributed_task/taskflow/task/create_service_tasks.py @@ -49,13 +49,18 @@ class CreateProviderServicesTask(task.Task): service_controller, self.storage_controller = \ memoized_controllers.task_controllers('poppy', 'storage') + _, self.ssl_certificate_manager = \ + memoized_controllers.task_controllers('poppy', 'ssl_certificate') + + self.ssl_certificate_storage = self.ssl_certificate_manager.storage + providers_list = json.loads(providers_list_json) try: service_obj = self.storage_controller.get(project_id, service_id) for domain in service_obj.domains: if domain.certificate == 'san': cert_for_domain = ( - self.storage_controller.get_certs_by_domain( + self.ssl_certificate_storage.get_certs_by_domain( domain.domain, project_id=project_id, flavor_id=service_obj.flavor_id, diff --git a/poppy/distributed_task/taskflow/task/create_ssl_certificate_tasks.py b/poppy/distributed_task/taskflow/task/create_ssl_certificate_tasks.py index 45db5c9c..1c31b98b 100644 --- a/poppy/distributed_task/taskflow/task/create_ssl_certificate_tasks.py +++ b/poppy/distributed_task/taskflow/task/create_ssl_certificate_tasks.py @@ -77,8 +77,9 @@ class SendNotificationTask(task.Task): class UpdateCertInfoTask(task.Task): def execute(self, project_id, cert_obj_json, responders): - service_controller, self.storage_controller = \ - memoized_controllers.task_controllers('poppy', 'storage') + service_controller, self.ssl_certificate_manager = \ + memoized_controllers.task_controllers('poppy', 'ssl_certificate') + self.storage_controller = self.ssl_certificate_manager.storage cert_details = {} for responder in responders: @@ -86,24 +87,28 @@ class UpdateCertInfoTask(task.Task): cert_details[provider] = json.dumps(responder[provider]) cert_obj = ssl_certificate.load_from_json(json.loads(cert_obj_json)) - self.storage_controller.update_cert_info(cert_obj.domain_name, - cert_obj.cert_type, - cert_obj.flavor_id, - cert_details) + self.storage_controller.update_certificate( + cert_obj.domain_name, + cert_obj.cert_type, + cert_obj.flavor_id, + cert_details + ) return class CreateStorageSSLCertificateTask(task.Task): - '''This task is meant to be used in san rerun flow.''' + """This task is meant to be used in san rerun flow.""" def execute(self, project_id, cert_obj_json): cert_obj = ssl_certificate.load_from_json(json.loads(cert_obj_json)) - service_controller, self.storage_controller = \ - memoized_controllers.task_controllers('poppy', 'storage') + service_controller, self.ssl_certificate_manager = \ + memoized_controllers.task_controllers('poppy', 'ssl_certificate') + self.storage_controller = self.ssl_certificate_manager.storage + try: - self.storage_controller.create_cert(project_id, cert_obj) + self.storage_controller.create_certificate(project_id, cert_obj) except ValueError as e: LOG.exception(e) 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 860e727e..f27efcfd 100644 --- a/poppy/distributed_task/taskflow/task/delete_ssl_certificate_tasks.py +++ b/poppy/distributed_task/taskflow/task/delete_ssl_certificate_tasks.py @@ -57,12 +57,16 @@ class SendNotificationTask(task.Task): class DeleteStorageSSLCertificateTask(task.Task): def execute(self, project_id, domain_name, cert_type): - service_controller, self.storage_controller = \ - memoized_controllers.task_controllers('poppy', 'storage') + service_controller, self.ssl_certificate_manager = \ + memoized_controllers.task_controllers('poppy', 'ssl_certificate') + self.storage_controller = self.ssl_certificate_manager.storage + try: - self.storage_controller.delete_cert(project_id, - domain_name, - cert_type) + self.storage_controller.delete_certificate( + project_id, + domain_name, + cert_type + ) except ValueError as e: LOG.exception(e) diff --git a/poppy/manager/base/services.py b/poppy/manager/base/services.py index 94e8b85c..12bbb2ab 100644 --- a/poppy/manager/base/services.py +++ b/poppy/manager/base/services.py @@ -33,19 +33,19 @@ class ServicesControllerBase(controller.ManagerControllerBase): self.notification_wrapper = notifications.NotificationWrapper() @abc.abstractmethod - def list(self, project_id, marker=None, limit=None): - """list + def get_services(self, project_id, marker=None, limit=None): + """Get a list of services. :param project_id :param marker - :limit + :param limit :raises: NotImplementedError """ raise NotImplementedError @abc.abstractmethod - def get(self, project_id, service_id): - """GET + def get_service(self, project_id, service_id): + """Get a service. :param project_id :param service_id @@ -54,22 +54,26 @@ class ServicesControllerBase(controller.ManagerControllerBase): raise NotImplementedError @abc.abstractmethod - def create(self, project_id, auth_token, service_obj): - """create + def create_service(self, project_id, auth_token, service_obj): + """Create a service. :param project_id + :param auth_token :param service_obj :raises: NotImplementedError """ raise NotImplementedError @abc.abstractmethod - def update(self, project_id, service_id, service_obj): - """POST + def update_service(self, project_id, service_id, + auth_token, service_updates, force_update=False): + """Update a service. :param project_id :param service_id - :param service_obj + :param auth_token + :param service_updates + :param force_update :raises: NotImplementedError """ raise NotImplementedError @@ -85,8 +89,8 @@ class ServicesControllerBase(controller.ManagerControllerBase): """ @abc.abstractmethod - def delete(self, project_id, service_id): - """DELETE + def delete_service(self, project_id, service_id): + """Delete a service. :param project_id :param service_id @@ -96,5 +100,13 @@ class ServicesControllerBase(controller.ManagerControllerBase): @abc.abstractmethod def purge(self, project_id, service_id, hard=False, purge_url=None): - '''If purge_url is none, all content of this service will be purge.''' + """Purge assets for a service. + + If purge_url is none, all content of this service will be purged. + + :param project_id + :param service_id + :param hard + :param purge_url + """ raise NotImplementedError diff --git a/poppy/manager/base/ssl_certificate.py b/poppy/manager/base/ssl_certificate.py index b6cb774e..d773c15a 100644 --- a/poppy/manager/base/ssl_certificate.py +++ b/poppy/manager/base/ssl_certificate.py @@ -22,17 +22,38 @@ from poppy.manager.base import controller @six.add_metaclass(abc.ABCMeta) class SSLCertificateController(controller.ManagerControllerBase): - """Home controller base class.""" + """SSL certificate controller base class.""" def __init__(self, manager): super(SSLCertificateController, self).__init__(manager) @abc.abstractmethod - def create_ssl_certificate(self, project_id, domain_name, **extras): - """create_ssl_certificate + def create_ssl_certificate(self, project_id, cert_obj): + """Create ssl certificate. :param project_id - :param domain_name + :param cert_obj :raises: NotImplementedError """ raise NotImplementedError + + @abc.abstractmethod + def delete_ssl_certificate(self, project_id, domain_name, cert_type): + """Delete ssl certificate. + + :param project_id + :param domain_name + :param cert_type + :raises: NotImplementedError + """ + raise NotImplementedError + + @abc.abstractmethod + def get_certs_info_by_domain(self, domain_name, project_id): + """Get ssl certificate by domain. + + :param domain_name: + :param project_id: + :raises: NotImplementedError + """ + raise NotImplementedError diff --git a/poppy/manager/default/services.py b/poppy/manager/default/services.py index 61bc4765..f4eab133 100644 --- a/poppy/manager/default/services.py +++ b/poppy/manager/default/services.py @@ -50,6 +50,9 @@ class DefaultServicesController(base.ServicesController): super(DefaultServicesController, self).__init__(manager) self.storage_controller = self._driver.storage.services_controller + self.ssl_certificate_storage = ( + self._driver.storage.certificates_controller + ) self.flavor_controller = self._driver.storage.flavors_controller self.dns_controller = self._driver.dns.services_controller self.distributed_task_controller = ( @@ -100,13 +103,6 @@ class DefaultServicesController(base.ServicesController): return services_project_ids - def get_certs_by_status(self, status): - - services_project_ids = \ - self.storage_controller.get_certs_by_status(status) - - return services_project_ids - def get_domains_by_provider_url(self, provider_url): domains = \ @@ -151,26 +147,26 @@ class DefaultServicesController(base.ServicesController): request_url='/*') caching_entry['rules'].append(default_rule.to_dict()) - def list(self, project_id, marker=None, limit=None): - """list. + def get_services(self, project_id, marker=None, limit=None): + """Get a list of services. :param project_id :param marker :param limit :return list """ - return self.storage_controller.list(project_id, marker, limit) + return self.storage_controller.get_services(project_id, marker, limit) - def get(self, project_id, service_id): + def get_service(self, project_id, service_id): """get. :param project_id :param service_id :return controller """ - return self.storage_controller.get(project_id, service_id) + return self.storage_controller.get_service(project_id, service_id) - def create(self, project_id, auth_token, service_json): + def create_service(self, project_id, auth_token, service_json): """create. :param project_id @@ -232,8 +228,7 @@ class DefaultServicesController(base.ServicesController): raise e try: - self.storage_controller.create(project_id, - service_obj) + self.storage_controller.create_service(project_id, service_obj) except ValueError as e: raise e @@ -252,8 +247,8 @@ class DefaultServicesController(base.ServicesController): return service_obj - def update(self, project_id, service_id, - auth_token, service_updates, force_update=False): + def update_service(self, project_id, service_id, + auth_token, service_updates, force_update=False): """update. :param project_id @@ -265,7 +260,10 @@ class DefaultServicesController(base.ServicesController): """ # get the current service object try: - service_old = self.storage_controller.get(project_id, service_id) + service_old = self.storage_controller.get_service( + project_id, + service_id + ) except ValueError: raise errors.ServiceNotFound("Service not found") @@ -292,7 +290,7 @@ class DefaultServicesController(base.ServicesController): # old domains need to bind as well elif domain.certificate == 'san': cert_for_domain = ( - self.storage_controller.get_certs_by_domain( + self.ssl_certificate_storage.get_certs_by_domain( domain.domain, project_id=project_id, flavor_id=service_old.flavor_id, @@ -354,7 +352,7 @@ class DefaultServicesController(base.ServicesController): store) elif domain.certificate == 'san': cert_for_domain = ( - self.storage_controller.get_certs_by_domain( + self.ssl_certificate_storage.get_certs_by_domain( domain.domain, project_id=project_id, flavor_id=service_new.flavor_id, @@ -404,7 +402,7 @@ class DefaultServicesController(base.ServicesController): project_id, new_cert_detail ) - self.storage_controller.create_cert( + self.ssl_certificate_storage.create_certificate( project_id, new_cert_obj ) @@ -434,7 +432,11 @@ class DefaultServicesController(base.ServicesController): for provider in provider_details: provider_details[provider].status = u'update_in_progress' service_new.provider_details = provider_details - self.storage_controller.update(project_id, service_id, service_new) + self.storage_controller.update_service( + project_id, + service_id, + service_new + ) kwargs = { 'project_id': project_id, @@ -458,13 +460,16 @@ class DefaultServicesController(base.ServicesController): def set_service_provider_details(self, project_id, service_id, auth_token, status): - old_service = self.storage_controller.get(project_id, service_id) + old_service = self.storage_controller.get_service( + project_id, + service_id + ) if ( old_service.status == 'create_in_progress' and old_service.provider_details == {} ): - self.update( + self.update_service( project_id, service_id, auth_token, [], force_update=True) return 202 self.storage_controller.set_service_provider_details( @@ -495,7 +500,7 @@ class DefaultServicesController(base.ServicesController): if action == 'delete': LOG.info('Deleting service: %s, project_id: %s' % ( service_obj.service_id, project_id)) - self.delete(project_id, service_obj.service_id) + self.delete_service(project_id, service_obj.service_id) elif action == 'enable': LOG.info('Enabling service: %s, project_id: %s' % ( service_obj.service_id, project_id)) @@ -539,7 +544,11 @@ class DefaultServicesController(base.ServicesController): return marker = None - service_batch = self.storage_controller.list(project_id, marker, 10) + service_batch = self.storage_controller.get_services( + project_id, + marker, + 10 + ) while len(service_batch) > 0: marker = service_batch[-1].service_id # process previous batch @@ -547,19 +556,23 @@ class DefaultServicesController(base.ServicesController): self._action_per_service_obj(project_id=project_id, action=action, service_obj=service_obj) - service_batch = self.storage_controller.list(project_id, marker, - 10) + service_batch = self.storage_controller.get_services( + project_id, + marker, + 10 + ) return - def delete(self, project_id, service_id): + def delete_service(self, project_id, service_id): """delete. :param project_id :param service_id :raises LookupError """ - service_obj = self.storage_controller.get(project_id, service_id) + service_obj = self.storage_controller.get_service( + project_id, service_id) # get provider details for this service provider_details = self._get_provider_details(project_id, service_id) @@ -569,7 +582,11 @@ class DefaultServicesController(base.ServicesController): service_obj.provider_details[provider].status = ( u'delete_in_progress') - self.storage_controller.update(project_id, service_id, service_obj) + self.storage_controller.update_service( + project_id, + service_id, + service_obj + ) kwargs = { "provider_details": json.dumps( @@ -588,7 +605,10 @@ class DefaultServicesController(base.ServicesController): def purge(self, project_id, service_id, hard=False, purge_url=None): """If purge_url is none, all content of this service will be purge.""" try: - service_obj = self.storage_controller.get(project_id, service_id) + service_obj = self.storage_controller.get_service( + project_id, + service_id + ) except ValueError as e: # This except is hit when service object does not exist raise LookupError(str(e)) @@ -607,7 +627,11 @@ class DefaultServicesController(base.ServicesController): service_obj.provider_details[provider].status = ( u'update_in_progress') - self.storage_controller.update(project_id, service_id, service_obj) + self.storage_controller.update_service( + project_id, + service_id, + service_obj + ) # possible validation of purge url here... kwargs = { diff --git a/poppy/manager/default/ssl_certificate.py b/poppy/manager/default/ssl_certificate.py index dd2d2db6..3447cdd0 100644 --- a/poppy/manager/default/ssl_certificate.py +++ b/poppy/manager/default/ssl_certificate.py @@ -36,8 +36,10 @@ class DefaultSSLCertificateController(base.SSLCertificateController): super(DefaultSSLCertificateController, self).__init__(manager) self.distributed_task_controller = ( - self._driver.distributed_task.services_controller) - self.storage_controller = self._driver.storage.services_controller + self._driver.distributed_task.services_controller + ) + self.storage = self._driver.storage.certificates_controller + self.service_storage = self._driver.storage.services_controller self.flavor_controller = self._driver.storage.flavors_controller def create_ssl_certificate(self, project_id, cert_obj): @@ -57,9 +59,10 @@ class DefaultSSLCertificateController(base.SSLCertificateController): raise e try: - self.storage_controller.create_cert( + self.storage.create_certificate( project_id, - cert_obj) + cert_obj + ) # ValueError will be raised if the cert_info has already existed except ValueError as e: raise e @@ -76,8 +79,7 @@ class DefaultSSLCertificateController(base.SSLCertificateController): **kwargs) return kwargs - def delete_ssl_certificate(self, project_id, domain_name, - cert_type): + def delete_ssl_certificate(self, project_id, domain_name, cert_type): kwargs = { 'project_id': project_id, 'domain_name': domain_name, @@ -91,7 +93,7 @@ class DefaultSSLCertificateController(base.SSLCertificateController): def get_certs_info_by_domain(self, domain_name, project_id): try: - certs_info = self.storage_controller.get_certs_by_domain( + certs_info = self.storage.get_certs_by_domain( domain_name=domain_name, project_id=project_id) if not certs_info: @@ -122,7 +124,7 @@ class DefaultSSLCertificateController(base.SSLCertificateController): def update_san_retry_list(self, queue_data_list): for r in queue_data_list: - service_obj = self.storage_controller\ + service_obj = self.service_storage\ .get_service_details_by_domain_name(r['domain_name']) if service_obj is None and r.get('validate_service', True): raise LookupError(u'Domain {0} does not exist on any service, ' @@ -131,7 +133,7 @@ class DefaultSSLCertificateController(base.SSLCertificateController): 'to retry this san-retry request forcefully'. format(r['domain_name'], r)) - cert_for_domain = self.storage_controller.get_certs_by_domain( + cert_for_domain = self.storage.get_certs_by_domain( r['domain_name']) if cert_for_domain != []: if cert_for_domain.get_cert_status() == "deployed": @@ -154,7 +156,7 @@ class DefaultSSLCertificateController(base.SSLCertificateController): akamai_driver.mod_san_queue.put_queue_data(new_queue_data)] deleted = tuple(x for x in orig if x not in res) - # other provider's retry-list implementaiton goes here + # other provider's retry-list implementation goes here return res, deleted def rerun_san_retry_list(self): @@ -168,10 +170,10 @@ class DefaultSSLCertificateController(base.SSLCertificateController): # remove duplicates # see http://bit.ly/1mX2Vcb for details def remove_duplicates(data): - '''Remove duplicates from the data (normally a list). + """Remove duplicates from the data (normally a list). The data must be sortable and have an equality operator - ''' + """ data = sorted(data) return [k for k, _ in itertools.groupby(data)] retry_list = remove_duplicates(retry_list) @@ -179,7 +181,7 @@ class DefaultSSLCertificateController(base.SSLCertificateController): # double check in POST. This check should really be first done in # PUT for r in retry_list: - service_obj = self.storage_controller\ + service_obj = self.service_storage\ .get_service_details_by_domain_name(r['domain_name']) if service_obj is None and r.get('validate_service', True): raise LookupError(u'Domain {0} does not exist on any ' @@ -189,7 +191,7 @@ class DefaultSSLCertificateController(base.SSLCertificateController): ' san-retry request forcefully'. format(r['domain_name'], r)) - cert_for_domain = self.storage_controller.get_certs_by_domain( + cert_for_domain = self.storage.get_certs_by_domain( r['domain_name']) if cert_for_domain != []: if cert_for_domain.get_cert_status() == "deployed": @@ -207,7 +209,7 @@ class DefaultSSLCertificateController(base.SSLCertificateController): ) cert_for_domain = ( - self.storage_controller.get_certs_by_domain( + self.storage.get_certs_by_domain( cert_obj.domain_name, project_id=cert_obj.project_id, flavor_id=cert_obj.flavor_id, @@ -303,3 +305,8 @@ class DefaultSSLCertificateController(base.SSLCertificateController): res = {} return res + + def get_certs_by_status(self, status): + services_project_ids = self.storage.get_certs_by_status(status) + + return services_project_ids 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 8941ca4a..9dd33936 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 @@ -32,9 +32,11 @@ class GetCertInfoTask(task.Task): default_provides = "cert_obj_json" def execute(self, domain_name, cert_type, flavor_id, project_id): - service_controller, self.storage_controller = \ - memoized_controllers.task_controllers('poppy', 'storage') - res = self.storage_controller.get_certs_by_domain( + service_controller, self.ssl_certificate_manager = \ + memoized_controllers.task_controllers('poppy', 'ssl_certificate') + self.storage = self.ssl_certificate_manager.storage + + res = self.storage.get_certs_by_domain( domain_name, project_id=project_id, flavor_id=flavor_id, cert_type=cert_type) if res is None: @@ -103,26 +105,33 @@ class UpdateCertStatusTask(task.Task): def __init__(self): super(UpdateCertStatusTask, self).__init__() - service_controller, self.storage_controller = \ - memoized_controllers.task_controllers('poppy', 'storage') + service_controller, self.ssl_certificate_manager = \ + memoized_controllers.task_controllers('poppy', 'ssl_certificate') + self.storage_controller = ( + self.ssl_certificate_manager.storage + ) + self.service_storage = service_controller.storage_controller def execute(self, project_id, cert_obj_json, status_change_to): if cert_obj_json != "": - cert_obj = ssl_certificate.load_from_json(json.loads(cert_obj_json) - ) + cert_obj = ssl_certificate.load_from_json( + json.loads(cert_obj_json) + ) cert_details = cert_obj.cert_details if status_change_to != "": cert_details['Akamai']['extra_info']['status'] = ( status_change_to) cert_details['Akamai'] = json.dumps(cert_details['Akamai']) - self.storage_controller.update_cert_info(cert_obj.domain_name, - cert_obj.cert_type, - cert_obj.flavor_id, - cert_details) + self.storage_controller.update_certificate( + cert_obj.domain_name, + cert_obj.cert_type, + cert_obj.flavor_id, + cert_details + ) service_obj = ( - self.storage_controller. + self.service_storage. get_service_details_by_domain_name(cert_obj.domain_name) ) # Update provider details @@ -131,7 +140,7 @@ class UpdateCertStatusTask(task.Task): domains_certificate_status.\ set_domain_certificate_status(cert_obj.domain_name, status_change_to) - self.storage_controller.update_provider_details( + self.service_storage.update_provider_details( project_id, service_obj.service_id, service_obj.provider_details diff --git a/poppy/storage/base/__init__.py b/poppy/storage/base/__init__.py index 3277a8c2..65bea622 100644 --- a/poppy/storage/base/__init__.py +++ b/poppy/storage/base/__init__.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from poppy.storage.base import certificates from poppy.storage.base import driver from poppy.storage.base import flavors from poppy.storage.base import services @@ -20,5 +21,6 @@ from poppy.storage.base import services Driver = driver.StorageDriverBase +CertificatesController = certificates.CertificatesControllerBase FlavorsController = flavors.FlavorsControllerBase ServicesController = services.ServicesControllerBase diff --git a/poppy/storage/base/certificates.py b/poppy/storage/base/certificates.py new file mode 100644 index 00000000..a76871ee --- /dev/null +++ b/poppy/storage/base/certificates.py @@ -0,0 +1,56 @@ +# Copyright (c) 2016 Rackspace, Inc. +# +# 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 abc + +import six + +from poppy.storage.base import controller + + +@six.add_metaclass(abc.ABCMeta) +class CertificatesControllerBase(controller.StorageControllerBase): + + @abc.abstractmethod + def create_certificate(self, project_id, cert_obj): + """Create a certificate + + :param project_id + :param cert_obj + :raise NotImplementedError + """ + raise NotImplementedError + + def delete_certificate(self, project_id, domain_name, cert_type): + """Delete a certificate. + + :param project_id + :param domain_name + :param cert_type + :raise NotImplementedError + """ + raise NotImplementedError + + @abc.abstractmethod + def update_certificate(self, domain_name, cert_type, flavor_id, + cert_details): + """update_cert_info. + + :param domain_name + :param cert_type + :param flavor_id + :param cert_details + """ + raise NotImplementedError diff --git a/poppy/storage/base/driver.py b/poppy/storage/base/driver.py index 5b9358d4..ddea089a 100644 --- a/poppy/storage/base/driver.py +++ b/poppy/storage/base/driver.py @@ -62,8 +62,8 @@ class StorageDriverBase(object): raise NotImplementedError @abc.abstractproperty - def services_controller(self): - """Returns the driver's hostname controller. + def certificates_controller(self): + """Returns the driver's certificates controller. :raise NotImplementedError """ @@ -71,7 +71,15 @@ class StorageDriverBase(object): @abc.abstractproperty def flavors_controller(self): - """Returns the driver's hostname controller. + """Returns the driver's flavors controller. + + :raise NotImplementedError + """ + raise NotImplementedError + + @abc.abstractproperty + def services_controller(self): + """Returns the driver's services controller. :raise NotImplementedError """ diff --git a/poppy/storage/base/services.py b/poppy/storage/base/services.py index a34f00e5..7f331825 100644 --- a/poppy/storage/base/services.py +++ b/poppy/storage/base/services.py @@ -28,8 +28,8 @@ class ServicesControllerBase(controller.StorageControllerBase): super(ServicesControllerBase, self).__init__(driver) @abc.abstractmethod - def list(self, project_id, marker=None, limit=None): - """list + def get_services(self, project_id, marker=None, limit=None): + """Get a list of services for a project. :param project_id :param marker @@ -39,19 +39,18 @@ class ServicesControllerBase(controller.StorageControllerBase): raise NotImplementedError @abc.abstractmethod - def create(self, project_id, service_id, service_json): - """create + def create_service(self, project_id, service_obj): + """Create a service. :param project_id - :param service_id - :param service_json + :param service_obj :raise NotImplementedError """ raise NotImplementedError @abc.abstractmethod - def update(self, project_id, service_id, service_json): - """update + def update_service(self, project_id, service_id, service_json): + """Update a service. :param project_id :param service_id @@ -64,7 +63,7 @@ class ServicesControllerBase(controller.StorageControllerBase): @abc.abstractmethod def update_state(self, project_id, service_id, state): - """update_state + """Update service state. Update service state @@ -76,8 +75,8 @@ class ServicesControllerBase(controller.StorageControllerBase): raise NotImplementedError @abc.abstractmethod - def delete(self, project_id, service_id): - """delete + def delete_service(self, project_id, service_id): + """Delete a service. :param project_id :param service_id @@ -86,8 +85,8 @@ class ServicesControllerBase(controller.StorageControllerBase): raise NotImplementedError @abc.abstractmethod - def get(self): - """get + def get_service(self, project_id, service_id): + """Get a service object. :raise NotImplementedError """ @@ -95,7 +94,7 @@ class ServicesControllerBase(controller.StorageControllerBase): @abc.abstractmethod def get_provider_details(self, project_id, service_id): - """get_provider_details + """Get provider details for a service. :param project_id :param service_id @@ -104,36 +103,17 @@ class ServicesControllerBase(controller.StorageControllerBase): raise NotImplementedError @abc.abstractmethod - def update_provider_details(self, provider_details): - """update_provider_details - - :param provider_details - :raise NotImplementedError - """ - raise NotImplementedError - - @abc.abstractmethod - def create_cert(self, project_id, cert_obj): - """create_cert + def update_provider_details(self, project_id, service_id, + provider_details): + """Update provider details for a service. :param project_id - :param cert_obj + :param service_id + :param provider_details :raise NotImplementedError """ raise NotImplementedError - @abc.abstractmethod - def update_cert_info(self, domain_name, cert_type, flavor_id, - cert_details): - """update_cert_info. - - :param domain_name - :param cert_type - :param flavor_id - :param cert_info - """ - raise NotImplementedError - @staticmethod def format_result(result): """format_result diff --git a/poppy/storage/cassandra/certificates.py b/poppy/storage/cassandra/certificates.py new file mode 100644 index 00000000..50bb5676 --- /dev/null +++ b/poppy/storage/cassandra/certificates.py @@ -0,0 +1,295 @@ +# Copyright (c) 2016 Rackspace, Inc. +# +# 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 json + +from cassandra import query +from oslo_log import log +from six.moves import filterfalse + +from poppy.model import ssl_certificate +from poppy.storage import base + + +LOG = log.getLogger(__name__) + +CQL_CREATE_CERT = ''' + INSERT INTO certificate_info (project_id, + flavor_id, + cert_type, + domain_name, + cert_details + ) + VALUES (%(project_id)s, + %(flavor_id)s, + %(cert_type)s, + %(domain_name)s, + %(cert_details)s) +''' + +CQL_SEARCH_CERT_BY_DOMAIN = ''' + SELECT project_id, + flavor_id, + cert_type, + domain_name, + cert_details + FROM certificate_info + WHERE domain_name = %(domain_name)s +''' + +CQL_GET_CERTS_BY_STATUS = ''' + SELECT domain_name + FROM cert_status WHERE status = %(status)s +''' + +CQL_DELETE_CERT = ''' + DELETE FROM certificate_info + WHERE domain_name = %(domain_name)s +''' + +CQL_DELETE_CERT_STATUS = ''' + DELETE FROM cert_status + WHERE domain_name = %(domain_name)s +''' + + +CQL_INSERT_CERT_STATUS = ''' + INSERT INTO cert_status (domain_name, + status + ) + VALUES (%(domain_name)s, + %(status)s) +''' + +CQL_UPDATE_CERT_DETAILS = ''' + UPDATE certificate_info + set cert_details = %(cert_details)s + WHERE domain_name = %(domain_name)s + IF cert_type = %(cert_type)s AND flavor_id = %(flavor_id)s +''' + + +class CertificatesController(base.CertificatesController): + + """Certificates Controller.""" + + @property + def session(self): + """Get session. + + :returns session + """ + return self._driver.database + + def create_certificate(self, project_id, cert_obj): + if self.cert_already_exist(domain_name=cert_obj.domain_name, + comparing_cert_type=cert_obj.cert_type, + comparing_flavor_id=cert_obj.flavor_id, + comparing_project_id=project_id): + raise ValueError('Certificate already exists ' + 'for {0} '.format(cert_obj.domain_name)) + + args = { + 'project_id': project_id, + 'flavor_id': cert_obj.flavor_id, + 'cert_type': cert_obj.cert_type, + 'domain_name': cert_obj.domain_name, + # when create the cert, cert domain has not been assigned yet + # In future we can tweak the logic to assign cert_domain + # 'cert_domain': '', + 'cert_details': cert_obj.cert_details + } + stmt = query.SimpleStatement( + CQL_CREATE_CERT, + consistency_level=self._driver.consistency_level) + self.session.execute(stmt, args) + + def delete_certificate(self, project_id, domain_name, cert_type): + args = { + 'domain_name': domain_name.lower() + } + + stmt = query.SimpleStatement( + CQL_SEARCH_CERT_BY_DOMAIN, + consistency_level=self._driver.consistency_level) + result_set = self.session.execute(stmt, args) + complete_results = list(result_set) + if complete_results: + for r in complete_results: + r_project_id = str(r.get('project_id')) + r_cert_type = str(r.get('cert_type')) + if r_project_id == str(project_id) and \ + r_cert_type == str(cert_type): + args = { + 'domain_name': str(r.get('domain_name')) + } + stmt = query.SimpleStatement( + CQL_DELETE_CERT, + consistency_level=self._driver.consistency_level) + self.session.execute(stmt, args) + stmt = query.SimpleStatement( + CQL_DELETE_CERT_STATUS, + consistency_level=self._driver.consistency_level) + self.session.execute(stmt, args) + else: + raise ValueError( + "No certificate found for: {0}," + "type: {1}".format(domain_name, cert_type)) + + def update_certificate(self, domain_name, cert_type, flavor_id, + cert_details): + + args = { + 'domain_name': domain_name, + 'cert_type': cert_type, + 'flavor_id': flavor_id, + 'cert_details': cert_details + } + stmt = query.SimpleStatement( + CQL_UPDATE_CERT_DETAILS, + consistency_level=self._driver.consistency_level) + self.session.execute(stmt, args) + + try: + provider_status = json.loads(cert_details.values()[0]) + cert_status = provider_status['extra_info']['status'] + except (IndexError, IndexError, ValueError) as e: + LOG.error("Certificate details " + "in inconsistent " + "state: {0}".format(cert_details)) + LOG.error(e) + else: + cert_args = { + 'domain_name': domain_name, + 'status': cert_status + } + stmt = query.SimpleStatement( + CQL_INSERT_CERT_STATUS, + consistency_level=self._driver.consistency_level) + self.session.execute(stmt, cert_args) + + def get_certs_by_status(self, status): + + LOG.info("Getting domains which have " + "certificate in status : {0}".format(status)) + args = { + 'status': status + } + stmt = query.SimpleStatement( + CQL_GET_CERTS_BY_STATUS, + consistency_level=self._driver.consistency_level) + resultset = self.session.execute(stmt, args) + complete_results = list(resultset) + + return complete_results + + def get_certs_by_domain(self, domain_name, project_id=None, + flavor_id=None, + cert_type=None): + + LOG.info("Check if cert on '{0}' exists".format(domain_name)) + args = { + 'domain_name': domain_name.lower() + } + stmt = query.SimpleStatement( + CQL_SEARCH_CERT_BY_DOMAIN, + consistency_level=self._driver.consistency_level) + resultset = self.session.execute(stmt, args) + complete_results = list(resultset) + certs = [] + if complete_results: + for r in complete_results: + r_project_id = str(r.get('project_id')) + r_flavor_id = str(r.get('flavor_id')) + r_cert_type = str(r.get('cert_type')) + r_cert_details = {} + # in case cert_details is None + cert_details = r.get('cert_details', {}) or {} + # Need to convert cassandra dict into real dict + # And the value of cert_details is a string dict + for key in cert_details: + r_cert_details[key] = json.loads(cert_details[key]) + LOG.info( + "Certificate for domain: {0} with flavor_id: {1}, " + "cert_details : {2} and cert_type: {3} present " + "on project_id: {4}".format( + domain_name, + r_flavor_id, + r_cert_details, + r_cert_type, + r_project_id + ) + ) + ssl_cert = ssl_certificate.SSLCertificate( + domain_name=domain_name, + flavor_id=r_flavor_id, + cert_details=r_cert_details, + cert_type=r_cert_type, + project_id=r_project_id + ) + + certs.append(ssl_cert) + + non_none_attrs_gen = filterfalse( + lambda x: list(x.values())[0] is None, [{'project_id': project_id}, + {'flavor_id': flavor_id}, + {'cert_type': cert_type}]) + non_none_attrs_list = list(non_none_attrs_gen) + non_none_attrs_dict = {} + + if non_none_attrs_list: + for attr in non_none_attrs_list: + non_none_attrs_dict.update(attr) + + def argfilter(certificate): + all_conditions = True + if non_none_attrs_dict: + for k, v in non_none_attrs_dict.items(): + if getattr(certificate, k) != v: + all_conditions = False + + return all_conditions + + total_certs = [cert for cert in certs if argfilter(cert)] + + if len(total_certs) == 1: + return total_certs[0] + else: + return total_certs + + def cert_already_exist(self, domain_name, comparing_cert_type, + comparing_flavor_id, comparing_project_id): + """cert_already_exist + + Check if a cert with this domain name and type has already been + created, or if the domain has been taken by other customers + + :param domain_name + :param comparing_cert_type + :param comparing_flavor_id + :param comparing_project_id + + :returns Boolean if the cert with same type exists with another user. + """ + cert = self.get_certs_by_domain( + domain_name=domain_name, + cert_type=comparing_cert_type, + flavor_id=comparing_flavor_id + ) + + if cert: + return True + else: + return False diff --git a/poppy/storage/cassandra/controllers.py b/poppy/storage/cassandra/controllers.py index ed0f48be..d76995ff 100644 --- a/poppy/storage/cassandra/controllers.py +++ b/poppy/storage/cassandra/controllers.py @@ -23,8 +23,10 @@ Field Mappings: updated and documented in each controller class. """ +from poppy.storage.cassandra import certificates from poppy.storage.cassandra import flavors from poppy.storage.cassandra import services -ServicesController = services.ServicesController +CertificatesController = certificates.CertificatesController FlavorsController = flavors.FlavorsController +ServicesController = services.ServicesController diff --git a/poppy/storage/cassandra/driver.py b/poppy/storage/cassandra/driver.py index cb329930..d7c69085 100644 --- a/poppy/storage/cassandra/driver.py +++ b/poppy/storage/cassandra/driver.py @@ -220,12 +220,12 @@ class CassandraStorageDriver(base.Driver): return _connection(self.cassandra_conf, self.datacenter) @property - def services_controller(self): - """services_controller. + def certificates_controller(self): + """certificates_controller. - :returns service controller + :returns certificates controller """ - return controllers.ServicesController(self) + return controllers.CertificatesController(self) @property def flavors_controller(self): @@ -235,6 +235,14 @@ class CassandraStorageDriver(base.Driver): """ return controllers.FlavorsController(self) + @property + def services_controller(self): + """services_controller. + + :returns service controller + """ + return controllers.ServicesController(self) + @property def database(self): """database. diff --git a/poppy/storage/cassandra/services.py b/poppy/storage/cassandra/services.py index 152559ad..c6017b16 100644 --- a/poppy/storage/cassandra/services.py +++ b/poppy/storage/cassandra/services.py @@ -22,16 +22,8 @@ except ImportError: # pragma: no cover import collections # pragma: no cover from cassandra import query - -import six - from oslo_log import log - -if six.PY2: - from itertools import ifilterfalse as filterfalse -else: - from itertools import filterfalse from poppy.model.helpers import cachingrule from poppy.model.helpers import domain from poppy.model.helpers import origin @@ -40,7 +32,6 @@ from poppy.model.helpers import restriction from poppy.model.helpers import rule from poppy.model import log_delivery as ld from poppy.model import service -from poppy.model import ssl_certificate from poppy.storage import base LOG = log.getLogger(__name__) @@ -190,62 +181,6 @@ CQL_CREATE_SERVICE = ''' %(log_delivery)s) ''' -CQL_CREATE_CERT = ''' - INSERT INTO certificate_info (project_id, - flavor_id, - cert_type, - domain_name, - cert_details - ) - VALUES (%(project_id)s, - %(flavor_id)s, - %(cert_type)s, - %(domain_name)s, - %(cert_details)s) -''' - -CQL_VERIFY_CERT = ''' - SELECT project_id, - flavor_id, - cert_type, - domain_name - FROM certificate_info - WHERE domain_name = %(domain_name)s -''' - -CQL_SEARCH_CERT_BY_DOMAIN = ''' - SELECT project_id, - flavor_id, - cert_type, - domain_name, - cert_details - FROM certificate_info - WHERE domain_name = %(domain_name)s -''' - -CQL_DELETE_CERT = ''' - DELETE FROM certificate_info - WHERE domain_name = %(domain_name)s -''' - -CQL_INSERT_CERT_STATUS = ''' - INSERT INTO cert_status (domain_name, - status - ) - VALUES (%(domain_name)s, - %(status)s) -''' - -CQL_DELETE_CERT_STATUS = ''' - DELETE FROM cert_status - WHERE domain_name = %(domain_name)s -''' - - -CQL_GET_CERTS_BY_STATUS = ''' - SELECT domain_name - FROM cert_status WHERE status = %(status)s -''' CQL_UPDATE_SERVICE = CQL_CREATE_SERVICE @@ -320,7 +255,7 @@ class ServicesController(base.ServicesController): """ return self._driver.database - def list(self, project_id, marker, limit): + def get_services(self, project_id, marker, limit): """list. :param project_id @@ -347,11 +282,11 @@ class ServicesController(base.ServicesController): return services - def get(self, project_id, service_id): + def get_service(self, project_id, service_id): """get. :param project_id - :param service_name + :param service_id :returns result The requested service :raises ValueError @@ -364,8 +299,8 @@ class ServicesController(base.ServicesController): stmt = query.SimpleStatement( CQL_GET_SERVICE, consistency_level=self._driver.consistency_level) - resultset = self.session.execute(stmt, args) - complete_result = list(resultset) + result_set = self.session.execute(stmt, args) + complete_result = list(result_set) if len(complete_result) != 1: raise ValueError('No service found: %s' % service_id) @@ -418,30 +353,6 @@ class ServicesController(base.ServicesController): LOG.exception(ex) return False - def cert_already_exist(self, domain_name, comparing_cert_type, - comparing_flavor_id, - comparing_project_id): - """cert_already_exist - - Check if a cert with this domain name and type has already been - created, or if the domain has been taken by other customers - - :param domain_name - :param comparing_cert_type - :param comparing_flavor_id - :param comparing_project_id - - :returns Boolean if the cert with same type exists with another user. - """ - cert = self.get_certs_by_domain(domain_name=domain_name, - cert_type=comparing_cert_type, - flavor_id=comparing_flavor_id) - - if cert: - return True - else: - return False - def get_service_count(self, project_id): """get_service_count @@ -484,8 +395,8 @@ class ServicesController(base.ServicesController): CQL_GET_SERVICE_STATUS, consistency_level=self._driver.consistency_level) - resultset = self.session.execute(stmt, args) - complete_results = list(resultset) + result_set = self.session.execute(stmt, args) + complete_results = list(result_set) for result in complete_results: result['service_id'] = str(result['service_id']) @@ -519,9 +430,9 @@ class ServicesController(base.ServicesController): CQL_GET_BY_PROVIDER_URL, consistency_level=self._driver.consistency_level) - resultset = self.session.execute(stmt, get_domain_provider_url_args) + result_set = self.session.execute(stmt, get_domain_provider_url_args) - return list(resultset) + return list(result_set) def delete_provider_url(self, provider_url, domain_name): @@ -562,8 +473,8 @@ class ServicesController(base.ServicesController): stmt = query.SimpleStatement( CQL_GET_SERVICE_LIMIT, consistency_level=self._driver.consistency_level) - resultset = self.session.execute(stmt, args) - complete_results = list(resultset) + result_set = self.session.execute(stmt, args) + complete_results = list(result_set) if complete_results: LOG.info("Checking for service limit for project_id: '{0}' " "existence yielded {1}".format(project_id, @@ -625,7 +536,7 @@ class ServicesController(base.ServicesController): :param project_id :param service_id - + :param status """ LOG.info("Setting service " @@ -656,132 +567,7 @@ class ServicesController(base.ServicesController): service_id=service_id, provider_details=provider_details_dict) - def get_certs_by_status(self, status): - - LOG.info("Getting domains which have " - "certificate in status : {0}".format(status)) - args = { - 'status': status - } - stmt = query.SimpleStatement( - CQL_GET_CERTS_BY_STATUS, - consistency_level=self._driver.consistency_level) - resultset = self.session.execute(stmt, args) - complete_results = list(resultset) - - return complete_results - - def get_certs_by_domain(self, domain_name, project_id=None, flavor_id=None, - cert_type=None): - LOG.info("Check if cert on '{0}' exists".format(domain_name)) - args = { - 'domain_name': domain_name.lower() - } - stmt = query.SimpleStatement( - CQL_SEARCH_CERT_BY_DOMAIN, - consistency_level=self._driver.consistency_level) - resultset = self.session.execute(stmt, args) - complete_results = list(resultset) - certs = [] - if complete_results: - for r in complete_results: - r_project_id = str(r.get('project_id')) - r_flavor_id = str(r.get('flavor_id')) - r_cert_type = str(r.get('cert_type')) - r_cert_details = {} - # in case cert_details is None - cert_details = r.get('cert_details', {}) or {} - # Need to convert cassandra dict into real dict - # And the value of cert_details is a string dict - for key in cert_details: - r_cert_details[key] = json.loads(cert_details[key]) - LOG.info("Certificate for domain: {0} " - "with flavor_id: {1}, " - "cert_details : {2} and " - "cert_type: {3} present " - "on project_id: {4}".format(domain_name, - r_flavor_id, - r_cert_details, - r_cert_type, - r_project_id)) - ssl_cert = ssl_certificate.SSLCertificate( - domain_name=domain_name, - flavor_id=r_flavor_id, - cert_details=r_cert_details, - cert_type=r_cert_type, - project_id=r_project_id) - - certs.append(ssl_cert) - - non_none_attrs_gen = filterfalse( - lambda x: list(x.values())[0] is None, [{'project_id': project_id}, - {'flavor_id': flavor_id}, - {'cert_type': cert_type}]) - non_none_attrs_list = list(non_none_attrs_gen) - non_none_attrs_dict = {} - - if non_none_attrs_list: - for attr in non_none_attrs_list: - non_none_attrs_dict.update(attr) - - def argfilter(certificate): - all_conditions = True - if non_none_attrs_dict: - for k, v in non_none_attrs_dict.items(): - if getattr(certificate, k) != v: - all_conditions = False - - return all_conditions - - total_certs = [cert for cert in certs if argfilter(cert)] - - if len(total_certs) == 1: - return total_certs[0] - else: - return total_certs - - def delete_cert(self, project_id, domain_name, cert_type): - """delete_cert - - Delete a certificate. - - :param project_id - :param domain_name - :param cert_type - - :raises ValueError - """ - args = { - 'domain_name': domain_name.lower() - } - - stmt = query.SimpleStatement( - CQL_SEARCH_CERT_BY_DOMAIN, - consistency_level=self._driver.consistency_level) - resultset = self.session.execute(stmt, args) - complete_results = list(resultset) - if complete_results: - for r in complete_results: - r_project_id = str(r.get('project_id')) - r_cert_type = str(r.get('cert_type')) - if r_project_id == str(project_id) and \ - r_cert_type == str(cert_type): - args = { - 'domain_name': str(r.get('domain_name')) - } - stmt = query.SimpleStatement( - CQL_DELETE_CERT, - consistency_level=self._driver.consistency_level) - self.session.execute(stmt, args) - stmt = query.SimpleStatement( - CQL_DELETE_CERT_STATUS, - consistency_level=self._driver.consistency_level) - self.session.execute(stmt, args) - else: - raise ValueError("No certificate found for: {0}," - "type: {1}".format(domain_name, cert_type)) - - def create(self, project_id, service_obj): + def create_service(self, project_id, service_obj): """create. :param project_id @@ -842,7 +628,7 @@ class ServicesController(base.ServicesController): self.session.execute(batch) - def update(self, project_id, service_id, service_obj): + def update_service(self, project_id, service_id, service_obj): """update. :param project_id @@ -876,8 +662,8 @@ class ServicesController(base.ServicesController): CQL_GET_SERVICE, consistency_level=self._driver.consistency_level) - resultset = self.session.execute(stmt, args) - complete_results = list(resultset) + result_set = self.session.execute(stmt, args) + complete_results = list(result_set) result = complete_results[0] # updates an existing service @@ -938,9 +724,7 @@ class ServicesController(base.ServicesController): self.session.execute(stmt, args) def update_state(self, project_id, service_id, state): - """update_state - - Update service state + """Update service state :param project_id :param service_id @@ -949,13 +733,13 @@ class ServicesController(base.ServicesController): :returns service_obj """ - service_obj = self.get(project_id, service_id) + service_obj = self.get_service(project_id, service_id) service_obj.operator_status = state - self.update(project_id, service_id, service_obj) + self.update_service(project_id, service_id, service_obj) return service_obj - def delete(self, project_id, service_id): + def delete_service(self, project_id, service_id): """delete. Archive local configuration storage @@ -1033,29 +817,6 @@ class ServicesController(base.ServicesController): consistency_level=self._driver.consistency_level) self.session.execute(stmt, delete_args) - def create_cert(self, project_id, cert_obj): - if self.cert_already_exist(domain_name=cert_obj.domain_name, - comparing_cert_type=cert_obj.cert_type, - comparing_flavor_id=cert_obj.flavor_id, - comparing_project_id=project_id): - raise ValueError('Certificate already exists ' - 'for {0} '.format(cert_obj.domain_name)) - - args = { - 'project_id': project_id, - 'flavor_id': cert_obj.flavor_id, - 'cert_type': cert_obj.cert_type, - 'domain_name': cert_obj.domain_name, - # when create the cert, cert domain has not been assigned yet - # In future we can tweak the logic to assign cert_domain - # 'cert_domain': '', - 'cert_details': cert_obj.cert_details - } - stmt = query.SimpleStatement( - CQL_CREATE_CERT, - consistency_level=self._driver.consistency_level) - self.session.execute(stmt, args) - def get_provider_details(self, project_id, service_id): """get_provider_details. @@ -1108,6 +869,7 @@ class ServicesController(base.ServicesController): """get_provider_details_by_domain_name. :param domain_name + :param project_id :returns Provider details """ @@ -1119,8 +881,8 @@ class ServicesController(base.ServicesController): stmt = query.SimpleStatement( CQL_SEARCH_BY_DOMAIN, consistency_level=self._driver.consistency_level) - resultset = self.session.execute(stmt, args) - complete_results = list(resultset) + result_set = self.session.execute(stmt, args) + complete_results = list(result_set) # If there is not service with this domain # return None details = None @@ -1131,8 +893,8 @@ class ServicesController(base.ServicesController): "present under " "project_id: {1}".format(domain_name, project_id)) - service = r.get('service_id') - details = self.get(proj_id, service) + service_id = r.get('service_id') + details = self.get_service(proj_id, service_id) return details def update_provider_details(self, project_id, service_id, @@ -1212,45 +974,6 @@ class ServicesController(base.ServicesController): consistency_level=self._driver.consistency_level) self.session.execute(stmt, provider_url_args) - def update_cert_info(self, domain_name, cert_type, flavor_id, - cert_details): - """update_cert_info. - - :param domain_name - :param cert_type - :param flavor_id - :param cert_info - """ - - args = { - 'domain_name': domain_name, - 'cert_type': cert_type, - 'flavor_id': flavor_id, - 'cert_details': cert_details - } - stmt = query.SimpleStatement( - CQL_UPDATE_CERT_DETAILS, - consistency_level=self._driver.consistency_level) - self.session.execute(stmt, args) - - try: - provider_status = json.loads(cert_details.values()[0]) - cert_status = provider_status['extra_info']['status'] - except (IndexError, IndexError, ValueError) as e: - LOG.error("Certificate details " - "in inconsistent " - "state: {0}".format(cert_details)) - LOG.error(e) - else: - cert_args = { - 'domain_name': domain_name, - 'status': cert_status - } - stmt = query.SimpleStatement( - CQL_INSERT_CERT_STATUS, - consistency_level=self._driver.consistency_level) - self.session.execute(stmt, cert_args) - @staticmethod def format_result(result): """format_result. diff --git a/poppy/storage/mockdb/certificates.py b/poppy/storage/mockdb/certificates.py new file mode 100644 index 00000000..0f5ffde5 --- /dev/null +++ b/poppy/storage/mockdb/certificates.py @@ -0,0 +1,77 @@ +# Copyright (c) 2016 Rackspace, Inc. +# +# 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 + +from poppy.model import ssl_certificate +from poppy.storage import base + + +class CertificatesController(base.CertificatesController): + + def __init__(self, driver): + super(CertificatesController, self).__init__(driver) + + self.certs = {} + + def create_certificate(self, project_id, cert_obj): + key = (cert_obj.flavor_id, cert_obj.domain_name, cert_obj.cert_type) + if key not in self.certs: + self.certs[key] = cert_obj + else: + raise ValueError + + def delete_certificate(self, project_id, domain_name, cert_type): + if "non_exist" in domain_name: + raise ValueError("No certs on this domain") + + def update_certificate(self, domain_name, cert_type, flavor_id, + cert_details): + key = (flavor_id, domain_name, cert_type) + if key in self.certs: + self.certs[key].cert_details = cert_details + + def get_certs_by_domain(self, domain_name, project_id=None, + flavor_id=None, + cert_type=None, + status=u'create_in_progress'): + certs = [] + for cert in self.certs: + if domain_name in cert: + certs.append(self.certs[cert]) + if project_id: + if flavor_id is not None and cert_type is not None: + return ssl_certificate.SSLCertificate( + "premium", + "blog.testabcd.com", + "san", + project_id=project_id, + cert_details={ + 'Akamai': { + u'cert_domain': u'secure2.san1.test_123.com', + u'extra_info': { + u'action': u'Waiting for customer domain ' + 'validation for blog.testabc.com', + u'akamai_spsId': str(random.randint(1, 100000) + ), + u'create_at': u'2015-09-29 16:09:12.429147', + u'san cert': u'secure2.san1.test_123.com', + u'status': status} + } + } + ) + return [cert for cert in certs if cert.project_id == project_id] + else: + return certs diff --git a/poppy/storage/mockdb/controllers.py b/poppy/storage/mockdb/controllers.py index fbfeab97..2ae1c100 100644 --- a/poppy/storage/mockdb/controllers.py +++ b/poppy/storage/mockdb/controllers.py @@ -23,8 +23,10 @@ Field Mappings: updated and documented in each controller class. """ +from poppy.storage.mockdb import certificates from poppy.storage.mockdb import flavors from poppy.storage.mockdb import services -ServicesController = services.ServicesController +CertificatesController = certificates.CertificatesController FlavorsController = flavors.FlavorsController +ServicesController = services.ServicesController diff --git a/poppy/storage/mockdb/driver.py b/poppy/storage/mockdb/driver.py index fd8abc3e..c14741af 100644 --- a/poppy/storage/mockdb/driver.py +++ b/poppy/storage/mockdb/driver.py @@ -59,13 +59,17 @@ class MockDBStorageDriver(base.Driver): return _connection() @property - def services_controller(self): - return controllers.ServicesController(self) + def certificates_controller(self): + return controllers.CertificatesController(self) @property def flavors_controller(self): return controllers.FlavorsController(self) + @property + def services_controller(self): + return controllers.ServicesController(self) + @property def database(self): return self.connection diff --git a/poppy/storage/mockdb/services.py b/poppy/storage/mockdb/services.py index b42270db..e294fb91 100644 --- a/poppy/storage/mockdb/services.py +++ b/poppy/storage/mockdb/services.py @@ -14,7 +14,6 @@ # limitations under the License. import json -import random from poppy.model.helpers import domain from poppy.model.helpers import origin @@ -22,28 +21,32 @@ from poppy.model.helpers import provider_details from poppy.model.helpers import restriction from poppy.model.helpers import rule from poppy.model import service -from poppy.model import ssl_certificate from poppy.storage import base +created_services = {} +created_service_ids = [] +claimed_domains = [] +project_id_service_limit = {} +service_count_per_project_id = {} + class ServicesController(base.ServicesController): def __init__(self, driver): super(ServicesController, self).__init__(driver) - self.created_service_ids = [] - self.created_services = {} - self.claimed_domains = [] - self.projectid_service_limit = {} + self.created_service_ids = created_service_ids + self.created_services = created_services + self.claimed_domains = claimed_domains + self.project_id_service_limit = project_id_service_limit self.default_max_service_limit = 20 - self.service_count_per_project_id = {} - self.certs = {} + self.service_count_per_project_id = service_count_per_project_id @property def session(self): return self._driver.database - def list(self, project_id, marker=None, limit=None): + def get_services(self, project_id, marker=None, limit=None): services = [] for service_id in self.created_services: services.append(self.created_services[service_id]) @@ -55,7 +58,7 @@ class ServicesController(base.ServicesController): return services_result - def get(self, project_id, service_id): + def get_service(self, project_id, service_id): # get the requested service from storage if service_id not in self.created_service_ids: raise ValueError("service {0} does not exist".format(service_id)) @@ -65,7 +68,7 @@ class ServicesController(base.ServicesController): service_result._status = 'deployed' return service_result - def create(self, project_id, service_obj): + def create_service(self, project_id, service_obj): if service_obj.service_id in self.created_service_ids: raise ValueError("Service %s already exists." % service_obj.service_id) @@ -80,11 +83,11 @@ class ServicesController(base.ServicesController): self.service_count_per_project_id[project_id] = 1 def set_service_limit(self, project_id, project_limit): - self.projectid_service_limit[project_id] = project_limit + self.project_id_service_limit[project_id] = project_limit def get_service_limit(self, project_id): try: - return self.projectid_service_limit[project_id] + return self.project_id_service_limit[project_id] except KeyError: return self.default_max_service_limit @@ -106,7 +109,7 @@ class ServicesController(base.ServicesController): return complete_results - def update(self, project_id, service_id, service_json): + def update_service(self, project_id, service_id, service_json): # update configuration in storage if service_json.service_id in self.created_service_ids \ and service_json.service_id == service_id: @@ -132,14 +135,14 @@ class ServicesController(base.ServicesController): :returns service_obj """ - service_obj = self.get(project_id, service_id) + service_obj = self.get_service(project_id, service_id) service_obj.operator_status = state - self.update(project_id, service_id, service_obj) + self.update_service(project_id, service_id, service_obj) - return self.get(project_id, service_id) + return self.get_service(project_id, service_id) - def delete(self, project_id, service_id): - if (service_id in self.created_service_ids): + def delete_service(self, project_id, service_id): + if service_id in self.created_service_ids: self.created_service_ids.remove(service_id) try: self.service_count_per_project_id[project_id] -= 1 @@ -179,12 +182,6 @@ class ServicesController(base.ServicesController): def domain_exists_elsewhere(self, domain_name, service_id): return domain_name in self.claimed_domains - def update_cert_info(self, domain_name, cert_type, flavor_id, - cert_details): - key = (flavor_id, domain_name, cert_type) - if key in self.certs: - self.certs[key].cert_details = cert_details - def get_service_details_by_domain_name(self, domain_name, project_id=None): for service_id in self.created_services: @@ -195,48 +192,6 @@ class ServicesController(base.ServicesController): service_result._status = 'deployed' return service_result - def create_cert(self, project_id, cert_obj): - key = (cert_obj.flavor_id, cert_obj.domain_name, cert_obj.cert_type) - if key not in self.certs: - self.certs[key] = cert_obj - else: - raise ValueError - - def get_certs_by_domain(self, domain_name, project_id=None, flavor_id=None, - cert_type=None, status=u'create_in_progress'): - certs = [] - for cert in self.certs: - if domain_name in cert: - certs.append(self.certs[cert]) - if project_id: - if flavor_id is not None and cert_type is not None: - return ssl_certificate.SSLCertificate( - "premium", - "blog.testabcd.com", - "san", - project_id=project_id, - cert_details={ - 'Akamai': { - u'cert_domain': u'secure2.san1.test_123.com', - u'extra_info': { - u'action': u'Waiting for customer domain ' - 'validation for blog.testabc.com', - u'akamai_spsId': str(random.randint(1, 100000) - ), - u'create_at': u'2015-09-29 16:09:12.429147', - u'san cert': u'secure2.san1.test_123.com', - u'status': status} - } - } - ) - return [cert for cert in certs if cert.project_id == project_id] - else: - return certs - - def delete_cert(self, project_id, domain_name, cert_type): - if "non_exist" in domain_name: - raise ValueError("No certs on this domain") - @staticmethod def format_result(result): service_id = result.get('service_id') diff --git a/poppy/transport/pecan/controllers/v1/admin.py b/poppy/transport/pecan/controllers/v1/admin.py index c4f4e1f5..79c34fe9 100644 --- a/poppy/transport/pecan/controllers/v1/admin.py +++ b/poppy/transport/pecan/controllers/v1/admin.py @@ -425,16 +425,17 @@ class AdminCertController(base.Controller, hooks.HookController): stoplight_helpers.pecan_getter) ) def get(self): - services_controller = self._driver.manager.services_controller + ssl_certificate_controller = ( + self._driver.manager.ssl_certificate_controller + ) call_args = getattr(pecan.request.context, "call_args") status = call_args.pop('status') - cert_domains = services_controller.get_certs_by_status( + cert_domains = ssl_certificate_controller.get_certs_by_status( status) - return pecan.Response(json_body=cert_domains, - status=200) + return pecan.Response(json_body=cert_domains, status=200) class AdminServiceController(base.Controller, hooks.HookController): diff --git a/poppy/transport/pecan/controllers/v1/services.py b/poppy/transport/pecan/controllers/v1/services.py index 3cd40f87..2ebf7299 100644 --- a/poppy/transport/pecan/controllers/v1/services.py +++ b/poppy/transport/pecan/controllers/v1/services.py @@ -173,7 +173,7 @@ class ServicesController(base.Controller, hooks.HookController): pecan.abort(400, detail="Marker must be a valid UUID") services_controller = self._driver.manager.services_controller - service_resultset = services_controller.list( + service_resultset = services_controller.get_services( self.project_id, marker, limit) results = [ resp_service_model.Model(s, self) @@ -202,7 +202,7 @@ class ServicesController(base.Controller, hooks.HookController): def get_one(self, service_id): services_controller = self._driver.manager.services_controller try: - service_obj = services_controller.get( + service_obj = services_controller.get_service( self.project_id, service_id) except ValueError: pecan.abort(404, detail='service %s could not be found' % @@ -222,9 +222,11 @@ class ServicesController(base.Controller, hooks.HookController): service_json_dict = json.loads(pecan.request.body.decode('utf-8')) service_id = None try: - service_obj = services_controller.create(self.project_id, - self.auth_token, - service_json_dict) + service_obj = services_controller.create_service( + self.project_id, + self.auth_token, + service_json_dict + ) service_id = service_obj.service_id except errors.SharedShardsExhausted as e: # domain - shared domains exhausted @@ -253,7 +255,7 @@ class ServicesController(base.Controller, hooks.HookController): services_controller = self._driver.manager.services_controller try: - services_controller.delete(self.project_id, service_id) + services_controller.delete_service(self.project_id, service_id) except LookupError as e: pecan.abort(404, detail=str(e)) except ValueError as e: @@ -277,7 +279,7 @@ class ServicesController(base.Controller, hooks.HookController): services_controller = self._driver.manager.services_controller try: - services_controller.update( + services_controller.update_service( self.project_id, service_id, self.auth_token, service_updates) except exceptions.ValidationFailed as e: pecan.abort(400, detail=u'{0}'.format(e)) diff --git a/tests/functional/transport/pecan/base.py b/tests/functional/transport/pecan/base.py index 9e5f4ea0..7bb451fc 100644 --- a/tests/functional/transport/pecan/base.py +++ b/tests/functional/transport/pecan/base.py @@ -20,6 +20,7 @@ from oslo_config import cfg import webtest from poppy import bootstrap +from poppy.storage.mockdb import services from tests.functional import base @@ -34,24 +35,33 @@ class BaseFunctionalTest(base.TestCase): )))) conf_path = os.path.join(tests_path, 'etc', 'default_functional.conf') cfg.CONF(args=[], default_config_files=[conf_path]) - b_obj = bootstrap.Bootstrap(cfg.CONF) + self.b_obj = bootstrap.Bootstrap(cfg.CONF) # mock the persistence part for taskflow distributed_task mock_persistence = mock.Mock() mock_persistence.__enter__ = mock.Mock() mock_persistence.__exit__ = mock.Mock() - b_obj.distributed_task.persistence = mock.Mock() - b_obj.distributed_task.persistence.return_value = mock_persistence - b_obj.distributed_task.job_board = mock.Mock() - b_obj.distributed_task.job_board.return_value = ( + self.b_obj.distributed_task.persistence = mock.Mock() + self.b_obj.distributed_task.persistence.return_value = mock_persistence + self.b_obj.distributed_task.job_board = mock.Mock() + self.b_obj.distributed_task.job_board.return_value = ( mock_persistence.copy()) - b_obj.distributed_task.is_alive = mock.Mock(return_value=True) + self.b_obj.distributed_task.is_alive = mock.Mock(return_value=True) # Note(tonytan4ever):Need this hack to preserve mockdb storage # controller's service cache - b_obj.manager.ssl_certificate_controller.storage_controller = ( - b_obj.manager.services_controller.storage_controller - ) - poppy_wsgi = b_obj.transport.app + # b_obj.manager.ssl_certificate_controller.storage_controller = ( + # b_obj.manager.services_controller.storage_controller + # ) + poppy_wsgi = self.b_obj.transport.app self.app = webtest.app.TestApp(poppy_wsgi) + def tearDown(self): + super(BaseFunctionalTest, self).tearDown() + + services.created_services = {} + services.created_service_ids = [] + services.claimed_domains = [] + services.project_id_service_limit = {} + services.service_count_per_project_id = {} + FunctionalTest = BaseFunctionalTest diff --git a/tests/functional/transport/pecan/controllers/test_get_ssl_certficate_by_status.py b/tests/functional/transport/pecan/controllers/test_get_ssl_certficate_by_status.py index fc8b7faa..c779f157 100644 --- a/tests/functional/transport/pecan/controllers/test_get_ssl_certficate_by_status.py +++ b/tests/functional/transport/pecan/controllers/test_get_ssl_certficate_by_status.py @@ -21,7 +21,8 @@ from hypothesis import strategies import mock import six -from poppy.manager.default.services import DefaultServicesController +from poppy.manager.default.ssl_certificate import \ + DefaultSSLCertificateController from tests.functional.transport.pecan import base @@ -56,7 +57,7 @@ class SSLCertificatebyStatusTest(base.FunctionalTest): @ddt.data(u'create_in_progress', u'deployed', u'failed', u'cancelled') def test_get_service_status_valid_queryparam(self, status): # valid status - with mock.patch.object(DefaultServicesController, + with mock.patch.object(DefaultSSLCertificateController, 'get_certs_by_status'): response = self.app.get('/v1.0/admin/certificates' '?status={0}'.format(status), diff --git a/tests/functional/transport/pecan/controllers/test_retry_list.py b/tests/functional/transport/pecan/controllers/test_retry_list.py index c99548b8..5bdcd27f 100644 --- a/tests/functional/transport/pecan/controllers/test_retry_list.py +++ b/tests/functional/transport/pecan/controllers/test_retry_list.py @@ -20,7 +20,7 @@ import uuid import ddt import mock -from poppy.storage.mockdb import services +from poppy.storage.mockdb import certificates from tests.functional.transport.pecan import base @@ -100,10 +100,10 @@ class TestRetryList(base.FunctionalTest): def test_put_retry_list_negative_with_deployed_domain(self): # A cert already in deployed status will cause 400. - with mock.patch('poppy.storage.mockdb.services.ServicesController.' - 'get_certs_by_domain', + with mock.patch('poppy.storage.mockdb.certificates.' + 'CertificatesController.get_certs_by_domain', new=functools. - partial(services.ServicesController. + partial(certificates.CertificatesController. get_certs_by_domain, status='deployed')): self.service_name = str(uuid.uuid1()) @@ -228,7 +228,7 @@ class TestRetryList(base.FunctionalTest): 'X-Project-ID': self.project_id}) self.assertEqual(202, response.status_code) - # This time the service is present, so the request goes thru + # This time the service is present, so the request goes through put_data = [ { "domain_name": "test-san1.cnamecdn.com", @@ -249,7 +249,7 @@ class TestRetryList(base.FunctionalTest): headers={ 'Content-Type': 'application/json', 'X-Project-ID': self.project_id}, - expect_errors=True) + ) self.assertEqual(200, response.status_code) def test_post_retry_list(self): diff --git a/tests/unit/distributed_task/taskflow/test_flows.py b/tests/unit/distributed_task/taskflow/test_flows.py index 4fe027c3..1aedb44f 100644 --- a/tests/unit/distributed_task/taskflow/test_flows.py +++ b/tests/unit/distributed_task/taskflow/test_flows.py @@ -72,7 +72,10 @@ class TestFlowRuns(base.TestCase): memoized_controllers.task_controllers('poppy', 'storage') service_controller, dns_controller = \ memoized_controllers.task_controllers('poppy', 'dns') - return service_controller, storage_controller, dns_controller + service_controller, ssl_cert_controller = \ + memoized_controllers.task_controllers('poppy', 'ssl_certificate') + return service_controller, storage_controller, dns_controller, \ + ssl_cert_controller def dns_exceptions_and_succeed(self): # NOTE(TheSriram): create a chain of mocked return values, @@ -193,12 +196,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_create_flow(service_controller, @@ -235,12 +243,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_update_flow(service_controller, storage_controller, @@ -270,12 +283,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_delete_flow(service_controller, storage_controller, @@ -306,12 +324,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_purge_flow(service_controller, storage_controller, @@ -343,12 +366,17 @@ class TestFlowRuns(base.TestCase): disable_kwargs = enable_kwargs.copy() disable_kwargs['state'] = 'disable' - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_service_state_flow(service_controller, @@ -374,12 +402,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_create_flow(service_controller, storage_controller, @@ -424,12 +457,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_update_flow(service_controller, storage_controller, @@ -469,12 +507,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_delete_flow(service_controller, storage_controller, @@ -513,12 +556,17 @@ class TestFlowRuns(base.TestCase): disable_kwargs = enable_kwargs.copy() disable_kwargs['state'] = 'disable' - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_service_state_flow(service_controller, @@ -574,12 +622,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_update_flow(service_controller, storage_controller, @@ -621,12 +674,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_update_flow(service_controller, storage_controller, @@ -654,12 +712,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_create_flow(service_controller, storage_controller, @@ -687,12 +750,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_create_flow(service_controller, storage_controller, @@ -730,12 +798,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_delete_flow(service_controller, storage_controller, @@ -770,12 +843,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_delete_flow(service_controller, storage_controller, @@ -812,12 +890,17 @@ class TestFlowRuns(base.TestCase): disable_kwargs = enable_kwargs.copy() disable_kwargs['state'] = 'disable' - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_service_state_flow(service_controller, @@ -860,12 +943,17 @@ class TestFlowRuns(base.TestCase): disable_kwargs = enable_kwargs.copy() disable_kwargs['state'] = 'disable' - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_service_state_flow(service_controller, storage_controller, @@ -902,12 +990,17 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_create_ssl_certificate_flow(service_controller, @@ -934,12 +1027,17 @@ class TestFlowRuns(base.TestCase): 'cert_obj_json': json.dumps(cert_obj_json.to_dict()), } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): self.patch_recreate_ssl_certificate_flow(service_controller, @@ -962,16 +1060,25 @@ class TestFlowRuns(base.TestCase): 'context_dict': context_utils.RequestContext().to_dict() } - service_controller, storage_controller, dns_controller = \ - self.all_controllers() + ( + service_controller, + storage_controller, + dns_controller, + ssl_cert_controller + ) = self.all_controllers() with MonkeyPatchControllers(service_controller, dns_controller, storage_controller, + ssl_cert_controller, memoized_controllers.task_controllers): - self.patch_create_ssl_certificate_flow(service_controller, - storage_controller, - dns_controller) - engines.run(delete_ssl_certificate.delete_ssl_certificate(), - store=kwargs) + self.patch_create_ssl_certificate_flow( + service_controller, + storage_controller, + dns_controller + ) + engines.run( + delete_ssl_certificate.delete_ssl_certificate(), + store=kwargs + ) diff --git a/tests/unit/manager/default/test_services.py b/tests/unit/manager/default/test_services.py index 6a450af1..5eabcf7d 100644 --- a/tests/unit/manager/default/test_services.py +++ b/tests/unit/manager/default/test_services.py @@ -76,10 +76,11 @@ class MonkeyPatchControllers(object): def __init__(self, service_controller, dns_controller, - storage_controller, func): + storage_controller, ssl_cert_controller, func): self.service_controller = service_controller self.dns_controller = dns_controller self.storage_controller = storage_controller + self.ssl_cert_controller = ssl_cert_controller self.func = func def __enter__(self): @@ -89,6 +90,8 @@ class MonkeyPatchControllers(object): return self.service_controller, self.storage_controller if controller == 'dns': return self.service_controller, self.dns_controller + if controller == 'ssl_certificate': + return self.service_controller, self.ssl_cert_controller else: return self.service_controller @@ -437,14 +440,17 @@ class DefaultManagerServiceTests(base.TestCase): self.sc.storage_controller.get_service_count = mock.Mock( return_value=1) - service_obj = self.sc.create(self.project_id, - self.auth_token, - self.service_json) + service_obj = self.sc.create_service( + self.project_id, + self.auth_token, + self.service_json + ) # ensure the manager calls the storage driver with the appropriate data - self.sc.storage_controller.create.assert_called_once_with( + self.sc.storage_controller.create_service.assert_called_once_with( self.project_id, - service_obj) + service_obj + ) @ddt.file_data('data_provider_details.json') def test_create_service_worker(self, provider_details_json): @@ -518,6 +524,7 @@ class DefaultManagerServiceTests(base.TestCase): with MonkeyPatchControllers(self.sc, self.sc.dns_controller, self.sc.storage_controller, + self.sc.ssl_certificate_storage, memoized_controllers.task_controllers): self.mock_create_service(provider_details_json) @@ -621,6 +628,7 @@ class DefaultManagerServiceTests(base.TestCase): with MonkeyPatchControllers(self.sc, self.sc.dns_controller, self.sc.storage_controller, + self.sc.ssl_certificate_storage, memoized_controllers.task_controllers): # NOTE(TheSriram): Successful update @@ -675,7 +683,7 @@ class DefaultManagerServiceTests(base.TestCase): service_obj = service.load_from_json(self.service_json) service_obj.status = u'deployed' - self.sc.storage_controller.get.return_value = service_obj + self.sc.storage_controller.get_service.return_value = service_obj service_updates = json.dumps([ { "op": "replace", @@ -684,13 +692,15 @@ class DefaultManagerServiceTests(base.TestCase): } ]) - self.sc.update(self.project_id, - self.service_id, - self.auth_token, - service_updates) + self.sc.update_service( + self.project_id, + self.service_id, + self.auth_token, + service_updates + ) # ensure the manager calls the storage driver with the appropriate data - self.sc.storage_controller.update.assert_called_once() + self.sc.storage_controller.update_service.assert_called_once() @ddt.file_data('data_provider_details.json') def test_delete(self, provider_details_json): @@ -710,16 +720,21 @@ class DefaultManagerServiceTests(base.TestCase): self.service_obj.provider_details = self.provider_details sc = self.sc.storage_controller - sc.get.return_value = self.service_obj + sc.get_service.return_value = self.service_obj - self.sc.delete(self.project_id, self.service_id) + self.sc.delete_service(self.project_id, self.service_id) # ensure the manager calls the storage driver with the appropriate data - sc.get.assert_called_once_with(self.project_id, self.service_id) - sc.update.assert_called_once_with(self.project_id, - self.service_id, - self.service_obj) + sc.get_service.assert_called_once_with( + self.project_id, + self.service_id + ) + sc.update_service.assert_called_once_with( + self.project_id, + self.service_id, + self.service_obj + ) # break into 2 lines. sc = self.sc.storage_controller @@ -791,6 +806,7 @@ class DefaultManagerServiceTests(base.TestCase): with MonkeyPatchControllers(self.sc, self.sc.dns_controller, self.sc.storage_controller, + self.sc.ssl_certificate_storage, memoized_controllers.task_controllers): self.mock_delete_service() @@ -854,6 +870,7 @@ class DefaultManagerServiceTests(base.TestCase): with MonkeyPatchControllers(self.sc, self.sc.dns_controller, self.sc.storage_controller, + self.sc.ssl_certificate_storage, memoized_controllers.task_controllers): self.mock_delete_service() @@ -878,7 +895,7 @@ class DefaultManagerServiceTests(base.TestCase): ) self.service_obj.provider_details = self.provider_details - self.sc.storage_controller.get.return_value = ( + self.sc.storage_controller.get_service.return_value = ( self.service_obj ) @@ -952,6 +969,7 @@ class DefaultManagerServiceTests(base.TestCase): with MonkeyPatchControllers(self.sc, self.sc.dns_controller, self.sc.storage_controller, + self.sc.ssl_certificate_storage, memoized_controllers.task_controllers): self.mock_purge_service(hard=True) self.mock_purge_service(hard=False) @@ -1005,6 +1023,7 @@ class DefaultManagerServiceTests(base.TestCase): with MonkeyPatchControllers(self.sc, self.sc.dns_controller, self.sc.storage_controller, + self.sc.ssl_certificate_storage, memoized_controllers.task_controllers): self.mock_purge_service(hard=True) self.mock_purge_service(hard=False) @@ -1051,15 +1070,19 @@ class DefaultManagerServiceTests(base.TestCase): return_value=list() ) - self.mock_storage.services_controller.get.return_value = ( + self.mock_storage.services_controller.get_service.return_value = ( mock_service_obj ) self.sc.set_service_provider_details( "project_id", "service_id", "auth_token", "deployed" ) - self.assertTrue(self.mock_storage.services_controller.get.called) - self.assertTrue(self.mock_storage.services_controller.update.called) + self.assertTrue( + self.mock_storage.services_controller.get_service.called + ) + self.assertTrue( + self.mock_storage.services_controller.update_service.called + ) self.assertTrue( self.mock_distributed_task.services_controller.submit_task.called ) diff --git a/tests/unit/provider/akamai/background_jobs/akamai_mocks.py b/tests/unit/provider/akamai/background_jobs/akamai_mocks.py index 508b4bb2..3d74044b 100644 --- a/tests/unit/provider/akamai/background_jobs/akamai_mocks.py +++ b/tests/unit/provider/akamai/background_jobs/akamai_mocks.py @@ -87,6 +87,12 @@ class MockManager(mock.Mock): def services_controller(self): return self.get_services_controller() + @staticmethod + def get_ssl_certificate_controller(): + sc = mock.Mock() + sc.ssl_certificate_controller = MockStorageController() + return sc + @staticmethod def get_services_controller(): sc = mock.Mock() diff --git a/tests/unit/provider/akamai/background_jobs/test_flows.py b/tests/unit/provider/akamai/background_jobs/test_flows.py index aa7c9083..83fc933c 100644 --- a/tests/unit/provider/akamai/background_jobs/test_flows.py +++ b/tests/unit/provider/akamai/background_jobs/test_flows.py @@ -43,12 +43,18 @@ class TestAkamaiBJFlowRuns(base.TestCase): akamai_mocks.MockManager.get_services_controller(). storage_controller ) + if args[1] == 'ssl_certificate': + return ( + akamai_mocks.MockManager.get_services_controller(), + akamai_mocks.MockManager. + get_ssl_certificate_controller(). + ssl_certificate_controller + ) mock_task_controllers = mock.Mock() mock_task_controllers.task_controllers.side_effect = ( task_controllers_side_effect ) memo_controllers_patcher = mock.patch( - # 'poppy.distributed_task.utils.memoized_controllers', 'poppy.provider.akamai.background_jobs.' 'check_cert_status_and_update.' 'check_cert_status_and_update_tasks.memoized_controllers', diff --git a/tests/unit/storage/cassandra/test_certificates.py b/tests/unit/storage/cassandra/test_certificates.py new file mode 100644 index 00000000..c7fb4cb4 --- /dev/null +++ b/tests/unit/storage/cassandra/test_certificates.py @@ -0,0 +1,106 @@ +# Copyright (c) 2014 Rackspace, Inc. +# +# 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 uuid + +import cassandra +import ddt +import mock +from oslo_config import cfg + +from poppy.model import ssl_certificate +from poppy.storage.cassandra import certificates +from poppy.storage.cassandra import driver +from tests.unit import base + + +@ddt.ddt +class CassandraStorageServiceTests(base.TestCase): + + def setUp(self): + super(CassandraStorageServiceTests, self).setUp() + + # mock arguments to use + self.project_id = '123456' + self.service_id = uuid.uuid4() + self.service_name = 'mocksite' + + # create mocked config and driver + conf = cfg.ConfigOpts() + conf.register_opt( + cfg.StrOpt( + 'datacenter', + default='', + help='datacenter where the C* cluster hosted')) + conf.register_opts(driver.CASSANDRA_OPTIONS, + group=driver.CASSANDRA_GROUP) + cassandra_driver = driver.CassandraStorageDriver(conf) + + migrations_patcher = mock.patch( + 'cdeploy.migrator.Migrator' + ) + migrations_patcher.start() + self.addCleanup(migrations_patcher.stop) + + # stubbed cassandra driver + self.cc = certificates.CertificatesController(cassandra_driver) + + @ddt.file_data('data_get_certs_by_domain.json') + @mock.patch.object(certificates.CertificatesController, 'session') + @mock.patch.object(cassandra.cluster.Session, 'execute') + def test_get_certs_by_domain(self, cert_details_json, + mock_session, mock_execute): + # mock the response from cassandra + mock_execute.execute.return_value = cert_details_json[0] + actual_response = self.cc.get_certs_by_domain( + domain_name="www.mydomain.com" + ) + self.assertEqual(len(actual_response), 2) + self.assertTrue(all([isinstance(ssl_cert, + ssl_certificate.SSLCertificate) + for ssl_cert in actual_response])) + mock_execute.execute.return_value = cert_details_json[1] + actual_response = self.cc.get_certs_by_domain( + domain_name="www.example.com", + flavor_id="flavor1") + self.assertEqual(len(actual_response), 2) + self.assertTrue(all([isinstance(ssl_cert, + ssl_certificate.SSLCertificate) + for ssl_cert in actual_response])) + mock_execute.execute.return_value = cert_details_json[2] + actual_response = self.cc.get_certs_by_domain( + domain_name="www.mydomain.com", + flavor_id="flavor1", + cert_type="san") + self.assertTrue(isinstance(actual_response, + ssl_certificate.SSLCertificate)) + + @mock.patch.object(certificates.CertificatesController, 'session') + @mock.patch.object(cassandra.cluster.Session, 'execute') + def test_get_certs_by_status(self, mock_session, mock_execute): + # mock the response from cassandra + mock_execute.execute.return_value = \ + [{"domain_name": "www.example.com"}] + actual_response = self.cc.get_certs_by_status( + status="deployed") + self.assertEqual(actual_response, + [{"domain_name": "www.example.com"}]) + + mock_execute.execute.return_value = \ + [{"domain_name": "www.example1.com"}] + actual_response = self.cc.get_certs_by_status( + status="failed") + self.assertEqual(actual_response, + [{"domain_name": "www.example1.com"}]) diff --git a/tests/unit/storage/cassandra/test_services.py b/tests/unit/storage/cassandra/test_services.py index 764c46ab..16111233 100644 --- a/tests/unit/storage/cassandra/test_services.py +++ b/tests/unit/storage/cassandra/test_services.py @@ -26,7 +26,6 @@ import mock from oslo_config import cfg from poppy.model.helpers import provider_details -from poppy.model import ssl_certificate from poppy.storage.cassandra import driver from poppy.storage.cassandra import services from poppy.transport.pecan.models.request import service as req_service @@ -73,7 +72,7 @@ class CassandraStorageServiceTests(base.TestCase): value[0]['service_id'] = self.service_id mock_execute.execute.return_value = value - actual_response = self.sc.get(self.project_id, self.service_id) + actual_response = self.sc.get_service(self.project_id, self.service_id) # TODO(amitgandhinz): assert the response # matches the expectation (using jsonschema) @@ -86,8 +85,12 @@ class CassandraStorageServiceTests(base.TestCase): # mock the response from cassandra mock_execute.execute.return_value = [] - self.assertRaises(ValueError, self.sc.get, - self.project_id, self.service_id) + self.assertRaises( + ValueError, + self.sc.get_service, + self.project_id, + self.service_id + ) @ddt.file_data('../data/data_create_service.json') @mock.patch.object(services.ServicesController, @@ -98,7 +101,7 @@ class CassandraStorageServiceTests(base.TestCase): def test_create_service(self, value, mock_check, mock_session, mock_execute): service_obj = req_service.load_from_json(value) - responses = self.sc.create(self.project_id, service_obj) + responses = self.sc.create_service(self.project_id, service_obj) # Expect the response to be None as there are no providers passed # into the driver to respond to this call @@ -117,9 +120,11 @@ class CassandraStorageServiceTests(base.TestCase): service_obj = req_service.load_from_json(value) self.sc.get = mock.Mock(return_value=service_obj) - self.assertRaises(ValueError, - self.sc.create, - self.project_id, service_obj) + self.assertRaises( + ValueError, + self.sc.create_service, + self.project_id, service_obj + ) @ddt.file_data('data_list_services.json') @mock.patch.object(services.ServicesController, 'session') @@ -130,7 +135,7 @@ class CassandraStorageServiceTests(base.TestCase): mock_execute.prepare.return_value = mock.Mock() mock_execute.execute.return_value = value - actual_response = self.sc.list(self.project_id, None, None) + actual_response = self.sc.get_services(self.project_id, None, None) # TODO(amitgandhinz): assert the response # matches the expectation (using jsonschema) @@ -142,7 +147,10 @@ class CassandraStorageServiceTests(base.TestCase): def test_delete_service(self, mock_session, mock_execute): # mock the response from cassandra mock_execute.execute.return_value = iter([{}]) - actual_response = self.sc.delete(self.project_id, self.service_id) + actual_response = self.sc.delete_service( + self.project_id, + self.service_id + ) # Expect the response to be None as there are no providers passed # into the driver to respond to this call @@ -173,9 +181,11 @@ class CassandraStorageServiceTests(base.TestCase): } mock_session.execute.return_value = iter([{}]) service_obj = req_service.load_from_json(service_json) - actual_response = self.sc.update(self.project_id, - self.service_id, - service_obj) + actual_response = self.sc.update_service( + self.project_id, + self.service_id, + service_obj + ) # Expect the response to be None as there are no # providers passed into the driver to respond to this call @@ -196,53 +206,6 @@ class CassandraStorageServiceTests(base.TestCase): self.assertTrue("CloudFront" in actual_response) self.assertTrue("Fastly" in actual_response) - @ddt.file_data('data_get_certs_by_domain.json') - @mock.patch.object(services.ServicesController, 'session') - @mock.patch.object(cassandra.cluster.Session, 'execute') - def test_get_certs_by_domain(self, cert_details_json, - mock_session, mock_execute): - # mock the response from cassandra - mock_execute.execute.return_value = cert_details_json[0] - actual_response = self.sc.get_certs_by_domain( - domain_name="www.mydomain.com") - self.assertEqual(len(actual_response), 2) - self.assertTrue(all([isinstance(ssl_cert, - ssl_certificate.SSLCertificate) - for ssl_cert in actual_response])) - mock_execute.execute.return_value = cert_details_json[1] - actual_response = self.sc.get_certs_by_domain( - domain_name="www.example.com", - flavor_id="flavor1") - self.assertEqual(len(actual_response), 2) - self.assertTrue(all([isinstance(ssl_cert, - ssl_certificate.SSLCertificate) - for ssl_cert in actual_response])) - mock_execute.execute.return_value = cert_details_json[2] - actual_response = self.sc.get_certs_by_domain( - domain_name="www.mydomain.com", - flavor_id="flavor1", - cert_type="san") - self.assertTrue(isinstance(actual_response, - ssl_certificate.SSLCertificate)) - - @mock.patch.object(services.ServicesController, 'session') - @mock.patch.object(cassandra.cluster.Session, 'execute') - def test_get_certs_by_status(self, mock_session, mock_execute): - # mock the response from cassandra - mock_execute.execute.return_value = \ - [{"domain_name": "www.example.com"}] - actual_response = self.sc.get_certs_by_status( - status="deployed") - self.assertEqual(actual_response, - [{"domain_name": "www.example.com"}]) - - mock_execute.execute.return_value = \ - [{"domain_name": "www.example1.com"}] - actual_response = self.sc.get_certs_by_status( - status="failed") - self.assertEqual(actual_response, - [{"domain_name": "www.example1.com"}]) - @ddt.file_data('data_provider_details.json') @mock.patch.object(services.ServicesController, 'session') @mock.patch.object(cassandra.cluster.Session, 'execute')