Updated doc warnings, updated AWS request signer to support POST.

Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/9507a634
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/9507a634
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/9507a634

Branch: refs/heads/trunk
Commit: 9507a634d0453bac7227a362b1d972dd81abc260
Parents: 0999e52
Author: anthony-shaw <[email protected]>
Authored: Sun Jan 3 20:42:07 2016 +1100
Committer: anthony-shaw <[email protected]>
Committed: Sun Jan 3 20:42:07 2016 +1100

----------------------------------------------------------------------
 docs/container/api.rst            |  2 +-
 docs/container/drivers/docker.rst |  4 +--
 docs/container/drivers/ecs.rst    |  6 +++-
 docs/container/drivers/index.rst  |  2 +-
 docs/container/drivers/joyent.rst |  4 +--
 libcloud/common/aws.py            | 59 +++++++++++++++++++++++-----------
 libcloud/common/base.py           |  1 +
 libcloud/container/drivers/ecs.py | 40 +++++++++++++----------
 8 files changed, 76 insertions(+), 42 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/9507a634/docs/container/api.rst
----------------------------------------------------------------------
diff --git a/docs/container/api.rst b/docs/container/api.rst
index a3ee225..f9f7dd4 100644
--- a/docs/container/api.rst
+++ b/docs/container/api.rst
@@ -1,7 +1,7 @@
 :orphan:
 
 Container Base API
-============
+==================
 
 .. autoclass:: libcloud.container.base.ContainerDriver
     :members:

http://git-wip-us.apache.org/repos/asf/libcloud/blob/9507a634/docs/container/drivers/docker.rst
----------------------------------------------------------------------
diff --git a/docs/container/drivers/docker.rst 
b/docs/container/drivers/docker.rst
index 7bb729c..32cbd2c 100644
--- a/docs/container/drivers/docker.rst
+++ b/docs/container/drivers/docker.rst
@@ -1,5 +1,5 @@
 Docker Container Driver Documentation
-==================================
+=====================================
 
 `Docker`_ containers wrap up a piece of software in a complete filesystem that 
contains everything it needs to run:
 code, runtime, system tools, system libraries – anything you can install on 
a server. This guarantees that it will always run the same,
@@ -11,7 +11,7 @@ regardless of the environment it is running in.
     :target: http://docker.io/
 
 Instantiating the driver
--------------------------------------
+------------------------
 
 .. literalinclude:: /examples/container/docker/instantiate_driver.py
    :language: python

http://git-wip-us.apache.org/repos/asf/libcloud/blob/9507a634/docs/container/drivers/ecs.rst
----------------------------------------------------------------------
diff --git a/docs/container/drivers/ecs.rst b/docs/container/drivers/ecs.rst
index 45cc2f8..089fae6 100644
--- a/docs/container/drivers/ecs.rst
+++ b/docs/container/drivers/ecs.rst
@@ -8,8 +8,12 @@ Elastic Container Service is a container-as-a-service feature 
of `AWS`_.
     :width: 300
     :target: http://aws.amazon.com/
 
+To provide API key access, you should apply one of the roles:
+* AmazonEC2ContainerServiceFullAccess
+* AmazonEC2ContainerServiceReadOnlyAccess
+
 Instantiating the driver
--------------------------------------
+------------------------
         
 .. literalinclude:: /examples/container/ecs/instantiate_driver.py
    :language: python

http://git-wip-us.apache.org/repos/asf/libcloud/blob/9507a634/docs/container/drivers/index.rst
----------------------------------------------------------------------
diff --git a/docs/container/drivers/index.rst b/docs/container/drivers/index.rst
index be5db22..fd1145e 100644
--- a/docs/container/drivers/index.rst
+++ b/docs/container/drivers/index.rst
@@ -1,7 +1,7 @@
 :orphan:
 
 Container Drivers Documentation
-============================
+===============================
 
 This chapter includes links to driver (provider) specific documentation pages.
 

