Repository: deis/deis

Issue:
500 error on missing credentials
To reproduce:
1. Register new user with `deis register`
2. Skip credentials discovery
3. Create a formation with `deis create`

```
==> /var/log/deis/celeryd.log <==
[2013-09-04 14:20:36,844: INFO/MainProcess] Got task from broker: ec2.build_layer[a49d3f10-64f4-47eb-88c3-f742992f9344]
[2013-09-04 14:20:36,871: ERROR/MainProcess] Task ec2.build_layer[a49d3f10-64f4-47eb-88c3-f742992f9344] raised exception: KeyError(u'access_key',)
Traceback (most recent call last):
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/celery/task/trace.py", line 233, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/celery/task/trace.py", line 420, in __protected_call__
    return self.run(*args, **kwargs)
  File "/opt/deis/controller/celerytasks/ec2.py", line 34, in build_layer
    region, creds['access_key'], creds['secret_key'])
None: u'access_key'

==> /var/log/deis/server.log <==
ERROR Internal Server Error: /api/formations/unisex-jerrycan/layers
Traceback (most recent call last):
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 115, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/viewsets.py", line 78, in view
    return self.dispatch(request, *args, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 77, in wrapped_view
    return view_func(*args, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/views.py", line 327, in dispatch
    response = self.handle_exception(exc)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/views.py", line 324, in dispatch
    response = handler(request, *args, **kwargs)
  File "/opt/deis/controller/api/views.py", line 277, in create
    return OwnerViewSet.create(self, request, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/mixins.py", line 53, in create
    self.post_save(self.object, created=True)
  File "/opt/deis/controller/api/views.py", line 284, in post_save
    layer.build()
  File "/opt/deis/controller/api/models.py", line 524, in build
    return tasks.build_layer.delay(*args).wait()
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/celery/result.py", line 108, in get
    interval=interval)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/celery/backends/amqp.py", line 151, in wait_for
    raise self.exception_to_python(meta['result'])
KeyError: u'access_key'
```

Instead we should be returning a 400 Bad Request with an appropriate error message.



Discussion:

Resolving patch:
diff --git a/api/views.py b/api/views.py
--- a/api/views.py
+++ b/api/views.py
@@ -155,6 +155,8 @@ def create(self, request, **kwargs):
         except IntegrityError:
             return Response('Formation with this Id already exists.',
                             status=HTTP_400_BAD_REQUEST)
+        except EnvironmentError as e:
+            return Response(str(e), status=HTTP_400_BAD_REQUEST)
 
     def post_save(self, formation, created=False, **kwargs):
         if created:
@@ -242,7 +244,10 @@ def run(self, request, **kwargs):
 
     def destroy(self, request, **kwargs):
         formation = self.get_object()
-        formation.destroy()
+        try:
+            formation.destroy()
+        except EnvironmentError as e:
+            return Response(str(e), status=HTTP_400_BAD_REQUEST)
         formation.delete()
         return Response(status=status.HTTP_204_NO_CONTENT)
 
@@ -309,7 +314,10 @@ def get_object(self, *args, **kwargs):
 
     def destroy(self, request, **kwargs):
         node = self.get_object()
-        node.destroy()
+        try:
+            node.destroy()
+        except EnvironmentError as e:
+            return Response(str(e), status=HTTP_400_BAD_REQUEST)
         return Response(status=status.HTTP_204_NO_CONTENT)
 
 
diff --git a/celerytasks/ec2.py b/celerytasks/ec2.py
--- a/celerytasks/ec2.py
+++ b/celerytasks/ec2.py
@@ -30,8 +30,7 @@
 @task(name='ec2.build_layer')
 def build_layer(layer, creds, params):
     region = params.get('region', 'us-east-1')
-    conn = create_ec2_connection(
-        region, creds['access_key'], creds['secret_key'])
+    conn = create_ec2_connection(creds, region)
     # create a new sg and authorize all ports
     # use iptables on the host to firewall ports
     sg = conn.create_security_group(layer, 'Created by Deis')
@@ -56,8 +55,7 @@ def destroy_layer(layer, creds, params):
     # let's take a nap
     time.sleep(5)
     region = params.get('region', 'us-east-1')
-    conn = create_ec2_connection(
-        region, creds['access_key'], creds['secret_key'])
+    conn = create_ec2_connection(creds, region)
     try:
         conn.delete_security_group(layer)
     except EC2ResponseError as e:
@@ -68,8 +66,7 @@ def destroy_layer(layer, creds, params):
 @task(name='ec2.launch_node')
 def launch_node(node_id, creds, params, init, ssh_username, ssh_private_key):
     region = params.get('region', 'us-east-1')
-    conn = create_ec2_connection(
-        region, creds['access_key'], creds['secret_key'])
+    conn = create_ec2_connection(creds, region)
     # find or create the security group for this formation
     sg_name = params['layer']
     sg = conn.get_all_security_groups(sg_name)[0]
@@ -134,8 +131,7 @@ def launch_node(node_id, creds, params, init, ssh_username, ssh_private_key):
 @task(name='ec2.terminate_node')
 def terminate_node(node_id, creds, params, provider_id):
     region = params.get('region', 'us-east-1')
-    conn = create_ec2_connection(
-        region, creds['access_key'], creds['secret_key'])
+    conn = create_ec2_connection(creds, region)
     if provider_id:
         conn.terminate_instances([provider_id])
         i = conn.get_all_instances([provider_id])[0].instances[0]
@@ -174,9 +170,12 @@ def run_node(node_id, ssh_username, fqdn, ssh_private_key, docker_args, command)
 
 # utility functions
 
-def create_ec2_connection(region, access_key, secret_key):
-    return ec2.connect_to_region(region, aws_access_key_id=access_key,
-                                 aws_secret_access_key=secret_key)
+def create_ec2_connection(creds, region):
+    if not creds:
+        raise EnvironmentError('No credentials provided')
+    return ec2.connect_to_region(region,
+                                 aws_access_key_id=creds['access_key'],
+                                 aws_secret_access_key=creds['secret_key'])
 
 
 def prepare_run_kwargs(params, init):


Test patch: