Example #1
0
def validate_client_submit_response(json):
    if u'error' in json:
        error = Error._parse(json)
        raise error

    if u'header' in json:
        header = Header._parse(json[u'header'])
    else:
        header = None

    responses = []
    for r in json.get(u'responses', []):
        if len(r.keys()) != 1:
            raise Exception(
                'bogus transaction response (multiple response tags in item): {}'
                .format(json))

        first = list(r.keys())[0]

        if first == u'response_put':
            re = Revision._parse(r[u'response_put'])
        elif first == u'response_delete_range':
            re = Deleted._parse(r[u'response_delete_range'])
        elif first == u'response_range':
            re = Range._parse(r[u'response_range'])
        else:
            raise Exception(
                'response item "{}" bogus or not implemented'.format(first))

        responses.append(re)

    return header, responses
Example #2
0
    async def delete(self, key, return_previous=None, timeout=None):
        assembler = commons.DeleteRequestAssembler(self._url, key,
                                                   return_previous)

        obj = await self._post(assembler.url, assembler.data, timeout)

        return Deleted._parse(obj)
Example #3
0
    def delete(self, key, return_previous=None, timeout=None):
        """
        Delete value(s) from etcd.

        :param key: key is the first key to delete in the range.
        :type key: bytes or instance of :class:`txaioetcd.KeySet`

        :param return_previous: If enabled, return the deleted key-value pairs
        :type return_previous: bool or None

        :param timeout: Request timeout in seconds.
        :type timeout: int

        :returns: Deletion info
        :rtype: instance of :class:`txaioetcd.Deleted`
        """
        assembler = commons.DeleteRequestAssembler(self._url, key, return_previous)

        obj = yield self._post(assembler.url, assembler.data, timeout)

        deleted = Deleted._parse(obj)

        returnValue(deleted)
Example #4
0
    def submit(self, txn, timeout=None):
        """
        Submit a transaction.

        Processes multiple requests in a single transaction.
        A transaction increments the revision of the key-value store
        and generates events with the same revision for every
        completed request.

        It is not allowed to modify the same key several times
        within one transaction.

        From google paxosdb paper:

        Our implementation hinges around a powerful primitive which
        we call MultiOp. All other database operations except for
        iteration are implemented as a single call to MultiOp.
        A MultiOp is applied atomically and consists of three components:

        1. A list of tests called guard. Each test in guard checks
           a single entry in the database. It may check for the absence
           or presence of a value, or compare with a given value.
           Two different tests in the guard may apply to the same or
           different entries in the database. All tests in the guard
           are applied and MultiOp returns the results. If all tests
           are true, MultiOp executes t op (see item 2 below), otherwise
           it executes f op (see item 3 below).

        2. A list of database operations called t op. Each operation in
           the list is either an insert, delete, or lookup operation, and
           applies to a single database entry. Two different operations
           in the list may apply to the same or different entries in
           the database. These operations are executed if guard evaluates
           to true.

        3. A list of database operations called f op. Like t op, but
           executed if guard evaluates to false.

        :param txn: The transaction to submit.
        :type txn: instance of :class:`txaioetcd.Transaction`

        :param timeout: Request timeout in seconds.
        :type timeout: int

        :returns: An instance of :class:`txaioetcd.Success` or an exception
            of :class:`txioetcd.Failed` or :class:`txaioetcd.Error`
        :rtype: instance of :class:`txaioetcd.Success`,
            :class:`txaioetcd.Failed` or :class:`txaioetcd.Error`
        """
        url = u'{}/v3alpha/kv/txn'.format(self._url).encode()
        obj = txn._marshal()
        data = json.dumps(obj).encode('utf8')

        response = yield treq.post(url, data, headers=self._REQ_HEADERS, timeout=(timeout or self._timeout))
        obj = yield treq.json_content(response)

        if u'error' in obj:
            error = Error._parse(obj)
            raise error

        if u'header' in obj:
            header = Header._parse(obj[u'header'])
        else:
            header = None

        responses = []
        for r in obj.get(u'responses', []):
            if len(r.keys()) != 1:
                raise Exception('bogus transaction response (multiple response tags in item): {}'.format(obj))

            first = list(r.keys())[0]

            if first == u'response_put':
                re = Revision._parse(r[u'response_put'])
            elif first == u'response_delete_range':
                re = Deleted._parse(r[u'response_delete_range'])
            elif first == u'response_range':
                re = Range._parse(r[u'response_range'])
            else:
                raise Exception('response item "{}" bogus or not implemented'.format(first))

            responses.append(re)

        if obj.get(u'succeeded', False):
            returnValue(Success(header, responses))
        else:
            raise Failed(header, responses)
Example #5
0
    def delete(self, key, return_previous=None, timeout=None):
        """
        Delete value(s) from etcd.

        :param key: key is the first key to delete in the range.
        :type key: bytes or instance of :class:`txaioetcd.KeySet`

        :param return_previous: If enabled, return the deleted key-value pairs
        :type return_previous: bool or None

        :param timeout: Request timeout in seconds.
        :type timeout: int

        :returns: Deletion info
        :rtype: instance of :class:`txaioetcd.Deleted`
        """
        if type(key) == six.binary_type:
            key = KeySet(key)
        elif isinstance(key, KeySet):
            pass
        else:
            raise TypeError('key must either be bytes or a KeySet object, not {}'.format(type(key)))

        if return_previous is not None and type(return_previous) != bool:
            raise TypeError('return_previous must be bool, not {}'.format(type(return_previous)))

        if key.type == KeySet._SINGLE:
            range_end = None
        elif key.type == KeySet._PREFIX:
            range_end = _increment_last_byte(key.key)
        elif key.type == KeySet._RANGE:
            range_end = key.range_end
        else:
            raise Exception('logic error')

        url = u'{}/v3alpha/kv/deleterange'.format(self._url).encode()
        obj = {
            u'key': base64.b64encode(key.key).decode(),
        }
        if range_end:
            # range_end is the key following the last key to delete
            # for the range [key, range_end).
            # If range_end is not given, the range is defined to contain only
            # the key argument.
            # If range_end is one bit larger than the given key, then the range
            # is all keys with the prefix (the given key).
            # If range_end is '\\0', the range is all keys greater
            # than or equal to the key argument.
            #
            obj[u'range_end'] = base64.b64encode(range_end).decode()

        if return_previous:
            # If prev_kv is set, etcd gets the previous key-value pairs
            # before deleting it.
            # The previous key-value pairs will be returned in the
            # delete response.
            #
            obj[u'prev_kv'] = True

        data = json.dumps(obj).encode('utf8')

        response = yield treq.post(url, data, headers=self._REQ_HEADERS, timeout=(timeout or self._timeout))
        obj = yield treq.json_content(response)

        deleted = Deleted._parse(obj)

        returnValue(deleted)