http://git-wip-us.apache.org/repos/asf/libcloud/blob/9507a634/docs/container/drivers/joyent.rst
----------------------------------------------------------------------
diff --git a/docs/container/drivers/joyent.rst 
b/docs/container/drivers/joyent.rst
index fc43916..f0e3da7 100644
--- a/docs/container/drivers/joyent.rst
+++ b/docs/container/drivers/joyent.rst
@@ -1,5 +1,5 @@
 Joyent Triton Container Driver Documentation
-==================================
+============================================
 
 `Joyent Triton`_ is a Docker hosting service, provided by service provider 
`Joyent`_.
 Docker-native tools and elastic hosts make deploying on Triton as easy as 
running Docker on your laptop.
@@ -12,7 +12,7 @@ Mix Docker containers with container-native Linux to extend 
the benefits of cont
     :target: http://joyent.com/
 
 Instantiating the driver
--------------------------------------
+------------------------
 
 Download the script::
 

http://git-wip-us.apache.org/repos/asf/libcloud/blob/9507a634/libcloud/common/aws.py
----------------------------------------------------------------------
diff --git a/libcloud/common/aws.py b/libcloud/common/aws.py
index 4d7523e..b7153e3 100644
--- a/libcloud/common/aws.py
+++ b/libcloud/common/aws.py
@@ -21,6 +21,11 @@ import time
 from hashlib import sha256
 
 try:
+    import simplejson as json
+except ImportError:
+    import json
+
+try:
     from lxml import etree as ET
 except ImportError:
     from xml.etree import ElementTree as ET
@@ -180,7 +185,8 @@ class AWSRequestSigner(object):
     def get_request_params(self, params, method='GET', path='/'):
         return params
 
-    def get_request_headers(self, params, headers, method='GET', path='/'):
+    def get_request_headers(self, params, headers, method='GET', path='/',
+                            data=None):
         return params, headers
 
 
@@ -237,27 +243,32 @@ class AWSRequestSignerAlgorithmV2(AWSRequestSigner):
 
 class AWSRequestSignerAlgorithmV4(AWSRequestSigner):
     def get_request_params(self, params, method='GET', path='/'):
-        params['Version'] = self.version
+        if method == 'GET':
+            params['Version'] = self.version
         return params
 
-    def get_request_headers(self, params, headers, method='GET', path='/'):
+    def get_request_headers(self, params, headers, method='GET', path='/',
+                            data=None):
         now = datetime.utcnow()
         headers['X-AMZ-Date'] = now.strftime('%Y%m%dT%H%M%SZ')
         headers['Authorization'] = \
             self._get_authorization_v4_header(params=params, headers=headers,
-                                              dt=now, method=method, path=path)
+                                              dt=now, method=method, path=path,
+                                              data=data)
 
         return params, headers
 
     def _get_authorization_v4_header(self, params, headers, dt, method='GET',
-                                     path='/'):
-        assert method in ['GET', 'POST'], 'AWS Signature V4 not implemented 
for ' \
-                                'other methods than GET and POST'
+                                     path='/', data=None):
+        assert method in ['GET', 'POST'], 'AWS Signature V4 ' \
+                                          'not implemented for ' \
+                                          'other methods than GET and POST'
 
         credentials_scope = self._get_credential_scope(dt=dt)
         signed_headers = self._get_signed_headers(headers=headers)
         signature = self._get_signature(params=params, headers=headers,
-                                        dt=dt, method=method, path=path)
+                                        dt=dt, method=method, path=path,
+                                        data=data)
 
         return 'AWS4-HMAC-SHA256 Credential=%(u)s/%(c)s, ' \
                'SignedHeaders=%(sh)s, Signature=%(s)s' % {
@@ -267,11 +278,12 @@ class AWSRequestSignerAlgorithmV4(AWSRequestSigner):
                    's': signature
                }
 
-    def _get_signature(self, params, headers, dt, method, path):
+    def _get_signature(self, params, headers, dt, method, path, data):
         key = self._get_key_to_sign_with(dt)
         string_to_sign = self._get_string_to_sign(params=params,
                                                   headers=headers, dt=dt,
-                                                  method=method, path=path)
+                                                  method=method, path=path,
+                                                  data=data)
         return _sign(key=key, msg=string_to_sign, hex=True)
 
     def _get_key_to_sign_with(self, dt):
@@ -284,11 +296,12 @@ class AWSRequestSignerAlgorithmV4(AWSRequestSigner):
                 self.connection.service_name),
             'aws4_request')
 
-    def _get_string_to_sign(self, params, headers, dt, method, path):
+    def _get_string_to_sign(self, params, headers, dt, method, path, data):
         canonical_request = self._get_canonical_request(params=params,
                                                         headers=headers,
                                                         method=method,
-                                                        path=path)
+                                                        path=path,
+                                                        data=data)
 
         return '\n'.join(['AWS4-HMAC-SHA256',
                           dt.strftime('%Y%m%dT%H%M%SZ'),
@@ -308,8 +321,11 @@ class AWSRequestSignerAlgorithmV4(AWSRequestSigner):
         return '\n'.join([':'.join([k.lower(), v.strip()])
                           for k, v in sorted(headers.items())]) + '\n'
 
-    def _get_payload_hash(self):
-        return _hash('')
+    def _get_payload_hash(self, method, data=None):
+        if method == 'GET':
+            return _hash('')
+        elif method == 'POST':
+            return _hash(data)
 
     def _get_request_params(self, params):
         # For self.method == GET
@@ -317,14 +333,14 @@ class AWSRequestSignerAlgorithmV4(AWSRequestSigner):
                          (urlquote(k, safe=''), urlquote(str(v), safe='~'))
                          for k, v in sorted(params.items())])
 
-    def _get_canonical_request(self, params, headers, method, path):
+    def _get_canonical_request(self, params, headers, method, path, data):
         return '\n'.join([
             method,
             path,
             self._get_request_params(params),
             self._get_canonical_headers(headers),
             self._get_signed_headers(headers),
-            self._get_payload_hash()
+            self._get_payload_hash(method, data)
         ])
 
 
@@ -365,7 +381,8 @@ class SignedAWSConnection(AWSTokenConnection):
         params, headers = self.signer.get_request_headers(params=params,
                                                           headers=headers,
                                                           method=self.method,
-                                                          path=self.action)
+                                                          path=self.action,
+                                                          data=self.data)
         return params, headers
 
 
@@ -374,7 +391,13 @@ class AWSJsonResponse(JsonResponse):
     Amazon ECS response class.
     ECS API uses JSON unlike the s3, elb drivers
     """
-    
+    def parse_error(self):
+        response = json.loads(self.body)
+        code = response['__type']
+        message = response['Message']
+
+        return ('%s: %s' % (code, message))
+
 
 def _sign(key, msg, hex=False):
     if hex:

http://git-wip-us.apache.org/repos/asf/libcloud/blob/9507a634/libcloud/common/base.py
----------------------------------------------------------------------
diff --git a/libcloud/common/base.py b/libcloud/common/base.py
index d457e24..a25dcfc 100644
--- a/libcloud/common/base.py
+++ b/libcloud/common/base.py
@@ -709,6 +709,7 @@ class Connection(object):
         action = self.morph_action_hook(action)
         self.action = action
         self.method = method
+        self.data = data
 
         # Extend default parameters
         params = self.add_default_params(params)

http://git-wip-us.apache.org/repos/asf/libcloud/blob/9507a634/libcloud/container/drivers/ecs.py
----------------------------------------------------------------------
diff --git a/libcloud/container/drivers/ecs.py 
b/libcloud/container/drivers/ecs.py
index 1485027..746c176 100644
--- a/libcloud/container/drivers/ecs.py
+++ b/libcloud/container/drivers/ecs.py
@@ -13,16 +13,21 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-__all__ = [
-    'ElasticContainerDriver'
-]
-
+try:
+    import simplejson as json
+except ImportError:
+    import json
 
 from libcloud.container.base import (ContainerDriver, Container,
                                      ContainerCluster, ContainerImage)
 from libcloud.container.types import ContainerState
 from libcloud.common.aws import SignedAWSConnection, AWSJsonResponse
 
+__all__ = [
+    'ElasticContainerDriver'
+]
+
+
 VERSION = '2014-11-13'
 HOST = 'ecs.%s.amazonaws.com'
 ROOT = '/'
@@ -67,6 +72,7 @@ class ElasticContainerDriver(ContainerDriver):
         data = self.connection.request(
             ROOT,
             method='POST',
+            data=json.dumps({}),
             headers=self._get_headers(params['Action'])
         ).object
         return self._to_clusters(data)
@@ -87,7 +93,7 @@ class ElasticContainerDriver(ContainerDriver):
         response = self.connection.request(
             ROOT,
             method='POST',
-            data=request,
+            data=json.dumps(request),
             headers=self._get_headers('CreateCluster')
         ).object
         return self._to_cluster(response['cluster'])
@@ -103,7 +109,7 @@ class ElasticContainerDriver(ContainerDriver):
         data = self.connection.request(
             ROOT,
             method='POST',
-            data=request,
+            data=json.dumps(request),
             headers=self._get_headers('DeleteCluster')
         ).object
         return data['cluster']['status'] == 'INACTIVE'
@@ -142,15 +148,15 @@ class ElasticContainerDriver(ContainerDriver):
 
         :rtype: ``list`` of :class:`Container`
         """
