Add an OrderClient and order API tests

This change adds an OrderClient for testing the Barbican order API.
It adds an order test suite and integrates the OrderClient with the
plugin testing infrastructure.

Change-Id: I813648476a3a7b0b0e416a524d4629dffe36c92e
Depends-On: I844d1a97ffb82aa67a4ef3b89bd82c90aa221cbb
Implements: bp tempest-plugin
diff --git a/barbican_tempest_plugin/plugin.py b/barbican_tempest_plugin/plugin.py
index 68a971a..a0f4ca4 100644
--- a/barbican_tempest_plugin/plugin.py
+++ b/barbican_tempest_plugin/plugin.py
@@ -44,8 +44,9 @@
             'client_names': [
                 'ConsumerClient',
                 'ContainerClient',
-                'SecretMetadataClient',
-                'SecretClient'
+                'OrderClient',
+                'SecretClient',
+                'SecretMetadataClient'
             ],
         }
         return [v1_params]
diff --git a/barbican_tempest_plugin/services/key_manager/json/__init__.py b/barbican_tempest_plugin/services/key_manager/json/__init__.py
index ffb1734..b3ee41e 100644
--- a/barbican_tempest_plugin/services/key_manager/json/__init__.py
+++ b/barbican_tempest_plugin/services/key_manager/json/__init__.py
@@ -16,6 +16,8 @@
     import ConsumerClient
 from barbican_tempest_plugin.services.key_manager.json.container_client \
     import ContainerClient
+from barbican_tempest_plugin.services.key_manager.json.order_client \
+    import OrderClient
 from barbican_tempest_plugin.services.key_manager.json.secret_client \
     import SecretClient
 from barbican_tempest_plugin.services.key_manager.json.secret_metadata_client \
@@ -24,6 +26,7 @@
 __all__ = [
     'ConsumerClient',
     'ContainerClient',
-    'SecretMetadataClient',
-    'SecretClient'
+    'OrderClient',
+    'SecretClient',
+    'SecretMetadataClient'
 ]
diff --git a/barbican_tempest_plugin/services/key_manager/json/order_client.py b/barbican_tempest_plugin/services/key_manager/json/order_client.py
new file mode 100644
index 0000000..ba0ce51
--- /dev/null
+++ b/barbican_tempest_plugin/services/key_manager/json/order_client.py
@@ -0,0 +1,56 @@
+# Copyright (c) 2016 Johns Hopkins University Applied Physics Laboratory
+#
+# 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 six.moves.urllib import parse as urllib
+
+from tempest import config
+from tempest.lib.common import rest_client
+
+CONF = config.CONF
+
+
+class OrderClient(rest_client.RestClient):
+
+    def list_orders(self, **kwargs):
+        uri = "/v1/orders"
+        if kwargs:
+            uri += "?%s" % urllib.urlencode(kwargs)
+
+        response, body = self.get(uri)
+        self.expected_success(200, response.status)
+        return json.loads(body)
+
+    def create_order(self, **kwargs):
+        uri = "/v1/orders"
+
+        response, body = self.post(uri, json.dumps(kwargs))
+        self.expected_success(202, response.status)
+        return json.loads(body)
+
+    def get_order(self, order_id):
+        uri = "v1/orders/%s" % order_id
+
+        response, body = self.get(uri)
+        self.expected_success(200, response.status)
+        return json.loads(body)
+
+    def delete_order(self, order_id):
+        uri = "/v1/orders/%s" % order_id
+
+        response, _ = self.delete(uri)
+        self.expected_success(204, response.status)
+        return
diff --git a/barbican_tempest_plugin/tests/api/base.py b/barbican_tempest_plugin/tests/api/base.py
index 58f6bbf..abf1e83 100644
--- a/barbican_tempest_plugin/tests/api/base.py
+++ b/barbican_tempest_plugin/tests/api/base.py
@@ -23,7 +23,7 @@
 CONF = config.CONF
 
 # NOTE(dane-fichter): We need to track resource types for cleanup.
-RESOURCE_TYPES = ['secret', 'container']
+RESOURCE_TYPES = ['container', 'order', 'secret']
 
 
 def _get_uuid(href):
@@ -37,10 +37,16 @@
         @functools.wraps(f)
         def wrapper(cls, *args, **kwargs):
             resp = f(cls, *args, **kwargs)
-            if resource == 'secret':
-                uuid = _get_uuid(resp['secret_ref'])
             if resource == 'container':
                 uuid = _get_uuid(resp['container_ref'])
+            if resource == 'order':
+                uuid = _get_uuid(resp.get('order_ref'))
+                order_metadata = cls.get_order(uuid)
+                secret_ref = order_metadata.get('secret_ref')
+                if secret_ref:
+                    cls.created_objects['secret'].add(_get_uuid(secret_ref))
+            if resource == 'secret':
+                uuid = _get_uuid(resp['secret_ref'])
             cls.created_objects[resource].add(uuid)
             return resp
         return wrapper
@@ -65,10 +71,11 @@
         cls.container_client = os.secret_v1.ContainerClient(
             service='key-manager'
         )
+        cls.order_client = os.secret_v1.OrderClient(service='key-manager')
+        cls.secret_client = os.secret_v1.SecretClient(service='key-manager')
         cls.secret_metadata_client = os.secret_v1.SecretMetadataClient(
             service='key-manager'
         )
-        cls.secret_client = os.secret_v1.SecretClient(service='key-manager')
 
     @classmethod
     def resource_setup(cls):
@@ -79,24 +86,16 @@
     @classmethod
     def resource_cleanup(cls):
         try:
-            for secret_uuid in list(cls.created_objects['secret']):
-                cls.delete_secret(secret_uuid)
             for container_uuid in list(cls.created_objects['container']):
                 cls.delete_container(container_uuid)
+            for order_uuid in list(cls.created_objects['order']):
+                cls.delete_order(order_uuid)
+            for secret_uuid in list(cls.created_objects['secret']):
+                cls.delete_secret(secret_uuid)
         finally:
             super(BaseKeyManagerTest, cls).resource_cleanup()
 
     @classmethod
-    @creates('secret')
-    def create_secret(cls, **kwargs):
-        return cls.secret_client.create_secret(**kwargs)
-
-    @classmethod
-    def delete_secret(cls, uuid):
-        cls.created_objects['secret'].remove(uuid)
-        return cls.secret_client.delete_secret(uuid)
-
-    @classmethod
     @creates('container')
     def create_container(cls, **kwargs):
         return cls.container_client.create_container(**kwargs)
@@ -105,3 +104,27 @@
     def delete_container(cls, uuid):
         cls.created_objects['container'].remove(uuid)
         return cls.container_client.delete_container(uuid)
+
+    @classmethod
+    @creates('order')
+    def create_order(cls, **kwargs):
+        return cls.order_client.create_order(**kwargs)
+
+    @classmethod
+    def get_order(cls, uuid):
+        return cls.order_client.get_order(uuid)
+
+    @classmethod
+    def delete_order(cls, uuid):
+        cls.created_objects['order'].remove(uuid)
+        return cls.order_client.delete_order(uuid)
+
+    @classmethod
+    @creates('secret')
+    def create_secret(cls, **kwargs):
+        return cls.secret_client.create_secret(**kwargs)
+
+    @classmethod
+    def delete_secret(cls, uuid):
+        cls.created_objects['secret'].remove(uuid)
+        return cls.secret_client.delete_secret(uuid)
diff --git a/barbican_tempest_plugin/tests/api/test_orders.py b/barbican_tempest_plugin/tests/api/test_orders.py
new file mode 100644
index 0000000..fa76516
--- /dev/null
+++ b/barbican_tempest_plugin/tests/api/test_orders.py
@@ -0,0 +1,135 @@
+# Copyright (c) 2016 Johns Hopkins University Applied Physics Laboratory
+#
+# 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.
+
+from barbican_tempest_plugin.tests.api import base
+
+
+class OrdersTest(base.BaseKeyManagerTest):
+    """Orders API tests."""
+
+    def test_create_list_delete_orders(self):
+        # Confirm that there are no orders
+        body = self.order_client.list_orders()
+        self.assertEqual(0, body.get('total'), body)
+        self.assertEqual(0, len(body.get('orders')), body)
+
+        # Create some orders
+        body = self.create_order(
+            type="key",
+            meta={
+                "name": "order-key-1",
+                "algorithm": "AES",
+                "bit_length": 256,
+                "mode": "cbc",
+                "payload_content_type": "application/octet-stream"
+            }
+        )
+        order_id_1 = base._get_uuid(body.get('order_ref'))
+        body = self.create_order(
+            type="key",
+            meta={
+                "name": "order-key-2",
+                "algorithm": "AES",
+                "bit_length": 256,
+                "mode": "cbc",
+                "payload_content_type": "application/octet-stream"
+            }
+        )
+        order_id_2 = base._get_uuid(body.get('order_ref'))
+
+        # Verify that the orders can be found via generic listing.
+        body = self.order_client.list_orders()
+        self.assertEqual(2, body.get('total'), body)
+        self.assertEqual(2, len(body.get('orders')), body)
+
+        orders = body.get('orders')
+        for order in orders:
+            self.assertIn(
+                base._get_uuid(order.get('order_ref')),
+                [order_id_1, order_id_2],
+                body
+            )
+            self.assertIn(
+                'secret_ref',
+                order.keys()
+            )
+
+        # Verify that the orders can be found via specific listing.
+        body = self.order_client.get_order(order_id_1)
+        self.assertEqual(
+            order_id_1,
+            base._get_uuid(body.get('order_ref')),
+            body
+        )
+        self.assertIn('created', body, body)
+        self.assertIn('creator_id', body, body)
+        self.assertIn('meta', body, body)
+
+        meta = body.get('meta')
+        self.assertEqual("order-key-1", meta.get('name'), meta)
+        self.assertEqual("AES", meta.get('algorithm'), meta)
+        self.assertEqual(256, meta.get('bit_length'), meta)
+        self.assertEqual("cbc", meta.get('mode'), meta)
+        self.assertEqual(
+            "application/octet-stream",
+            meta.get('payload_content_type'),
+            meta
+        )
+
+        self.assertIn('secret_ref', body, body)
+        self.assertEqual("ACTIVE", body.get('status'), body)
+        self.assertEqual("key", body.get('type'), body)
+        self.assertIn('updated', body, body)
+
+        body = self.order_client.get_order(order_id_2)
+        self.assertEqual(
+            order_id_2,
+            base._get_uuid(body.get('order_ref')),
+            body
+        )
+        self.assertIn('created', body, body)
+        self.assertIn('creator_id', body, body)
+        self.assertIn('meta', body, body)
+
+        meta = body.get('meta')
+        self.assertEqual("order-key-2", meta.get('name'), meta)
+        self.assertEqual("AES", meta.get('algorithm'), meta)
+        self.assertEqual(256, meta.get('bit_length'), meta)
+        self.assertEqual("cbc", meta.get('mode'), meta)
+        self.assertEqual(
+            "application/octet-stream",
+            meta.get('payload_content_type'),
+            meta
+        )
+
+        self.assertIn('secret_ref', body, body)
+        self.assertEqual("ACTIVE", body.get('status'), body)
+        self.assertEqual("key", body.get('type'), body)
+        self.assertIn('updated', body, body)
+
+        # Delete one order and confirm that it got deleted
+        self.delete_order(order_id_1)
+
+        body = self.order_client.list_orders()
+        self.assertEqual(1, body.get('total'), body)
+        self.assertEqual(1, len(body.get('orders')), body)
+
+        order = body.get('orders')[0]
+        self.assertEqual(
+            order_id_2,
+            base._get_uuid(order.get('order_ref')),
+            body
+        )
+
+        # Leave one order behind to get cleaned up by infra