def connect(self) -> None: self._client = None self._client_failed = False self.cont = True protocol = 'http' etcd_list = get_ticket().etcd if isinstance(etcd_list, dict): protocol = etcd_list['protocol'] etcd_list = etcd_list['host'] if len(etcd_list) == 1: host, port = etcd_list[0].split(':') self._client = etcd.Client( host=host, port=int(port), protocol=protocol, allow_reconnect=True, allow_redirect=True) else: def split_addr(e:str) -> Tuple[str, int]: host, port = e.split(':') return host, int(port) host_list = [split_addr(e) for e in etcd_list] self._client = etcd.Client( host=tuple(host_list), protocol=protocol, allow_reconnect=True, allow_redirect=True)
def setUp(self): # Sets up the root user, toggles auth loop = asyncio.get_event_loop() self.client = etcd.Client(port=6001, loop=loop) u = auth.EtcdUser(self.client, 'root') u.password = '******' loop.run_until_complete(u.write()) self.client = etcd.Client(port=6001, username='******', password='******', loop=loop) self.unauth_client = etcd.Client(port=6001, loop=loop) a = auth.Auth(self.client) loop.run_until_complete(a.set_active(True))
def test_read(loop, self): u = auth.EtcdUser(self.client, 'root') # Reading an existing user succeeds try: yield from u.read() except Exception: self.fail("reading the root user raised an exception") # roles for said user are fetched self.assertEquals(u.roles, set(['root'])) # The user is correctly rendered out self.assertEquals(u._to_net(), [{ 'user': '******', 'password': None, 'roles': ['root'] }]) # An inexistent user raises the appropriate exception u = auth.EtcdUser(self.client, 'user.does.not.exist') with raises(etcd.EtcdKeyNotFound): yield from u.read() # Reading with an unauthenticated client raises an exception u = auth.EtcdUser(self.unauth_client, 'root') with raises(etcd.EtcdInsufficientPermissions): yield from u.read() # Generic errors are caught c = etcd.Client(port=9999) u = auth.EtcdUser(c, 'root') with raises(etcd.EtcdException): yield from u.read()
def test_watch(loop, self): """ INTEGRATION: Receive a watch event from other process """ client = aio_etcd.Client(port=6281, allow_reconnect=True, loop=loop) set_result = yield from client.set('/test-key', 'test-value') queue = asyncio.Queue(loop=loop) @asyncio.coroutine def change_value(key, newValue): c = aio_etcd.Client(port=6281, loop=loop) yield from c.set(key, newValue) @asyncio.coroutine def watch_value(key, queue): c = aio_etcd.Client(port=6281, loop=loop) w = yield from c.watch(key) yield from queue.put(w.value) watcher = asyncio.async(watch_value('/test-key', queue), loop=loop) yield from asyncio.sleep(0.1, loop=loop) changer = asyncio.async(change_value('/test-key', 'new-test-value'), loop=loop) value = yield from asyncio.wait_for(queue.get(),timeout=2,loop=loop) yield from asyncio.wait_for(watcher,timeout=5,loop=loop) yield from asyncio.wait_for(changer,timeout=5,loop=loop) assert value == 'new-test-value'
def test_get_set_delete(loop, self): """ INTEGRATION: set a new value """ client = aio_etcd.Client(port=6281, loop=loop) try: get_result = yield from client.get('/test_set') assert False except aio_etcd.EtcdKeyNotFound as e: pass self.assertFalse((yield from client.contains('/test_set'))) set_result = yield from client.set('/test_set', 'test-key') self.assertEquals('set', set_result.action.lower()) self.assertEquals('/test_set', set_result.key) self.assertEquals('test-key', set_result.value) self.assertTrue((yield from client.contains('/test_set'))) get_result = yield from client.get('/test_set') self.assertEquals('get', get_result.action.lower()) self.assertEquals('/test_set', get_result.key) self.assertEquals('test-key', get_result.value) delete_result = yield from client.delete('/test_set') self.assertEquals('delete', delete_result.action.lower()) self.assertEquals('/test_set', delete_result.key) self.assertFalse((yield from client.contains('/test_set'))) try: get_result = yield from client.get('/test_set') assert False except aio_etcd.EtcdKeyNotFound as e: pass
def test_write_and_delete(loop,self): r = auth.EtcdRole(self.client, 'test_group') r.acls = {'/*': 'R', '/test/*': 'RW'} try: yield from r.write() except: self.fail("Writing a simple groups should not fail") # Create an user u = auth.EtcdUser(self.client, 'test_user') u.roles.add('guest') u.roles.add('root') # directly from my suitcase u.password = '******' try: yield from u.write() except: self.fail("creating a user doesn't work") # Password gets wiped self.assertEquals(u.password, None) yield from u.read() # Verify we can log in as this user and access the auth (it has the # root role) cl = etcd.Client(port=6001, username='******', password='******') ul = auth.EtcdUser(cl, 'root') try: yield from ul.read() except etcd.EtcdInsufficientPermissions: self.fail("Reading auth with the new user is not possible") self.assertEquals(u.name, "test_user") self.assertEquals(u.roles, set(['guest', 'root'])) # set roles as a list, it works! u.roles = ['guest', 'test_group'] try: yield from u.write() except: self.fail("updating a user you previously created fails") yield from u.read() self.assertIn('test_group', u.roles) # Unauthorized access is properly handled ua = auth.EtcdUser(self.unauth_client, 'test_user') with raises(etcd.EtcdInsufficientPermissions): yield from ua.write() # now let's test deletion du = auth.EtcdUser(self.client, 'user.does.not.exist') with raises(etcd.EtcdKeyNotFound): yield from du.delete() # Delete test_user yield from u.delete() with raises(etcd.EtcdKeyNotFound): yield from u.read() # Permissions are properly handled with raises(etcd.EtcdInsufficientPermissions): yield from ua.delete()
def test_get_set_authenticated(loop, self): """ INTEGRATION: set/get a new value authenticated """ client = aio_etcd.Client( port=6281, protocol='https', ca_cert=self.ca_cert_path, loop=loop) set_result = yield from client.set('/test_set', 'test-key') get_result = yield from client.get('/test_set')
def test_get_set_unauthenticated_missing_ca(loop, self): """ INTEGRATION: try unauthenticated w/out validation (https->https)""" # This doesn't work for now and will need further inspection client = aio_etcd.Client(protocol='https', port=6001, ssl_verify=ssl.CERT_NONE, loop=loop) set_result = yield from client.set('/test_set', 'test-key') get_result = yield from client.get('/test_set')
def test_retrieve_subkeys(loop, self): """ INTEGRATION: retrieve multiple subkeys """ client = aio_etcd.Client(port=6281, loop=loop) set_result = yield from client.write('/subtree/test_set', 'test-key1') set_result = yield from client.write('/subtree/test_set1', 'test-key2') set_result = yield from client.write('/subtree/test_set2', 'test-key3') get_result = yield from client.read('/subtree', recursive=True) result = [subkey.value for subkey in get_result.leaves] self.assertEquals(['test-key1', 'test-key2', 'test-key3'].sort(), result.sort())
def test_directory_ttl_update(loop, self): """ INTEGRATION: should be able to update a dir TTL """ client = aio_etcd.Client(port=6281, loop=loop) yield from client.write('/dir', None, dir=True, ttl=30) res = yield from client.write('/dir', None, dir=True, ttl=31, prevExist=True) self.assertEquals(res.ttl, 31) res = yield from client.get('/dir') res.ttl = 120 new_res = yield from client.update(res) self.assertEquals(new_res.ttl, 120)
def test_is_not_a_file(loop, self): """ INTEGRATION: try to write value to an existing directory """ client = aio_etcd.Client(port=6281, loop=loop) yield from client.set('/directory/test-key', 'test-value') try: yield from client.set('/directory', 'test-value') raise False except aio_etcd.EtcdNotFile: pass
def test_reconnect_not_allowed(loop, self): """ INTEGRATION: fail on server kill if not allow_reconnect """ self.processHelper.stop() self.processHelper.run(number=3) client = aio_etcd.Client(port=6281, allow_reconnect=False, loop=loop) self.processHelper.kill_one(0) try: yield from client.get('/test_set') assert False except aio_etcd.EtcdConnectionFailed: pass
def watch_value(key, index, queue): c = aio_etcd.Client(port=6281, loop=loop) n = 0 @asyncio.coroutine def qput(v): nonlocal n yield from queue.put(v.value) n += 1 if n == 3: raise aio_etcd.StopWatching yield from c.eternal_watch(key, qput, index=index) assert n == 3, n
def test_update(loop, self): """INTEGRATION: update a value""" client = aio_etcd.Client(port=6281, loop=loop) yield from client.set('/foo', 3) c = yield from client.get('/foo') c.value = int(c.value) + 3 yield from client.update(c) newres = yield from client.get('/foo') self.assertEquals(newres.value, u'6') try: yield from client.update(c) assert False except ValueError: pass
def connect(self): self.client = None self.client_failed = False self.cont = True etcd_list = get_ticket().etcd if len(etcd_list) == 1: host, port = etcd_list[0].split(':') self.client = etcd.Client( host=host, port=int(port), allow_reconnect=True, allow_redirect=True) else: def split_addr(e): host, port = e.split(':') return host, int(port) host = tuple(split_addr(e) for e in etcd_list) self.client = etcd.Client( host=host, allow_reconnect=True, allow_redirect=True)
def test_reconnect(loop, self): """ INTEGRATION: get key after the server we're connected fails. """ self.processHelper.stop() self.processHelper.run(number=3) yield from asyncio.sleep(0.2,loop=loop) client = aio_etcd.Client(port=6281, allow_reconnect=True, loop=loop) set_result = yield from client.set('/test_set', 'test-key1') get_result = yield from client.get('/test_set') self.assertEquals('test-key1', get_result.value) self.processHelper.kill_one(0) get_result = yield from client.get('/test_set') self.assertEquals('test-key1', get_result.value)
def test_creating_already_existing_directory(loop, self): """ INTEGRATION: creating an already existing directory without `prevExist=True` should fail """ client = aio_etcd.Client(port=6281, loop=loop) yield from client.write('/mydir', None, dir=True) try: yield from client.write('/mydir', None, dir=True) assert False except aio_etcd.EtcdNotFile: pass try: yield from client.write('/mydir', None, dir=True, prevExist=False) assert False except aio_etcd.EtcdAlreadyExist: pass
def test_test_and_set(loop, self): """ INTEGRATION: try test_and_set operation """ client = aio_etcd.Client(port=6281, loop=loop) set_result = yield from client.set('/test-key', 'old-test-value') set_result = yield from client.test_and_set( '/test-key', 'test-value', 'old-test-value', ) try: yield from client.test_and_set('/test-key', 'new-value', 'old-test-value') except ValueError: pass
def test_get_set_unauthenticated(loop, self): """ INTEGRATION: set/get a new value unauthenticated (http->https) """ client = aio_etcd.Client(port=6281, loop=loop) # See above for the reason of this change try: yield from client.set('/test_set', 'test-key') assert False except aio_etcd.EtcdException: pass try: yield from client.get('/test_set') assert False except aio_etcd.EtcdException: pass
def test_get_set_unauthenticated_with_ca(loop, self): """ INTEGRATION: try unauthenticated with validation (https->https)""" client = aio_etcd.Client( protocol='https', port=6281, ca_cert=self.ca2_cert_path, loop=loop) loop = asyncio.get_event_loop() try: yield from client.set('/test-set', 'test-key') assert False except aio_etcd.EtcdConnectionFailed: pass try: yield from client.get('/test-set') assert False except aio_etcd.EtcdConnectionFailed: pass
def test_watch_generator(loop, self): """ INTEGRATION: Receive a watch event from other process (gen) """ client = aio_etcd.Client(port=6281, allow_reconnect=True, loop=loop) set_result = yield from client.set('/test-key', 'test-value') queue = asyncio.Queue(loop=loop) @asyncio.coroutine def change_value(key): yield from asyncio.sleep(0.5, loop=loop) c = aio_etcd.Client(port=6281, loop=loop) for i in range(0, 3): yield from c.set(key, 'test-value%d' % i) yield from c.get(key) @asyncio.coroutine def watch_value(key, queue): c = aio_etcd.Client(port=6281, loop=loop) n = 0 @asyncio.coroutine def qput(event): nonlocal n yield from queue.put(event.value) n += 1 if n == 3: raise aio_etcd.StopWatching yield from c.eternal_watch(key, qput) assert n == 3, n watcher = asyncio.async(watch_value('/test-key', queue), loop=loop) changer = asyncio.async(change_value('/test-key'), loop=loop) values = ['test-value0', 'test-value1', 'test-value2'] for i in range(0, 3): value = yield from queue.get() log.debug("index: %d: %s" % (i, value)) self.assertTrue(value in values) yield from asyncio.wait_for(watcher,timeout=5,loop=loop) yield from asyncio.wait_for(changer,timeout=5,loop=loop)
def test_reconnet_fails(loop, self): """ INTEGRATION: fails to reconnect if no available machines """ self.processHelper.stop() # Start with three instances (0, 1, 2) self.processHelper.run(number=3) # Connect to instance 0 yield from asyncio.sleep(0.2,loop=loop) client = aio_etcd.Client(port=6281, allow_reconnect=True, loop=loop) set_result = yield from client.set('/test_set', 'test-key1') get_result = yield from client.get('/test_set') self.assertEquals('test-key1', get_result.value) self.processHelper.kill_one(2) self.processHelper.kill_one(1) self.processHelper.kill_one(0) try: yield from client.get('/test_set') except aio_etcd.EtcdException: pass
def test_get_set_unauthenticated(loop, self): """ INTEGRATION: set/get a new value unauthenticated (http->https) """ client = aio_etcd.Client(port=6281, loop=loop) # Since python 3 raises a MaxRetryError here, this gets caught in # different code blocks in python 2 and python 3, thus messages are # different. Python 3 does the right thing(TM), for the record try: yield from client.set('/test_set', 'test-key') raise False except aio_etcd.EtcdException: pass try: yield from client.get('/test_set') assert False except aio_etcd.EtcdException: pass
def test_write_and_delete(loop, self): # Create an user u = auth.EtcdUser(self.client, 'test_user') u.roles.add('guest') u.roles.add('root') # directly from my suitcase u.password = '******' try: yield from u.write() except: self.fail("creating a user doesn't work") # Password gets wiped self.assertEquals(u.password, None) yield from u.read() # Verify we can log in as this user and access the auth (it has the # root role) cl = etcd.Client(port=6281, username='******', password='******') ul = auth.EtcdUser(cl, 'root') try: yield from ul.read() except etcd.EtcdInsufficientPermissions: self.fail("Reading auth with the new user is not possible") self.assertEquals(u.name, "test_user") self.assertEquals(u.roles, set(['guest', 'root'])) # Unauthorized access is properly handled ua = auth.EtcdUser(self.unauth_client, 'test_user') with raises(etcd.EtcdInsufficientPermissions): yield from ua.write() # now let's test deletion du = auth.EtcdUser(self.client, 'user.does.not.exist') with raises(etcd.EtcdKeyNotFound): yield from du.delete() # Delete test_user yield from u.delete() with raises(etcd.EtcdKeyNotFound): yield from u.read() # Permissions are properly handled with raises(etcd.EtcdInsufficientPermissions): yield from ua.delete()
def test_watch_indexed_generator(loop, self): """ INTEGRATION: Receive a watch event from other process, ixd, (2) """ client = aio_etcd.Client(port=6281, allow_reconnect=True, loop=loop) set_result = yield from client.set('/test-key', 'test-value') set_result = yield from client.set('/test-key', 'test-value0') original_index = int(set_result.modifiedIndex) set_result = yield from client.set('/test-key', 'test-value1') set_result = yield from client.set('/test-key', 'test-value2') queue = asyncio.Queue(loop=loop) @asyncio.coroutine def change_value(key, newValue): c = aio_etcd.Client(port=6281, loop=loop) yield from c.set(key, newValue) @asyncio.coroutine def watch_value(key, index, queue): c = aio_etcd.Client(port=6281, loop=loop) n = 0 @asyncio.coroutine def qput(v): nonlocal n yield from queue.put(v.value) n += 1 if n == 3: raise aio_etcd.StopWatching yield from c.eternal_watch(key, qput, index=index) assert n == 3, n watcher = asyncio.async(watch_value('/test-key', original_index, queue), loop=loop) yield from asyncio.sleep(0.5, loop=loop) proc = asyncio.async(change_value('/test-key', 'test-value3',), loop=loop) for i in range(0, 3): value = yield from queue.get() log.debug("index: %d: %s" % (i, value)) self.assertEquals('test-value%d' % i, value) yield from asyncio.wait_for(watcher,timeout=5,loop=loop) yield from asyncio.wait_for(proc,timeout=5,loop=loop)
def test_reconnect_with_several_hosts_passed(loop, self): """ INTEGRATION: receive several hosts at connection setup. """ self.processHelper.stop() self.processHelper.run(number=3) yield from asyncio.sleep(0.2,loop=loop) client = aio_etcd.Client( host=( ('127.0.0.1', 6004), ('127.0.0.1', 6281)), allow_reconnect=True, loop=loop) set_result = yield from client.set('/test_set', 'test-key1') get_result = yield from client.get('/test_set') self.assertEquals('test-key1', get_result.value) self.processHelper.kill_one(0) get_result = yield from client.get('/test_set') self.assertEquals('test-key1', get_result.value)
def test_get_set_authenticated(loop, self): """ INTEGRATION: connecting to server with mutual auth """ # This gives an unexplicable ssl error, as connecting to the same # Etcd cluster where this fails with the exact same code this # doesn't fail client = aio_etcd.Client( port=6281, protocol='https', cert=self.client_all_cert, ca_cert=self.ca_cert_path, loop=loop, ) set_result = yield from client.set('/test_set', 'test-key') self.assertEquals(u'set', set_result.action.lower()) self.assertEquals(u'/test_set', set_result.key) self.assertEquals(u'test-key', set_result.value) get_result = yield from client.get('/test_set') self.assertEquals('get', get_result.action.lower()) self.assertEquals('/test_set', get_result.key) self.assertEquals('test-key', get_result.value)
def _connect(self): """Return an etcd client""" # Get sync client receiver while True: try: etcd_client = etcd.Client(self.host, self.port, allow_reconnect=True) # Test connection etcd_client.cluster_version # pylint: disable=W0104 # Save client self.sync_client = etcd_client break except (etcd.EtcdConnectionFailed, urllib3.exceptions.MaxRetryError, etcd.EtcdException): self.logger.warning( "Can not connect to etcd server, retrying in 5 seconds") time.sleep(5) # Get sync client sender while True: try: etcd_sender = etcd.Client(self.host, self.port, allow_reconnect=True) # Test connection etcd_sender.cluster_version # pylint: disable=W0104 # Save client self.sync_sender = etcd_sender break except (etcd.EtcdConnectionFailed, urllib3.exceptions.MaxRetryError, etcd.EtcdException): self.logger.warning( "Can not connect to etcd server, retrying in 5 seconds") time.sleep(5) # Get async etcd client self.async_client = aio_etcd.Client(self.host, self.port, allow_reconnect=True)
def test_watch_indexed(loop, self): """ INTEGRATION: Receive a watch event from other process, indexed """ client = aio_etcd.Client(port=6281, allow_reconnect=True, loop=loop) set_result = yield from client.set('/test-key', 'test-value') set_result = yield from client.set('/test-key', 'test-value0') original_index = int(set_result.modifiedIndex) set_result = yield from client.set('/test-key', 'test-value1') set_result = yield from client.set('/test-key', 'test-value2') queue = asyncio.Queue(loop=loop) @asyncio.coroutine def change_value(key, newValue): c = aio_etcd.Client(port=6281, loop=loop) yield from c.set(key, newValue) yield from c.get(key) @asyncio.coroutine def watch_value(key, index, queue): c = aio_etcd.Client(port=6281, loop=loop) for i in range(0, 3): yield from queue.put((yield from c.watch(key, index=index + i)).value) watcher = asyncio.async(watch_value('/test-key', original_index, queue), loop=loop) yield from asyncio.sleep(0.5, loop=loop) proc = asyncio.async(change_value('/test-key', 'test-value3'), loop=loop) for i in range(0, 3): value = yield from queue.get() log.debug("index: %d: %s" % (i, value)) self.assertEquals('test-value%d' % i, value) yield from asyncio.wait_for(watcher,timeout=5,loop=loop) yield from asyncio.wait_for(proc,timeout=5,loop=loop)
def create_client(self): return etcd.Client(self.host, self.port)