-        request = None
+        request = {'cluster': 'default'}
         if cluster is not None:
-            request = {'cluster': cluster.id}
+            request['cluster'] = cluster.id
         if image is not None:
             request['family'] = image.name
         list_response = self.connection.request(
             ROOT,
             method='POST',
-            data=request,
+            data=json.dumps(request),
             headers=self._get_headers('ListTasks')
         ).object
         containers = self.ex_list_containers_for_task(
@@ -204,7 +210,7 @@ class ElasticContainerDriver(ContainerDriver):
         response = self.connection.request(
             ROOT,
             method='POST',
-            data=data,
+            data=json.dumps(data),
             headers=self._get_headers('RegisterTaskDefinition')
         ).object
         if start:
@@ -263,7 +269,7 @@ class ElasticContainerDriver(ContainerDriver):
         response = self.connection.request(
             ROOT,
             method='POST',
-            data=request,
+            data=json.dumps(request),
             headers=self._get_headers('StopTask')
         ).object
         containers = []
@@ -313,7 +319,7 @@ class ElasticContainerDriver(ContainerDriver):
         response = self.connection.request(
             ROOT,
             method='POST',
-            data=request,
+            data=json.dumps(request),
             headers=self._get_headers('RunTask')
         ).object
         containers = []
@@ -334,7 +340,7 @@ class ElasticContainerDriver(ContainerDriver):
         descripe_response = self.connection.request(
             ROOT,
             method='POST',
-            data=describe_request,
+            data=json.dumps(describe_request),
             headers=self._get_headers('DescribeTasks')
         ).object
         containers = []
@@ -375,7 +381,7 @@ class ElasticContainerDriver(ContainerDriver):
         response = self.connection.request(
             ROOT,
             method='POST',
-            data=request,
+            data=json.dumps(request),
             headers=self._get_headers('CreateService')
         ).object
         return response
@@ -395,7 +401,7 @@ class ElasticContainerDriver(ContainerDriver):
         response = self.connection.request(
             ROOT,
             method='POST',
-            data=request,
+            data=json.dumps(request),
             headers=self._get_headers('ListServices')
         ).object
         return response['serviceArns']
@@ -419,7 +425,7 @@ class ElasticContainerDriver(ContainerDriver):
         response = self.connection.request(
             ROOT,
             method='POST',
-            data=request,
+            data=json.dumps(request),
             headers=self._get_headers('DescribeServices')
         ).object
         return response['services'][0]
@@ -440,7 +446,7 @@ class ElasticContainerDriver(ContainerDriver):
         response = self.connection.request(
             ROOT,
             method='POST',
-            data=request,
+            data=json.dumps(request),
             headers=self._get_headers('DeleteService')
         ).object
         return response

Reply via email to