commit 7f3dd65e5dc79cc456ef58a052501ec256d5070b
Author: Giuseppe Lavagetto <lavagetto@gmail.com>
Date:   Mon Feb 13 14:12:39 2017 +0100

    Support auth API both <= 2.2.5 and >= 2.3.0
    
    Closes #210

diff --git a/src/etcd/auth.py b/src/etcd/auth.py
index 796772d..c5c7346 100644
--- a/src/etcd/auth.py
+++ b/src/etcd/auth.py
@@ -14,13 +14,28 @@ class EtcdAuthBase(object):
         self.name = name
         self.uri = "{}/auth/{}s/{}".format(self.client.version_prefix,
                                            self.entity, self.name)
+        # This will be lazily evaluated if not manually set
+        self._legacy_api = None
+
+    @property
+    def legacy_api(self):
+        if self._legacy_api is None:
+            # The auth API has changed between 2.2 and 2.3, true story!
+            major, minor, _ = map(int, self.client.version.split('.'))
+            self._legacy_api = (major < 3 and minor < 3)
+        return self._legacy_api
+
 
     @property
     def names(self):
         key = "{}s".format(self.entity)
         uri = "{}/auth/{}".format(self.client.version_prefix, key)
         response = self.client.api_execute(uri, self.client._MGET)
-        return json.loads(response.data.decode('utf-8'))[key]
+        if self.legacy_api:
+            return json.loads(response.data.decode('utf-8'))[key]
+        else:
+            return [obj[self.entity]
+                    for obj in json.loads(response.data.decode('utf-8'))[key]]
 
     def read(self):
         try:
@@ -102,7 +117,16 @@ class EtcdUser(EtcdAuthBase):
 
     def _from_net(self, data):
         d = json.loads(data.decode('utf-8'))
-        self.roles = d.get('roles', [])
+        roles = d.get('roles', [])
+        try:
+            self.roles = roles
+        except TypeError:
+            # with the change of API, PUT responses are different
+            # from GET reponses, which makes everything so funny.
+            # Specifically, PUT responses are the same as before...
+            if self.legacy_api:
+                raise
+            self.roles = [obj['role'] for obj in roles]
         self.name = d.get('user')
 
     def _to_net(self, prevobj=None):
diff --git a/src/etcd/tests/test_auth.py b/src/etcd/tests/test_auth.py
index 14475f9..5c8c0b0 100644
--- a/src/etcd/tests/test_auth.py
+++ b/src/etcd/tests/test_auth.py
@@ -93,6 +93,10 @@ class EtcdUserTest(TestEtcdAuthBase):
         self.assertEquals(u.roles, set(['guest', 'root']))
         # set roles as a list, it works!
         u.roles = ['guest', 'test_group']
+        # We need this or the new API will return an internal error
+        r = auth.EtcdRole(self.client, 'test_group')
+        r.acls = {'*': 'R', '/test/*': 'RW'}
+        r.write()
         try:
             u.write()
         except: