async def watch_compacted_revision_test(): events_iterator, cancel = await etcd.watch(b'/watchcompation', start_revision=1) error_raised = False compacted_revision = 0 try: await events_iterator.__anext__() except Exception as err: error_raised = True assert isinstance( err, aiogrpc_etcd3.exceptions.RevisionCompactedError) compacted_revision = err.compacted_revision assert error_raised is True assert compacted_revision == 2 change_count = 0 events_iterator, cancel = await etcd.watch( b'/watchcompation', start_revision=compacted_revision) async for event in events_iterator: assert event.key == b'/watchcompation' assert event.value == \ utils.to_bytes(str(change_count)) # if cancel worked, we should not receive event 3 assert event.value != utils.to_bytes('3') change_count += 1 if change_count > 2: await cancel()
async def add_callback(self, key, callback, range_end=None, start_revision=None, progress_notify=False, filters=None, prev_kv=False): async with self._watch_id_lock: create_watch = etcdrpc.WatchCreateRequest() create_watch.key = utils.to_bytes(key) if range_end is not None: create_watch.range_end = utils.to_bytes(range_end) if start_revision is not None: create_watch.start_revision = start_revision if progress_notify: create_watch.progress_notify = progress_notify if filters is not None: create_watch.filters = filters if prev_kv: create_watch.prev_kv = prev_kv request = etcdrpc.WatchRequest(create_request=create_watch) self._watch_requests_queue.put_nowait((request, callback)) return await asyncio.wait_for(self._watch_id_queue.get(), self.timeout)
def _build_delete_request(self, key, range_end=None, prev_kv=None): delete_request = DeleteRangeRequest() delete_request.key = utils.to_bytes(key) if range_end is not None: delete_request.range_end = utils.to_bytes(range_end) if prev_kv is not None: delete_request.prev_kv = prev_kv return delete_request
async def get_prefix(self, key_prefix, sort_order=None, sort_target='key'): """ Get a range of keys with a prefix. :param key_prefix: first key in range :returns: sequence of (value, metadata) tuples """ range_request = self._build_get_range_request( key=key_prefix, range_end=utils.increment_last_byte(utils.to_bytes(key_prefix)), sort_order=sort_order, ) range_response = await self.kvstub.Range( range_request, self.timeout, credentials=self.call_credentials, ) if range_response.count < 1: return else: for kv in range_response.kvs: yield (kv.value, KVMetadata(kv))
def _build_get_range_request(self, key, range_end=None, limit=None, revision=None, sort_order=None, sort_target='key', serializable=None, keys_only=False, count_only=False, min_mod_revision=None, max_mod_revision=None, min_create_revision=None, max_create_revision=None): range_request = RangeRequest() range_request.key = utils.to_bytes(key) if range_end is not None: range_request.range_end = utils.to_bytes(range_end) range_request.keys_only = keys_only range_request.count_only = count_only if sort_order is None: range_request.sort_order = RangeRequest.NONE elif sort_order == 'ascend': range_request.sort_order = RangeRequest.ASCEND elif sort_order == 'descend': range_request.sort_order = RangeRequest.DESCEND else: raise ValueError('unknown sort order: "{}"'.format(sort_order)) if sort_target is None or sort_target == 'key': range_request.sort_target = RangeRequest.KEY elif sort_target == 'version': range_request.sort_target = RangeRequest.VERSION elif sort_target == 'create': range_request.sort_target = RangeRequest.CREATE elif sort_target == 'mod': range_request.sort_target = RangeRequest.MOD elif sort_target == 'value': range_request.sort_target = RangeRequest.VALUE else: raise ValueError('sort_target must be one of "key", ' '"version", "create", "mod" or "value"') return range_request
async def watch_prefix_once(self, key_prefix, timeout=None, **kwargs): """ Watches a range of keys with a prefix and stops after the first event. If the timeout was specified and event didn't arrived method will raise ``WatchTimedOut`` exception. """ kwargs['range_end'] = \ utils.increment_last_byte(utils.to_bytes(key_prefix)) return await self.watch_once(key_prefix, timeout=timeout, **kwargs)
async def delete_prefix(self, prefix): """Delete a range of keys with a prefix in etcd.""" delete_request = self._build_delete_request( prefix, range_end=utils.increment_last_byte(utils.to_bytes(prefix))) return await self.kvstub.DeleteRange( delete_request, self.timeout, credentials=self.call_credentials, )
def build_message(self): compare = Compare() compare.key = utils.to_bytes(self.key) if self.op is None: raise ValueError('op must be one of =, < or >') compare.result = self.op self.build_compare(compare) return compare
async def test_watch_prefix(self, etcd): def update_etcd(v): etcdctl(etcd, 'put', '/doot/watch/prefix/' + v, v) out = etcdctl(etcd, 'get', '/doot/watch/prefix/' + v) assert base64.b64decode(out['kvs'][0]['value']) == \ utils.to_bytes(v) def update_key(): # sleep to make watch can get the event time.sleep(3) update_etcd('0') time.sleep(1) update_etcd('1') time.sleep(1) update_etcd('2') time.sleep(1) update_etcd('3') time.sleep(1) t = threading.Thread(name="update_key_prefix", target=update_key) t.start() change_count = 0 events_iterator, cancel = await etcd.watch_prefix('/doot/watch/prefix/' ) async for event in events_iterator: assert event.key == \ utils.to_bytes('/doot/watch/prefix/{}'.format(change_count)) assert event.value == \ utils.to_bytes(str(change_count)) # if cancel worked, we should not receive event 3 assert event.value != utils.to_bytes('3') change_count += 1 if change_count > 2: # if cancel not work, we will block in this for-loop forever await cancel() t.join()
async def test_lease_expire(self, etcd): key = '/doot/lease_test_expire' lease = await etcd.lease(1) await etcd.put(key, 'this is a lease', lease=lease) assert await lease.keys == [utils.to_bytes(key)] v, _ = await etcd.get(key) assert v == b'this is a lease' assert await lease.remaining_ttl <= await lease.granted_ttl # wait for the lease to expire gttl = await lease.granted_ttl await asyncio.sleep(gttl + 2) v, _ = await etcd.get(key) assert v is None
async def get_count(self, key_prefix=None): """ Get count of all keys or keys with specified prefix. :param key_prefix: key_prefix for keys to count :type key_prefix: str or bytes :return: number of keys with specified prefix :rtype: int """ if key_prefix is not None: key = key_prefix range_end = utils.increment_last_byte(utils.to_bytes(key_prefix)) else: key = range_end = b'\0' range_request = self._build_get_range_request(key, range_end, count_only=True) range_response = await self.kvstub.Range( range_request, self.timeout, credentials=self.call_credentials) return range_response.count
async def get_keys(self, key_prefix=None): """ Get keys with specified prefix (all keys if no prefix specified). :param prefix: prefix for keys to count :type prefix: str or bytes :return: async generator of metadata objects """ if key_prefix is not None: key = key_prefix range_end = utils.increment_last_byte(utils.to_bytes(key_prefix)) else: key = range_end = b'\0' range_request = self._build_get_range_request(key, range_end, keys_only=True) range_response = await self.kvstub.Range( range_request, self.timeout, credentials=self.call_credentials) if range_response.count < 1: return else: for kv in range_response.kvs: yield KVMetadata(kv)
async def watch_prefix(self, key_prefix, **kwargs): """Watches a range of keys with a prefix.""" kwargs['range_end'] = \ utils.increment_last_byte(utils.to_bytes(key_prefix)) return await self.watch(key_prefix, **kwargs)
def update_etcd(v): etcdctl(etcd, 'put', '/watchcompation', v) out = etcdctl(etcd, 'get', '/watchcompation') assert base64.b64decode(out['kvs'][0]['value']) == \ utils.to_bytes(v)
def _build_put_request(self, key, value, lease=None): put_request = PutRequest() put_request.key = utils.to_bytes(key) put_request.value = utils.to_bytes(value) put_request.lease = utils.lease_to_id(lease) return put_request
def build_compare(self, compare): compare.target = Compare.VALUE compare.value = utils.to_bytes(self.value)
def update_etcd(v): etcdctl(etcd, 'put', '/doot/watch/prefix/' + v, v) out = etcdctl(etcd, 'get', '/doot/watch/prefix/' + v) assert base64.b64decode(out['kvs'][0]['value']) == \ utils.to_bytes(v)