Refactor orders cleanup

This patch continues the refactor of cleanup logic.  It adds a
new `cleanup()` method to the order client that attempts to delete
all orders and all screts created by those orders on cleanup.

This patch also adds some scaffolding to the Container client that
will be fleshed out in a follow-up patch.

Change-Id: I78d875980637e82ddc3173aad2fc7ecf4941230c
diff --git a/barbican_tempest_plugin/services/key_manager/json/container_client.py b/barbican_tempest_plugin/services/key_manager/json/container_client.py
index 766cbf2..ef2ebf8 100644
--- a/barbican_tempest_plugin/services/key_manager/json/container_client.py
+++ b/barbican_tempest_plugin/services/key_manager/json/container_client.py
@@ -80,3 +80,6 @@
         )
         self.expected_success(204, response.status)
         return
+
+    def queue_for_cleanup(self, container_id):
+        raise NotImplementedError
diff --git a/barbican_tempest_plugin/services/key_manager/json/order_client.py b/barbican_tempest_plugin/services/key_manager/json/order_client.py
index c81ddd0..84d64ef 100644
--- a/barbican_tempest_plugin/services/key_manager/json/order_client.py
+++ b/barbican_tempest_plugin/services/key_manager/json/order_client.py
@@ -15,6 +15,7 @@
 from urllib import parse as urllib
 
 from tempest import config
+from tempest.lib import exceptions
 
 from barbican_tempest_plugin.services.key_manager.json import base
 
@@ -24,6 +25,18 @@
 
 class OrderClient(base.BarbicanTempestClient):
 
+    def __init__(self, *args, secret_client=None, container_client=None,
+                 **kwargs):
+        """Create a new order client
+
+        secret_client and container_client are optional and will be used
+        to queue the respective objects for cleanup when given.
+        """
+        super().__init__(*args, **kwargs)
+        self._order_ids = set()
+        self._secret_client = secret_client
+        self._container_client = container_client
+
     def list_orders(self, **kwargs):
         uri = "/v1/orders"
         if kwargs:
@@ -38,7 +51,9 @@
 
         response, body = self.post(uri, json.dumps(kwargs))
         self.expected_success(202, response.status)
-        return json.loads(body.decode("utf-8"))
+        resp = json.loads(body.decode("utf-8"))
+        self._order_ids.add(self.ref_to_uuid(resp['order_ref']))
+        return resp
 
     def get_order(self, order_id):
         uri = "v1/orders/%s" % order_id
@@ -48,8 +63,46 @@
         return json.loads(body.decode("utf-8"))
 
     def delete_order(self, order_id):
+        self._order_ids.discard(order_id)
         uri = "/v1/orders/%s" % order_id
 
+        self._queue_cleanup(order_id)
+
         response, _ = self.delete(uri)
         self.expected_success(204, response.status)
         return
+
+    def cleanup(self):
+        """Attempt to delete all orders created by this client
+
+        If this client was instantiated with secret and/or container
+        clients, then we try to queue for cleanup any objects generated
+        by the orders.
+        """
+        cleanup_ids = self._order_ids
+        self._order_ids = set()
+        for order_id in cleanup_ids:
+            self._queue_cleanup(order_id)
+            try:
+                self.delete_order(order_id)
+            except exceptions.NotFound:
+                continue
+
+    def _queue_cleanup(self, order_id):
+        try:
+            order = self.get_order(order_id)
+        except exceptions.NotFound:
+            pass
+        except exceptions.Forbidden:
+            pass
+        else:
+            if (self._secret_client is not None) and \
+                    (order.get('secret_ref') is not None):
+                self._secret_client.queue_for_cleanup(
+                    self.ref_to_uuid(order['secret_ref'])
+                )
+            if (self._container_client is not None) and \
+                    (order.get('container_ref') is not None):
+                self._container_client.queue_for_cleanup(
+                    self.ref_to_uuid(order['container_ref'])
+                )
diff --git a/barbican_tempest_plugin/tests/rbac/v1/base.py b/barbican_tempest_plugin/tests/rbac/v1/base.py
index dbe6932..c03adb2 100644
--- a/barbican_tempest_plugin/tests/rbac/v1/base.py
+++ b/barbican_tempest_plugin/tests/rbac/v1/base.py
@@ -127,24 +127,7 @@
     def setup_clients(cls):
         super().setup_clients()
 
-        # set up member clients
-        os = cls.os_project_member
-        cls.secret_client = os.secret_v1.SecretClient()
-        cls.secret_metadata_client = os.secret_v1.SecretMetadataClient(
-            service='key-manager'
-        )
-        cls.consumer_client = os.secret_v1.ConsumerClient(
-            service='key-manager'
-        )
-        cls.container_client = os.secret_v1.ContainerClient()
-        cls.order_client = os.secret_v1.OrderClient()
-        cls.quota_client = os.secret_v1.QuotaClient()
-        cls.secret_metadata_client = os.secret_v1.SecretMetadataClient(
-            service='key-manager'
-        )
-
         # setup clients for admin persona
-        # this client is used for any cleanup/setup etc. as needed
         adm = cls.os_project_admin
         cls.admin_secret_client = adm.secret_v1.SecretClient()
         cls.admin_secret_metadata_client = adm.secret_v1.SecretMetadataClient(
@@ -154,11 +137,29 @@
             service='key-manager'
         )
         cls.admin_container_client = adm.secret_v1.ContainerClient()
-        cls.admin_order_client = adm.secret_v1.OrderClient()
+        cls.admin_order_client = adm.secret_v1.OrderClient(
+            secret_client=cls.admin_secret_client,
+            container_client=cls.admin_container_client
+        )
         cls.admin_quota_client = adm.secret_v1.QuotaClient()
-        cls.admin_secret_metadata_client = adm.secret_v1.SecretMetadataClient(
+
+        # set clients for member persona
+        member = cls.os_project_member
+        cls.secret_client = member.secret_v1.SecretClient()
+        cls.secret_metadata_client = member.secret_v1.SecretMetadataClient(
             service='key-manager'
         )
+        cls.consumer_client = member.secret_v1.ConsumerClient(
+            service='key-manager'
+        )
+        cls.container_client = member.secret_v1.ContainerClient()
+        cls.order_client = member.secret_v1.OrderClient(
+            secret_client=cls.secret_client,
+            container_client=cls.container_client
+        )
+        cls.quota_client = member.secret_v1.QuotaClient()
+        # set up clients for member persona associated with a different
+        # project
         cls.other_client = cls.os_project_alt_member.secret_v1.SecretClient()
 
     @classmethod
@@ -173,17 +174,17 @@
             for container_uuid in list(cls.created_objects['container']):
                 cls.admin_container_client.delete_container(container_uuid)
                 cls.created_objects['container'].remove(container_uuid)
-            for order_uuid in list(cls.created_objects['order']):
-                cls.admin_order_client.delete_order(order_uuid)
-                cls.created_objects['order'].remove(order_uuid)
             for quota_uuid in list(cls.created_objects['quota']):
                 cls.admin_quota_client.delete_project_quota(quota_uuid)
                 cls.created_objects['quota'].remove(quota_uuid)
             for secret_uuid in list(cls.created_objects['secret']):
                 cls.admin_secret_client.delete_secret(secret_uuid)
                 cls.created_objects['secret'].remove(secret_uuid)
+
             for client in [cls.secret_client,
+                           cls.order_client,
                            cls.admin_secret_client,
+                           cls.admin_order_client,
                            cls.other_client]:
                 client.cleanup()
         finally:
@@ -193,13 +194,6 @@
     def add_cleanup(cls, resource, response):
         if resource == 'container':
             uuid = cls.ref_to_uuid(response['container_ref'])
-        if resource == 'order':
-            uuid = cls.ref_to_uuid(response.get('order_ref'))
-            order_metadata = cls.admin_order_client.get_order(uuid)
-            secret_ref = order_metadata.get('secret_ref')
-            if secret_ref:
-                cls.created_objects['secret'].add(cls.ref_to_uuid(secret_ref))
-            uuid = cls.ref_to_uuid(response['order_ref'])
         if resource == 'quota':
             uuid = cls.ref_to_uuid(response['quota_ref'])
         if resource == 'secret':
@@ -261,3 +255,25 @@
             kwargs['payload_content_type'] = 'text/plain'
         resp = self.other_client.create_secret(**kwargs)
         return self.other_client.ref_to_uuid(resp['secret_ref'])
+
+    def create_key_order(self, name=None):
+        """Create a symmetric key order for testing
+
+        The new order is created using the default
+        member persona.
+
+        :returns: the uuid for the new order
+        """
+        meta = {
+            'algorithm': 'AES',
+            'bit_length': 256,
+            'mode': 'CBC'
+        }
+        if name is not None:
+            meta['name'] = name
+        kwargs = {
+            'type': 'key',
+            'meta': meta
+        }
+        resp = self.order_client.create_order(**kwargs)
+        return self.order_client.ref_to_uuid(resp['order_ref'])
diff --git a/barbican_tempest_plugin/tests/rbac/v1/test_orders.py b/barbican_tempest_plugin/tests/rbac/v1/test_orders.py
index 36f9a26..5eadfa3 100644
--- a/barbican_tempest_plugin/tests/rbac/v1/test_orders.py
+++ b/barbican_tempest_plugin/tests/rbac/v1/test_orders.py
@@ -10,7 +10,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 import abc
-import time
 
 from tempest.lib import exceptions
 
@@ -68,58 +67,28 @@
         cls.client = cls.os_project_member.secret_v1.OrderClient()
 
     def test_list_orders(self):
-        self.do_request('create_order', cleanup='order',
-                        name='list_orders', type='key',
-                        meta={
-                            'name': 'list_orders_s',
-                            'algorithm': 'aes',
-                            'bit_length': 256,
-                            'mode': 'cbc',
-                        })
-        resp = self.do_request('list_orders')
+        _ = self.create_key_order('test_list_orders')
+        resp = self.client.list_orders()
         self.assertGreaterEqual(len(resp['orders']), 1)
 
     def test_create_order(self):
-        self.do_request('create_order', cleanup='order',
-                        name='create_order', type='key',
-                        meta={
-                            'name': 'create_orders_s',
-                            'algorithm': 'aes',
-                            'bit_length': 256,
-                            'mode': 'cbc',
-                        })
+        self.client.create_order(
+            name='create_order', type='key',
+            meta={
+                'name': 'create_orders_s',
+                'algorithm': 'aes',
+                'bit_length': 256,
+                'mode': 'cbc',
+            })
 
     def test_get_order(self):
-        resp = self.do_request('create_order', cleanup='order',
-                               name='get_order', type='key',
-                               meta={
-                                   'name': 'get_order_s',
-                                   'algorithm': 'aes',
-                                   'bit_length': 256,
-                                   'mode': 'cbc',
-                               })
-        order_id = self.ref_to_uuid(resp['order_ref'])
-        resp = self.do_request('get_order', order_id=order_id)
-        self.assertEqual(order_id, self.ref_to_uuid(resp['order_ref']))
+        order_id = self.create_key_order('test_get_order')
+        resp = self.client.get_order(order_id)
+        self.assertEqual(order_id, self.client.ref_to_uuid(resp['order_ref']))
 
     def test_delete_order(self):
-        resp = self.do_request('create_order',
-                               name='delete_order', type='key',
-                               meta={
-                                   'name': 'delete_order_s',
-                                   'algorithm': 'aes',
-                                   'bit_length': 256,
-                                   'mode': 'cbc',
-                               })
-        order_id = self.ref_to_uuid(resp['order_ref'])
-        while True:
-            time.sleep(1)
-            resp = self.do_request('get_order', order_id=order_id)
-            if 'ACTIVE' == resp['status']:
-                self.add_cleanup('secret', resp)
-                break
-
-        self.do_request('delete_order', order_id=order_id)
+        order_id = self.create_key_order('test_delete_order')
+        self.client.delete_order(order_id)
 
 
 class ProjectAdminTests(ProjectMemberTests):
@@ -138,49 +107,30 @@
         cls.client = cls.os_project_reader.secret_v1.OrderClient()
 
     def test_list_orders(self):
-        self.do_request('list_orders', expected_status=exceptions.Forbidden)
+        self.assertRaises(exceptions.Forbidden, self.client.list_orders)
 
     def test_create_order(self):
-        self.do_request('create_order',
-                        expected_status=exceptions.Forbidden,
-                        cleanup='order',
-                        name='create_order', type='key',
-                        meta={
-                            'name': 'create_orders_s',
-                            'algorithm': 'aes',
-                            'bit_length': 256,
-                            'mode': 'cbc',
-                        })
-
-    def test_get_order(self):
-        resp = self.do_request(
-            'create_order',
-            client=self.os_project_member.secret_v1.OrderClient(),
-            cleanup='order',
-            name='get_order', type='key',
+        self.assertRaises(
+            exceptions.Forbidden,
+            self.client.create_order,
+            name='test_create_order', type='key',
             meta={
-                'name': 'get_order_s',
-                'algorithm': 'aes',
-                'bit_length': 256,
-                'mode': 'cbc',
-            }
-        )
-        order_id = self.ref_to_uuid(resp['order_ref'])
-        self.do_request('get_order', expected_status=exceptions.Forbidden,
-                        order_id=order_id)
-
-    def test_delete_order(self):
-        resp = self.do_request(
-            'create_order',
-            client=self.os_project_member.secret_v1.OrderClient(),
-            cleanup='order',
-            name='delete_order', type='key',
-            meta={
-                'name': 'delete_order_s',
+                'name': 'create_orders_s',
                 'algorithm': 'aes',
                 'bit_length': 256,
                 'mode': 'cbc',
             })
-        order_id = self.ref_to_uuid(resp['order_ref'])
-        self.do_request('delete_order', expected_status=exceptions.Forbidden,
-                        order_id=order_id)
+
+    def test_get_order(self):
+        order_id = self.create_key_order('test_get_order')
+        self.assertRaises(
+            exceptions.Forbidden,
+            self.client.get_order,
+            order_id=order_id)
+
+    def test_delete_order(self):
+        order_id = self.create_key_order('test_delete_order')
+        self.assertRaises(
+            exceptions.Forbidden,
+            self.client.delete_order,
+            order_id=order_id)