Repository: deis/deis

Issue:
deis auth:cancel doesn't delete the account 
steps to reproduce the issue 

```
deis register http://deis.54.193.35.8.xip.io --username=Test --password=asdf1234 --email=test@test.co.nz
Registered Test
Logged in as Test
sivarams-MacBook-Pro:integration ram$ deis auth:cancel
Please log in again in order to cancel this account
username: Test
password: 
Logged in as Test
Cancel account "Test" at http://deis.54.193.35.8.xip.io? (y/n) y
Account cancelled
sivarams-MacBook-Pro:integration ram$ deis register http://deis.54.193.35.8.xip.io --username=Test --password=asdf1234 --email=test@test.co.nz
Registration failed {"username": ["User with this Username already exists."]}
```

auth:cancel says account cancelled but when I try to register with the same account it's not succeeding  . 
controller logs

```
2014-07-01 14:12:00 [119] [DEBUG] POST /api/auth/register
2014-07-01 14:12:01 [119] [DEBUG] GET /api/auth/login/
2014-07-01 14:12:01 [120] [DEBUG] POST /api/auth/login/
2014-07-01 14:12:14 [120] [DEBUG] GET /api/auth/login/
2014-07-01 14:12:14 [120] [DEBUG] POST /api/auth/login/
2014-07-01 14:12:16 [121] [DEBUG] DELETE /api/auth/cancel
ERROR Internal Server Error: /api/auth/cancel
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 114, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/viewsets.py", line 78, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py", line 57, in wrapped_view
    return view_func(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py", line 400, in dispatch
    response = self.handle_exception(exc)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py", line 397, in dispatch
    response = handler(request, *args, **kwargs)
  File "/app/api/views.py", line 162, in destroy
    obj.delete()
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 695, in delete
    collector.delete()
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/deletion.py", line 282, in delete
    sender=model, instance=obj, using=self.using
  File "/usr/local/lib/python2.7/dist-packages/django/dispatch/dispatcher.py", line 185, in send
    response = receiver(signal=self, sender=sender, **named)
  File "/app/api/models.py", line 613, in _etcd_purge_user
    _etcd_client.delete('/deis/builder/users/{}'.format(username), dir=True, recursive=True)
  File "/usr/local/lib/python2.7/dist-packages/etcd/client.py", line 343, in delete
    self.key_endpoint + key, self._MDELETE, kwds)
  File "/usr/local/lib/python2.7/dist-packages/etcd/client.py", line 533, in api_execute
    return self._handle_server_response(response)
  File "/usr/local/lib/python2.7/dist-packages/etcd/client.py", line 549, in _handle_server_response
    etcd.EtcdError.handle(**r)
  File "/usr/local/lib/python2.7/dist-packages/etcd/__init__.py", line 110, in handle
    raise exc(msg)
KeyError: 'Key not found : /deis/builder/users/Test'
```



Discussion:
Sadly, this was easy to reproduce on my first auth:cancel

``` python
[2014-07-14 15:17:29,251: WARNING/MainProcess] celery@ff90d51daaa8 ready.
2014-07-14 15:23:50 [106] [DEBUG] POST /api/auth/register
2014-07-14 15:23:50 [107] [DEBUG] GET /api/auth/login/
2014-07-14 15:23:50 [107] [DEBUG] POST /api/auth/login/
2014-07-14 15:24:11 [107] [DEBUG] GET /api/auth/login/
2014-07-14 15:24:11 [106] [DEBUG] POST /api/auth/login/
2014-07-14 15:24:13 [106] [DEBUG] DELETE /api/auth/cancel
ERROR Internal Server Error: /api/auth/cancel
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 114, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/viewsets.py", line 78, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py", line 57, in wrapped_view
    return view_func(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py", line 400, in dispatch
    response = self.handle_exception(exc)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py", line 397, in dispatch
    response = handler(request, *args, **kwargs)
  File "/app/api/views.py", line 162, in destroy
    obj.delete()
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 695, in delete
    collector.delete()
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/deletion.py", line 282, in delete
    sender=model, instance=obj, using=self.using
  File "/usr/local/lib/python2.7/dist-packages/django/dispatch/dispatcher.py", line 185, in send
    response = receiver(signal=self, sender=sender, **named)
  File "/app/api/models.py", line 621, in _etcd_purge_user
    _etcd_client.delete('/deis/builder/users/{}'.format(username), dir=True, recursive=True)
  File "/usr/local/lib/python2.7/dist-packages/etcd/client.py", line 343, in delete
    self.key_endpoint + key, self._MDELETE, kwds)
  File "/usr/local/lib/python2.7/dist-packages/etcd/client.py", line 533, in api_execute
    return self._handle_server_response(response)
  File "/usr/local/lib/python2.7/dist-packages/etcd/client.py", line 549, in _handle_server_response
    etcd.EtcdError.handle(**r)
  File "/usr/local/lib/python2.7/dist-packages/etcd/__init__.py", line 110, in handle
    raise exc(msg)
KeyError: 'Key not found : /deis/builder/users/matt'
```

This particular case happens when you do `deis register` followed immediately by `deis auth:cancel` without having done `deis keys:add`. We use Django signals to remove the /deis/builder/users/you directory recursively, but it isn't there since we only create it passively when we upload keys.

Note for @smothiki and myself: we need an integration/regression test for the above scenario.


Resolving patch:
diff --git a/controller/api/models.py b/controller/api/models.py
--- a/controller/api/models.py
+++ b/controller/api/models.py
@@ -618,7 +618,12 @@ def _etcd_purge_key(**kwargs):
 
 def _etcd_purge_user(**kwargs):
     username = kwargs['instance'].username
-    _etcd_client.delete('/deis/builder/users/{}'.format(username), dir=True, recursive=True)
+    try:
+        _etcd_client.delete(
+            '/deis/builder/users/{}'.format(username), dir=True, recursive=True)
+    except KeyError:
+        # If _etcd_publish_key() wasn't called, there is no user dir to delete.
+        pass
 
 
 def _etcd_publish_domains(**kwargs):


Test patch: