Compare commits

..

No commits in common. "master" and "v0.14.2" have entirely different histories.

94 changed files with 6428 additions and 7581 deletions

View File

@ -1,15 +0,0 @@
BasedOnStyle: Google
DerivePointerAlignment: false
PointerAlignment: Left
Cpp11BracedListStyle: true
IndentCaseLabels: false
AllowShortBlocksOnASingleLine: true
AllowShortLoopsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
Standard: 'Cpp11'
SpaceAfterCStyleCast: true
AlignAfterOpenBracket: Align
SortIncludes: true
IncludeBlocks: Preserve
ForEachMacros:
- BOOST_FOREACH

View File

@ -15,9 +15,16 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-20.04, ubuntu-22.04, macos-11, macos-12] os: [ubuntu-18.04, ubuntu-20.04, ubuntu-22.04, macos-10.15, macos-11, macos-12]
etcd: [v3.2.32, v3.3.27, v3.4.27, v3.5.9] etcd: [v3.2.26, v3.3.11, v3.4.13, v3.5.7]
exclude: exclude:
- os: ubuntu-18.04
etcd: v3.3.11
- os: ubuntu-18.04
etcd: v3.4.13
- os: ubuntu-18.04
etcd: v3.5.7
- os: ubuntu-20.04 - os: ubuntu-20.04
etcd: v3.2.26 etcd: v3.2.26
- os: ubuntu-20.04 - os: ubuntu-20.04
@ -32,6 +39,13 @@ jobs:
- os: ubuntu-22.04 - os: ubuntu-22.04
etcd: v3.5.7 etcd: v3.5.7
- os: macos-10.15
etcd: v3.2.26
- os: macos-10.15
etcd: v3.3.11
- os: macos-10.15
etcd: v3.4.13
- os: macos-11 - os: macos-11
etcd: v3.2.26 etcd: v3.2.26
- os: macos-11 - os: macos-11
@ -82,10 +96,6 @@ jobs:
wget https://github.com/Kitware/CMake/releases/download/v3.19.3/cmake-3.19.3-Linux-x86_64.sh wget https://github.com/Kitware/CMake/releases/download/v3.19.3/cmake-3.19.3-Linux-x86_64.sh
sudo bash cmake-3.19.3-Linux-x86_64.sh --prefix /usr --skip-license sudo bash cmake-3.19.3-Linux-x86_64.sh --prefix /usr --skip-license
# install clang-format
sudo curl -L https://github.com/muttleyxd/clang-tools-static-binaries/releases/download/master-1d7ec53d/clang-format-11_linux-amd64 --output /usr/bin/clang-format
sudo chmod +x /usr/bin/clang-format
- name: Install grpc v1.27.x for Ubuntu 18.04 - name: Install grpc v1.27.x for Ubuntu 18.04
if: matrix.os == 'ubuntu-18.04' if: matrix.os == 'ubuntu-18.04'
run: | run: |
@ -174,7 +184,7 @@ jobs:
if: false if: false
uses: mxschmitt/action-tmate@v3 uses: mxschmitt/action-tmate@v3
- name: CMake - name: Build
run: | run: |
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib/x86_64-linux-gnu export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib/x86_64-linux-gnu
@ -186,42 +196,6 @@ jobs:
-DBUILD_ETCD_TESTS=ON \ -DBUILD_ETCD_TESTS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Format
if: runner.os == 'Linux'
run: |
cd build
function prepend() { while read line; do echo "${1}${line}"; done; }
make etcd_cpp_apiv3_clformat
GIT_DIFF=$(git diff --ignore-submodules)
if [[ -n $GIT_DIFF ]]; then
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
echo "| clang-format failures found!"
echo "|"
echo "$GIT_DIFF" | prepend "| "
echo "|"
echo "| Run: "
echo "|"
echo "| make etcd_cpp_apiv3_clformat"
echo "|"
echo "| to fix this error."
echo "|"
echo "| Ensure you are working with clang-format-11, which can be obtained from"
echo "|"
echo "| https://github.com/muttleyxd/clang-tools-static-binaries/releases "
echo "|"
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
exit -1
fi
- name: Build
run: |
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib/x86_64-linux-gnu
cd build
make -j`nproc` make -j`nproc`
sudo make install sudo make install
@ -243,10 +217,6 @@ jobs:
# tests without auth # tests without auth
echo "Run the etcd resolver test ........................."
# there's no ipv6 on github CI runner
# ./build/bin/EtcdResolverTest
echo "Run the etcd sync test ........................." echo "Run the etcd sync test ........................."
./build/bin/EtcdSyncTest ./build/bin/EtcdSyncTest
@ -403,26 +373,6 @@ jobs:
killall -TERM etcd killall -TERM etcd
sleep 5 sleep 5
- name: Etcd Member test
run: |
killall -TERM etcd || true
sleep 5
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib/x86_64-linux-gnu
# use etcd v3 api
export ETCDCTL_API="3"
rm -rf default.etcd
/usr/local/bin/etcd &
sleep 5
./build/bin/EtcdMemberTest
killall -TERM etcd
sleep 5
- name: Check ccache - name: Check ccache
run: | run: |
ccache --show-stats ccache --show-stats

View File

@ -4,8 +4,8 @@ project (etcd-cpp-api)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(etcd-cpp-api_VERSION_MAJOR 0) set(etcd-cpp-api_VERSION_MAJOR 0)
set(etcd-cpp-api_VERSION_MINOR 15) set(etcd-cpp-api_VERSION_MINOR 14)
set(etcd-cpp-api_VERSION_PATCH 5) set(etcd-cpp-api_VERSION_PATCH 2)
set(etcd-cpp-api_VERSION ${etcd-cpp-api_VERSION_MAJOR}.${etcd-cpp-api_VERSION_MINOR}.${etcd-cpp-api_VERSION_PATCH}) set(etcd-cpp-api_VERSION ${etcd-cpp-api_VERSION_MAJOR}.${etcd-cpp-api_VERSION_MINOR}.${etcd-cpp-api_VERSION_PATCH})
set(CMAKE_PROJECT_HOMEPAGE_URL https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3) set(CMAKE_PROJECT_HOMEPAGE_URL https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3)
@ -26,7 +26,6 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
) )
endif() endif()
option(BUILD_WITH_NO_EXCEPTIONS "Build etcd-cpp-apiv3 with disabling exception handling, i.e., -fno-exceptions" OFF)
option(BUILD_SHARED_LIBS "Build etcd-cpp-apiv3 shared libraries" ON) option(BUILD_SHARED_LIBS "Build etcd-cpp-apiv3 shared libraries" ON)
option(BUILD_ETCD_CORE_ONLY "Build etcd-cpp-apiv3 core library (the synchronous runtime) only" OFF) option(BUILD_ETCD_CORE_ONLY "Build etcd-cpp-apiv3 core library (the synchronous runtime) only" OFF)
option(BUILD_ETCD_TESTS "Build etcd-cpp-apiv3 test cases" OFF) option(BUILD_ETCD_TESTS "Build etcd-cpp-apiv3 test cases" OFF)
@ -89,21 +88,7 @@ macro(use_cxx target)
endif() endif()
endmacro(use_cxx) endmacro(use_cxx)
macro(set_exceptions target) find_package(Boost REQUIRED COMPONENTS system thread random)
if(BUILD_WITH_NO_EXCEPTIONS)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${target} PRIVATE "-fno-exceptions")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(${target} PRIVATE "-fno-exceptions")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_compile_options(${target} PRIVATE "-fno-exceptions")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${target} PRIVATE "/EHs-c-")
endif()
target_compile_definitions(${target} PUBLIC -D_ETCD_NO_EXCEPTIONS)
endif()
endmacro(set_exceptions)
if(APPLE) if(APPLE)
# If we're on OS X check for Homebrew's copy of OpenSSL instead of Apple's # If we're on OS X check for Homebrew's copy of OpenSSL instead of Apple's
if(NOT OpenSSL_DIR) if(NOT OpenSSL_DIR)
@ -122,10 +107,7 @@ if(APPLE)
endif() endif()
find_package(OpenSSL REQUIRED) find_package(OpenSSL REQUIRED)
find_package(Protobuf CONFIG QUIET)
if (NOT Protobuf_FOUND)
find_package(Protobuf REQUIRED) find_package(Protobuf REQUIRED)
endif()
if(Protobuf_PROTOC_EXECUTABLE) if(Protobuf_PROTOC_EXECUTABLE)
if(NOT TARGET protobuf::protoc) if(NOT TARGET protobuf::protoc)
add_executable(protobuf::protoc IMPORTED) add_executable(protobuf::protoc IMPORTED)
@ -136,34 +118,10 @@ if(Protobuf_PROTOC_EXECUTABLE)
endif() endif()
endif() endif()
# When cross compiling we look for the native protoc compiler
# overwrite protobuf::protoc with the proper protoc
if(CMAKE_CROSSCOMPILING)
find_program(Protobuf_PROTOC_EXECUTABLE REQUIRED NAMES protoc)
if(NOT TARGET protobuf::protoc)
add_executable(protobuf::protoc IMPORTED)
endif()
set_target_properties(protobuf::protoc PROPERTIES
IMPORTED_LOCATION "${Protobuf_PROTOC_EXECUTABLE}")
endif()
find_package(gRPC QUIET) find_package(gRPC QUIET)
if(gRPC_FOUND AND TARGET gRPC::grpc) if(gRPC_FOUND)
# When cross compiling we look for the native grpc_cpp_plugin
if(CMAKE_CROSSCOMPILING)
find_program(GRPC_CPP_PLUGIN REQUIRED NAMES grpc_cpp_plugin)
if(NOT TARGET gRPC::grpc_cpp_plugin)
add_executable(gRPC::grpc_cpp_plugin IMPORTED)
endif()
set_target_properties(gRPC::grpc_cpp_plugin PROPERTIES
IMPORTED_LOCATION "${GRPC_CPP_PLUGIN}")
elseif(TARGET gRPC::grpc_cpp_plugin)
get_target_property(GRPC_CPP_PLUGIN gRPC::grpc_cpp_plugin LOCATION)
else()
message(FATAL_ERROR "Found gRPC but no gRPC CPP plugin defined")
endif()
set(GRPC_LIBRARIES gRPC::gpr gRPC::grpc gRPC::grpc++) set(GRPC_LIBRARIES gRPC::gpr gRPC::grpc gRPC::grpc++)
get_target_property(GRPC_CPP_PLUGIN gRPC::grpc_cpp_plugin LOCATION)
get_target_property(GRPC_INCLUDE_DIR gRPC::grpc INTERFACE_INCLUDE_DIRECTORIES) get_target_property(GRPC_INCLUDE_DIR gRPC::grpc INTERFACE_INCLUDE_DIRECTORIES)
else() else()
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindGRPC.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindGRPC.cmake)
@ -182,9 +140,6 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateProtobufGRPC.cmake)
if(gRPC_VERSION VERSION_LESS "1.21" OR gRPC_VERSION VERSION_GREATER "1.31") if(gRPC_VERSION VERSION_LESS "1.21" OR gRPC_VERSION VERSION_GREATER "1.31")
add_definitions(-DWITH_GRPC_CHANNEL_CLASS) add_definitions(-DWITH_GRPC_CHANNEL_CLASS)
endif() endif()
if(gRPC_VERSION VERSION_LESS "1.17")
add_definitions(-DWITH_GRPC_CREATE_CHANNEL_INTERNAL_UNIQUE_POINTER)
endif()
# will set `PROTOBUF_GENERATES`, indicates all generated .cc files, and a target `protobuf_generates`. # will set `PROTOBUF_GENERATES`, indicates all generated .cc files, and a target `protobuf_generates`.
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateProtobuf.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateProtobuf.cmake)
@ -211,7 +166,8 @@ else()
set(CPPREST_LIB) set(CPPREST_LIB)
endif() endif()
include_directories(SYSTEM ${CPPREST_INCLUDE_DIR} include_directories(SYSTEM ${Boost_INCLUDE_DIR}
${CPPREST_INCLUDE_DIR}
${PROTOBUF_INCLUDE_DIRS} ${PROTOBUF_INCLUDE_DIRS}
${GRPC_INCLUDE_DIR} ${GRPC_INCLUDE_DIR}
${OPENSSL_INCLUDE_DIR}) ${OPENSSL_INCLUDE_DIR})
@ -224,13 +180,9 @@ if(NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
endif() endif()
check_cxx_compiler_flag(-Wno-c++17-extensions W_NO_CPP17_EXTENSIONS) check_cxx_compiler_flag(-Wno-c++17-extensions W_NO_CPP17_EXTENSIONS)
check_cxx_compiler_flag(-Wno-extra-semi W_NO_EXTRA_SEMI)
if(W_NO_CPP17_EXTENSIONS) if(W_NO_CPP17_EXTENSIONS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++17-extensions") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++17-extensions")
endif() endif()
if(W_NO_EXTRA_SEMI)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-extra-semi")
endif()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
@ -252,7 +204,6 @@ if(NOT BUILD_ETCD_CORE_ONLY)
endif() endif()
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/etcd/v3/action_constants.hpp install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/etcd/v3/action_constants.hpp
${CMAKE_CURRENT_SOURCE_DIR}/etcd/v3/Transaction.hpp ${CMAKE_CURRENT_SOURCE_DIR}/etcd/v3/Transaction.hpp
${CMAKE_CURRENT_SOURCE_DIR}/etcd/v3/Member.hpp
DESTINATION include/etcd/v3) DESTINATION include/etcd/v3)
configure_file(etcd-cpp-api-config.in.cmake configure_file(etcd-cpp-api-config.in.cmake
@ -271,17 +222,6 @@ install(EXPORT etcd-targets
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/etcd-cpp-api DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/etcd-cpp-api
) )
file(GLOB_RECURSE FILES_NEED_FORMAT
"etcd/*.hpp"
"src/*.cpp"
"tst/*.cpp"
)
add_custom_target(etcd_cpp_apiv3_clformat
COMMAND clang-format --style=file -i ${FILES_NEED_FORMAT}
COMMENT "Running clang-format, using clang-format-11 from https://github.com/muttleyxd/clang-tools-static-binaries/releases"
VERBATIM)
# deb/rpc packaging for Linux # deb/rpc packaging for Linux
set(CPACK_PACKAGE_NAME ${CMAKE_PROJECT_NAME}) set(CPACK_PACKAGE_NAME ${CMAKE_PROJECT_NAME})
@ -338,7 +278,6 @@ set(CPACK_DEBIAN_PACKAGE_INSTALL "/usr/lib/lib*.so*"
"/usr/include/etcd/*.hpp" "/usr/include/etcd/*.hpp"
"/usr/include/etcd/v3/action_constants.hpp" "/usr/include/etcd/v3/action_constants.hpp"
"/usr/include/etcd/v3/Transaction.hpp" "/usr/include/etcd/v3/Transaction.hpp"
"/usr/include/etcd/v3/Member.hpp"
) )
set(CPACK_DEBIAN_PACKAGE_BUILD_NUMBER_PREFIX "") set(CPACK_DEBIAN_PACKAGE_BUILD_NUMBER_PREFIX "")

View File

@ -28,7 +28,7 @@ i.e., `ETCDCTL_API=3`.
## Requirements ## Requirements
1. boost and openssl (**Note that boost is only required if you need the asynchronous runtime**) 1. boost and openssl
+ On Ubuntu, above requirement could be installed as: + On Ubuntu, above requirement could be installed as:
@ -83,36 +83,6 @@ dependencies have been successfully installed:
cmake .. cmake ..
make -j$(nproc) && make install make -j$(nproc) && make install
## Using this package in your CMake project
To use this package in your CMake project, you can either
- install, then find the library using `find_package()`:
```cmake
find_package(etcd-cpp-apiv3 REQUIRED)
target_link_libraries(your_target PRIVATE etcd-cpp-api)
```
- or, add this repository as a subdirectory in your project, and link the library directly:
```cmake
add_subdirectory(thirdparty/etcd-cpp-apiv3)
target_link_libraries(your_target PRIVATE etcd-cpp-api)
```
- or, use [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html):
```cmake
include(FetchContent)
FetchContent_Declare(
etcd-cpp-apiv3
https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3.git
)
FetchContent_MakeAvailable(etcd-cpp-apiv3)
target_link_libraries(your_target PRIVATE etcd-cpp-api)
```
## Compatible etcd version ## Compatible etcd version
The _etcd-cpp-apiv3_ should work well with etcd > 3.0. Feel free to issue an issue to us on The _etcd-cpp-apiv3_ should work well with etcd > 3.0. Feel free to issue an issue to us on
@ -222,14 +192,6 @@ Connecting to multiple endpoints is supported:
etcd::Client etcd("http://a.com:2379;http://b.com:2379;http://c.com:2379"); etcd::Client etcd("http://a.com:2379;http://b.com:2379;http://c.com:2379");
``` ```
### IPv6
Connecting to IPv6 endpoints is supported:
```c++
etcd::Client etcd("http://::1:2379");
```
Behind the screen, gRPC's load balancer is used and the round-robin strategy will Behind the screen, gRPC's load balancer is used and the round-robin strategy will
be used by default. be used by default.
@ -533,11 +495,10 @@ some specific conditions.
Values can be deleted with the `rm` method passing the key to be deleted as a parameter. The key Values can be deleted with the `rm` method passing the key to be deleted as a parameter. The key
should point to an existing value. There are conditional variations for deletion too. should point to an existing value. There are conditional variations for deletion too.
* `rm(std::string const& key)` unconditionally deletes the given key * `rm_if(key, value, old_value)` deletes an already existing value but only if the previous
* `rm_if(key, old_value)` deletes an already existing value but only if the previous
value equals with old_value. If the values does not match returns with "Compare failed" error value equals with old_value. If the values does not match returns with "Compare failed" error
(code `ERROR_COMPARE_FAILED`) (code `ERROR_COMPARE_FAILED`)
* `rm_if(key, old_index)` deletes an already existing value but only if the index of * `rm_if(key, value, old_index)` deletes an already existing value but only if the index of
the previous value equals with old_index. If the indices does not match returns with "Compare the previous value equals with old_index. If the indices does not match returns with "Compare
failed" error (code `ERROR_COMPARE_FAILED`) failed" error (code `ERROR_COMPARE_FAILED`)
@ -562,13 +523,13 @@ keys defined by the prefix. mkdir method is removed since etcdv3 treats everythi
etcd.set("/test/key3", "value3").wait(); etcd.set("/test/key3", "value3").wait();
etcd.set("/test/subdir/foo", "foo").wait(); etcd.set("/test/subdir/foo", "foo").wait();
etcd::Response resp = etcd.ls("/test").get(); etcd::Response resp = etcd.ls("/test/new_dir").get();
``` ```
`resp.key()` will have the following values: ```resp.key()``` will have the following values:
``` ```
/test/key1s /test/key1
/test/key2 /test/key2
/test/key3 /test/key3
/test/subdir/foo /test/subdir/foo
@ -871,8 +832,6 @@ Without handler, the internal state can be checked via `KeepAlive::Check()` and
the async exception when there are errors during keeping the lease alive. the async exception when there are errors during keeping the lease alive.
Note that even with `handler`, the `KeepAlive::Check()` still rethrow if there's an async exception. Note that even with `handler`, the `KeepAlive::Check()` still rethrow if there's an async exception.
When the library is built with `-fno-exceptions`, the `handler` argument and the `Check()` method
will abort the program when there are errors during keeping the lease alive.
### Etcd transactions ### Etcd transactions
@ -893,32 +852,11 @@ Transactions in etcd supports set a set of comparison targets to specify the con
etcdv3::Transaction txn; etcdv3::Transaction txn;
// setup the conditions // setup the conditions
txn.add_compare_value("/test/x1", "1"); txn.reset_key("/test/x1");
txn.add_compare_value("/test/x2", "2"); txn.init_compare("1", etcdv3::CompareResult::EQUAL, etcdv3::CompareTarget::VALUE);
// or, compare the last modified revision txn.reset_key("/test/x2");
txn.add_compare_mod("/test/x3", 0); // not exists txn.init_compare("2", etcdv3::CompareResult::EQUAL, etcdv3::CompareTarget::VALUE);
txn.add_compare_mod("/test/x4", etcdv3::CompareResult::GREATER, 1234); // the modified revision is greater than 1234
```
High-level APIs (e.g., `compare_and_create`, `compare_and_swap`) are also provided, e.g.,
`fetch-and-add` operation can be implemented as
```cpp
auto fetch_and_add = [](etcd::Client& client,
std::string const& key) -> void {
auto value = stoi(client.get(key).get().value().as_string());
while (true) {
auto txn = etcdv3::Transaction();
txn.setup_compare_and_swap(key, std::to_string(value),
std::to_string(value + 1));
etcd::Response resp = client.txn(txn).get();
if (resp.is_ok()) {
break;
}
value = stoi(resp.value().as_string());
}
};
``` ```
See full example of the usages of transaction APIs, please refer to [./tst/TransactionTest.cpp](./tst/TransactionTest.cpp), See full example of the usages of transaction APIs, please refer to [./tst/TransactionTest.cpp](./tst/TransactionTest.cpp),
@ -939,7 +877,7 @@ pplx::task<Response> proclaim(std::string const &name, int64_t lease_id,
pplx::task<Response> leader(std::string const &name); pplx::task<Response> leader(std::string const &name);
std::unique_ptr<SyncClient::Observer> observe(std::string const &name); std::unique_ptr<Observer> observe(std::string const &name);
pplx::task<Response> resign(std::string const &name, int64_t lease_id, pplx::task<Response> resign(std::string const &name, int64_t lease_id,
std::string const &key, int64_t revision); std::string const &key, int64_t revision);
@ -953,7 +891,7 @@ The `Observer` returned by `observe()` can be use to monitor the changes of elec
The observer stream will be canceled when been destructed. The observer stream will be canceled when been destructed.
```c++ ```c++
std::unique_ptr<etcd::SyncClient::Observer> observer = etcd.observe("test"); std::unique_ptr<etcd::Observer> observer = etcd.observe("test");
// wait one change event, blocked execution // wait one change event, blocked execution
etcd::Response resp = observer->WaitOnce(); etcd::Response resp = observer->WaitOnce();
@ -968,17 +906,9 @@ The observer stream will be canceled when been destructed.
observer.reset(nullptr); observer.reset(nullptr);
``` ```
for more details, please refer to [etcd/Client.hpp](./etcd/Client.hpp) and [tst/ElectionTest.cpp](./tst/ElectionTest.cpp). for more details, please refer to [etcd/Client.hpp](./etcd/Client.hpp).
## `-fno-exceptions` ### TODO
The _etcd-cpp-apiv3_ library supports to be built with `-fno-exceptions` flag, controlled by the
cmake option `BUILD_WITH_NO_EXCEPTIONS=ON/OFF` (defaults to `OFF`).
When building with `-fno-exceptions`, the library will abort the program under certain circumstances,
e.g., when calling `.Check()` method of `KeepAlive` and there are errors during keeping the lease alive,
## TODO
1. Cancellation of asynchronous calls(except for watch) 1. Cancellation of asynchronous calls(except for watch)

View File

@ -22,60 +22,50 @@ mark_as_advanced(GRPC_INCLUDE_DIR)
# Find gGPR library # Find gGPR library
find_library(GPR_LIBRARY NAMES gpr) find_library(GPR_LIBRARY NAMES gpr)
mark_as_advanced(GRPC_GPR_LIBRARY) mark_as_advanced(GRPC_GPR_LIBRARY)
if(NOT TARGET gRPC::gpr)
add_library(gRPC::gpr UNKNOWN IMPORTED) add_library(gRPC::gpr UNKNOWN IMPORTED)
set_target_properties(gRPC::gpr PROPERTIES set_target_properties(gRPC::gpr PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR} INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
INTERFACE_LINK_LIBRARIES "-lpthread;-ldl" INTERFACE_LINK_LIBRARIES "-lpthread;-ldl"
IMPORTED_LOCATION ${GPR_LIBRARY} IMPORTED_LOCATION ${GPR_LIBRARY}
) )
endif()
# Find gRPC library # Find gRPC library
find_library(GRPC_LIBRARY NAMES grpc) find_library(GRPC_LIBRARY NAMES grpc)
mark_as_advanced(GRPC_LIBRARY) mark_as_advanced(GRPC_LIBRARY)
if(NOT TARGET gRPC::grpc)
add_library(gRPC::grpc UNKNOWN IMPORTED) add_library(gRPC::grpc UNKNOWN IMPORTED)
set_target_properties(gRPC::grpc PROPERTIES set_target_properties(gRPC::grpc PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR} INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
INTERFACE_LINK_LIBRARIES gRPC::gpr INTERFACE_LINK_LIBRARIES gRPC::gpr
IMPORTED_LOCATION ${GRPC_LIBRARY} IMPORTED_LOCATION ${GRPC_LIBRARY}
) )
endif()
# Find gRPC C++ library # Find gRPC C++ library
find_library(GRPC_GRPC++_LIBRARY NAMES grpc++) find_library(GRPC_GRPC++_LIBRARY NAMES grpc++)
mark_as_advanced(GRPC_GRPC++_LIBRARY) mark_as_advanced(GRPC_GRPC++_LIBRARY)
if(NOT TARGET gRPC::grpc++)
add_library(gRPC::grpc++ UNKNOWN IMPORTED) add_library(gRPC::grpc++ UNKNOWN IMPORTED)
set_target_properties(gRPC::grpc++ PROPERTIES set_target_properties(gRPC::grpc++ PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR} INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
INTERFACE_LINK_LIBRARIES gRPC::grpc INTERFACE_LINK_LIBRARIES gRPC::grpc
IMPORTED_LOCATION ${GRPC_GRPC++_LIBRARY} IMPORTED_LOCATION ${GRPC_GRPC++_LIBRARY}
) )
endif()
# Find gRPC C++ reflection library # Find gRPC C++ reflection library
find_library(GRPC_GRPC++_REFLECTION_LIBRARY NAMES grpc++_reflection) find_library(GRPC_GRPC++_REFLECTION_LIBRARY NAMES grpc++_reflection)
mark_as_advanced(GRPC_GRPC++_REFLECTION_LIBRARY) mark_as_advanced(GRPC_GRPC++_REFLECTION_LIBRARY)
if(NOT TARGET gRPC::grpc++_reflection)
add_library(gRPC::grpc++_reflection UNKNOWN IMPORTED) add_library(gRPC::grpc++_reflection UNKNOWN IMPORTED)
set_target_properties(gRPC::grpc++_reflection PROPERTIES set_target_properties(gRPC::grpc++_reflection PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR} INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
INTERFACE_LINK_LIBRARIES gRPC::grpc++ INTERFACE_LINK_LIBRARIES gRPC::grpc++
IMPORTED_LOCATION ${GRPC_GRPC++_REFLECTION_LIBRARY} IMPORTED_LOCATION ${GRPC_GRPC++_REFLECTION_LIBRARY}
) )
endif()
# Find gRPC CPP generator # Find gRPC CPP generator
find_program(GRPC_CPP_PLUGIN NAMES grpc_cpp_plugin) find_program(GRPC_CPP_PLUGIN NAMES grpc_cpp_plugin)
mark_as_advanced(GRPC_CPP_PLUGIN) mark_as_advanced(GRPC_CPP_PLUGIN)
if(NOT TARGET gRPC::grpc_cpp_plugin)
add_executable(gRPC::grpc_cpp_plugin IMPORTED) add_executable(gRPC::grpc_cpp_plugin IMPORTED)
set_target_properties(gRPC::grpc_cpp_plugin PROPERTIES set_target_properties(gRPC::grpc_cpp_plugin PROPERTIES
IMPORTED_LOCATION ${GRPC_CPP_PLUGIN} IMPORTED_LOCATION ${GRPC_CPP_PLUGIN}
) )
endif()
file( file(
WRITE "${CMAKE_BINARY_DIR}/get_gRPC_version.cc" WRITE "${CMAKE_BINARY_DIR}/get_gRPC_version.cc"

View File

@ -32,8 +32,5 @@ set(ETCD_CPP_INCLUDE_DIRS "${ETCD_CPP_INCLUDE_DIR}")
include(FindPackageMessage) include(FindPackageMessage)
find_package_message(etcd find_package_message(etcd
"Found etcd: ${CMAKE_CURRENT_LIST_FILE} (found version \"@etcd-cpp-api_VERSION@\")" "Found etcd: ${CMAKE_CURRENT_LIST_FILE} (found version \"@etcd-cpp-api_VERSION@\")"
"etcd-cpp-apiv3 version: @etcd-cpp-api_VERSION@\n" "etcd-cpp-apiv3 version: @etcd-cpp-api_VERSION@\netcd-cpp-apiv3 libraries: ${ETCD_CPP_LIBRARIES}, \netcd-cpp-apiv3 core libraries: ${ETCD_CPP_CORE_LIBRARIES}\ninclude directories: ${ETCD_CPP_INCLUDE_DIRS}"
"etcd-cpp-apiv3 libraries: ${ETCD_CPP_LIBRARIES}\n"
"etcd-cpp-apiv3 core libraries: ${ETCD_CPP_CORE_LIBRARIES}\n"
"include directories: ${ETCD_CPP_INCLUDE_DIRS}"
) )

View File

@ -14,24 +14,24 @@
#include "etcd/SyncClient.hpp" #include "etcd/SyncClient.hpp"
#include "etcd/v3/action_constants.hpp" #include "etcd/v3/action_constants.hpp"
namespace etcd { namespace etcd
{
/** /**
* Client is responsible for maintaining a connection towards an etcd server. * Client is responsible for maintaining a connection towards an etcd server.
* Etcd operations can be reached via the methods of the client. * Etcd operations can be reached via the methods of the client.
*/ */
class Client { class Client
{
public: public:
/** /**
* Constructs an async etcd client object from an established synchronous * Constructs an async etcd client object from an established synchronous client.
* client.
* *
* @param sync_client The synchronous client to use for the async client. * @param sync_client The synchronous client to use for the async client.
*/ */
Client(SyncClient *client); Client(SyncClient *client);
/** /**
* Constructs an async etcd client object from an established synchronous * Constructs an async etcd client object from an established synchronous client.
* client.
* *
* @param sync_client The synchronous client to use for the async client. * @param sync_client The synchronous client to use for the async client.
*/ */
@ -40,10 +40,9 @@ class Client {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param load_balancer is the load balance strategy, can be one of * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* round_robin/pick_first/grpclb/xds.
*/ */
Client(std::string const & etcd_url, Client(std::string const & etcd_url,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
@ -51,8 +50,8 @@ class Client {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
*/ */
Client(std::string const & etcd_url, Client(std::string const & etcd_url,
@ -66,10 +65,9 @@ class Client {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param load_balancer is the load balance strategy, can be one of * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* round_robin/pick_first/grpclb/xds.
*/ */
static Client *WithUrl(std::string const & etcd_url, static Client *WithUrl(std::string const & etcd_url,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
@ -77,8 +75,8 @@ class Client {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
*/ */
static Client *WithUrl(std::string const & etcd_url, static Client *WithUrl(std::string const & etcd_url,
@ -92,32 +90,34 @@ class Client {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param username username of etcd auth * @param username username of etcd auth
* @param password password of etcd auth * @param password password of etcd auth
* @param load_balancer is the load balance strategy, can be one of * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* round_robin/pick_first/grpclb/xds. * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd.
* @param auth_token_ttl TTL seconds for auth token, see also
* `--auth-token-ttl` flags of etcd.
*/ */
Client(std::string const& etcd_url, std::string const& username, Client(std::string const & etcd_url,
std::string const& password, int const auth_token_ttl = 300, std::string const & username,
std::string const & password,
int const auth_token_ttl = 300,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param username username of etcd auth * @param username username of etcd auth
* @param password password of etcd auth * @param password password of etcd auth
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
* @param auth_token_ttl TTL seconds for auth token, see also * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd.
* `--auth-token-ttl` flags of etcd. Default value should be 300. * Default value should be 300.
*/ */
Client(std::string const& etcd_url, std::string const& username, Client(std::string const & etcd_url,
std::string const& password, int const auth_token_ttl, std::string const & username,
std::string const & password,
int const auth_token_ttl,
#if defined(WITH_GRPC_CHANNEL_CLASS) #if defined(WITH_GRPC_CHANNEL_CLASS)
grpc::ChannelArguments const & arguments grpc::ChannelArguments const & arguments
#else #else
@ -128,14 +128,12 @@ class Client {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param username username of etcd auth * @param username username of etcd auth
* @param password password of etcd auth * @param password password of etcd auth
* @param load_balancer is the load balance strategy, can be one of * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* round_robin/pick_first/grpclb/xds. * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd.
* @param auth_token_ttl TTL seconds for auth token, see also
* `--auth-token-ttl` flags of etcd.
*/ */
static Client *WithUser(std::string const & etcd_url, static Client *WithUser(std::string const & etcd_url,
std::string const & username, std::string const & username,
@ -146,17 +144,18 @@ class Client {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param username username of etcd auth * @param username username of etcd auth
* @param password password of etcd auth * @param password password of etcd auth
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
* @param auth_token_ttl TTL seconds for auth token, see also * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd.
* `--auth-token-ttl` flags of etcd. Default value should be 300. * Default value should be 300.
*/ */
static Client *WithUser(std::string const & etcd_url, static Client *WithUser(std::string const & etcd_url,
std::string const & username, std::string const & username,
std::string const& password, int const auth_token_ttl, std::string const & password,
int const auth_token_ttl,
#if defined(WITH_GRPC_CHANNEL_CLASS) #if defined(WITH_GRPC_CHANNEL_CLASS)
grpc::ChannelArguments const & arguments grpc::ChannelArguments const & arguments
#else #else
@ -167,35 +166,34 @@ class Client {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param ca root CA file for SSL/TLS connection. * @param ca root CA file for SSL/TLS connection.
* @param cert cert chain file for SSL/TLS authentication, could be empty * @param cert cert chain file for SSL/TLS authentication, could be empty string.
* string. * @param privkey private key file for SSL/TLS authentication, could be empty string.
* @param privkey private key file for SSL/TLS authentication, could be empty * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* string.
* @param load_balancer is the load balance strategy, can be one of
* round_robin/pick_first/grpclb/xds.
*/ */
Client(std::string const& etcd_url, std::string const& ca, Client(std::string const & etcd_url,
std::string const& cert, std::string const& privkey, std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & target_name_override, std::string const & target_name_override,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param ca root CA file for SSL/TLS connection. * @param ca root CA file for SSL/TLS connection.
* @param cert cert chain file for SSL/TLS authentication, could be empty * @param cert cert chain file for SSL/TLS authentication, could be empty string.
* string. * @param privkey private key file for SSL/TLS authentication, could be empty string.
* @param privkey private key file for SSL/TLS authentication, could be empty
* string.
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
*/ */
Client(std::string const& etcd_url, std::string const& ca, Client(std::string const & etcd_url,
std::string const& cert, std::string const& privkey, std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & target_name_override, std::string const & target_name_override,
#if defined(WITH_GRPC_CHANNEL_CLASS) #if defined(WITH_GRPC_CHANNEL_CLASS)
grpc::ChannelArguments const & arguments grpc::ChannelArguments const & arguments
@ -207,20 +205,18 @@ class Client {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param ca root CA file for SSL/TLS connection. * @param ca root CA file for SSL/TLS connection.
* @param cert cert chain file for SSL/TLS authentication, could be empty * @param cert cert chain file for SSL/TLS authentication, could be empty string.
* string. * @param privkey private key file for SSL/TLS authentication, could be empty string.
* @param privkey private key file for SSL/TLS authentication, could be empty * @param target_name_override Override the target host name if you want to pass multiple address
* string. * for load balancing with SSL, and there's no DNS. The @target_name_override@ must exist in the
* @param target_name_override Override the target host name if you want to * SANS of your SSL certificate.
* pass multiple address for load balancing with SSL, and there's no DNS. The * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* @target_name_override@ must exist in the SANS of your SSL certificate.
* @param load_balancer is the load balance strategy, can be one of
* round_robin/pick_first/grpclb/xds.
*/ */
static Client* WithSSL(std::string const& etcd_url, std::string const& ca, static Client *WithSSL(std::string const & etcd_url,
std::string const & ca,
std::string const & cert = "", std::string const & cert = "",
std::string const & privkey = "", std::string const & privkey = "",
std::string const & target_name_override = "", std::string const & target_name_override = "",
@ -229,16 +225,14 @@ class Client {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param ca root CA file for SSL/TLS connection. * @param ca root CA file for SSL/TLS connection.
* @param cert cert chain file for SSL/TLS authentication, could be empty * @param cert cert chain file for SSL/TLS authentication, could be empty string.
* string. * @param privkey private key file for SSL/TLS authentication, could be empty string.
* @param privkey private key file for SSL/TLS authentication, could be empty * @param target_name_override Override the target host name if you want to pass multiple address
* string. * for load balancing with SSL, and there's no DNS. The @target_name_override@ must exist in the
* @param target_name_override Override the target host name if you want to * SANS of your SSL certificate.
* pass multiple address for load balancing with SSL, and there's no DNS. The
* @target_name_override@ must exist in the SANS of your SSL certificate.
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
*/ */
static Client *WithSSL(std::string const & etcd_url, static Client *WithSSL(std::string const & etcd_url,
@ -247,7 +241,8 @@ class Client {
#else #else
grpc_impl::ChannelArguments const & arguments, grpc_impl::ChannelArguments const & arguments,
#endif #endif
std::string const& ca, std::string const& cert = "", std::string const & ca,
std::string const & cert = "",
std::string const & privkey = "", std::string const & privkey = "",
std::string const & target_name_override = ""); std::string const & target_name_override = "");
@ -265,21 +260,29 @@ class Client {
pplx::task<Response> get(std::string const & key); pplx::task<Response> get(std::string const & key);
/** /**
* Get the value of specified key of specified revision from the etcd server * Sets the value of a key. The key will be modified if already exists or created
* @param key is the key to be read * if it does not exists.
* @param revision is the revision of the key to be read * @param key is the key to be created or modified
* @param value is the new value to be set
*/ */
pplx::task<Response> get(std::string const& key, int64_t revision); pplx::task<Response> set(std::string const & key, std::string const & value, int ttl = 0);
/** /**
* Sets the value of a key. The key will be modified if already exists or * Sets the value of a key. The key will be modified if already exists or created
* created if it does not exists. * if it does not exists.
* @param key is the key to be created or modified * @param key is the key to be created or modified
* @param value is the new value to be set * @param value is the new value to be set
* @param leaseId is the lease attached to the key * @param leaseId is the lease attached to the key
*/ */
pplx::task<Response> set(std::string const& key, std::string const& value, pplx::task<Response> set(std::string const & key, std::string const & value, int64_t leaseId);
const int64_t leaseId = 0);
/**
* Creates a new key and sets it's value. Fails if the key already exists.
* @param key is the key to be created
* @param value is the value to be set
*/
pplx::task<Response> add(std::string const & key, std::string const & value, int ttl = 0);
/** /**
* Creates a new key and sets it's value. Fails if the key already exists. * Creates a new key and sets it's value. Fails if the key already exists.
@ -287,8 +290,7 @@ class Client {
* @param value is the value to be set * @param value is the value to be set
* @param leaseId is the lease attached to the key * @param leaseId is the lease attached to the key
*/ */
pplx::task<Response> add(std::string const& key, std::string const& value, pplx::task<Response> add(std::string const & key, std::string const & value, int64_t leaseId);
const int64_t leaseId = 0);
/** /**
* Put a new key-value pair. * Put a new key-value pair.
@ -298,13 +300,11 @@ class Client {
pplx::task<Response> put(std::string const & key, std::string const & value); pplx::task<Response> put(std::string const & key, std::string const & value);
/** /**
* Put a new key-value pair. * Modifies an existing key. Fails if the key does not exists.
* @param key is the key to be put * @param key is the key to be modified
* @param value is the value to be put * @param value is the new value to be set
* @param leaseId is the lease id to be associated with the key
*/ */
pplx::task<Response> put(std::string const& key, std::string const& value, pplx::task<Response> modify(std::string const & key, std::string const & value, int ttl = 0);
const int64_t leaseId);
/** /**
* Modifies an existing key. Fails if the key does not exists. * Modifies an existing key. Fails if the key does not exists.
@ -312,85 +312,81 @@ class Client {
* @param value is the new value to be set * @param value is the new value to be set
* @param leaseId is the lease attached to the key * @param leaseId is the lease attached to the key
*/ */
pplx::task<Response> modify(std::string const& key, std::string const& value, pplx::task<Response> modify(std::string const & key, std::string const & value, int64_t leaseId);
const int64_t leaseId = 0);
/** /**
* Modifies an existing key only if it has a specific value. Fails if the key * Modifies an existing key only if it has a specific value. Fails if the key does not exists
* does not exists or the original value differs from the expected one. * or the original value differs from the expected one.
* @param key is the key to be modified
* @param value is the new value to be set
* @param old_value is the value to be replaced
*/
pplx::task<Response> modify_if(std::string const & key, std::string const & value, std::string const & old_value, int ttl = 0);
/**
* Modifies an existing key only if it has a specific value. Fails if the key does not exists
* or the original value differs from the expected one.
* @param key is the key to be modified * @param key is the key to be modified
* @param value is the new value to be set * @param value is the new value to be set
* @param old_value is the value to be replaced * @param old_value is the value to be replaced
* @param leaseId is the lease attached to the key * @param leaseId is the lease attached to the key
*/ */
pplx::task<Response> modify_if(std::string const& key, pplx::task<Response> modify_if(std::string const & key, std::string const & value, std::string const & old_value, int64_t leaseId);
std::string const& value,
std::string const& old_value,
const int64_t leaseId = 0);
/** /**
* Modifies an existing key only if it has a specific modification index * Modifies an existing key only if it has a specific modification index value. Fails if the key
* value. Fails if the key does not exists or the modification index of the * does not exists or the modification index of the previous value differs from the expected one.
* previous value differs from the expected one. * @param key is the key to be modified
* @param value is the new value to be set
* @param old_index is the expected index of the original value
*/
pplx::task<Response> modify_if(std::string const & key, std::string const & value, int64_t old_index, int ttl = 0);
/**
* Modifies an existing key only if it has a specific modification index value. Fails if the key
* does not exists or the modification index of the previous value differs from the expected one.
* @param key is the key to be modified * @param key is the key to be modified
* @param value is the new value to be set * @param value is the new value to be set
* @param old_index is the expected index of the original value * @param old_index is the expected index of the original value
* @param leaseId is the lease attached to the key * @param leaseId is the lease attached to the key
*/ */
pplx::task<Response> modify_if(std::string const& key, pplx::task<Response> modify_if(std::string const & key, std::string const & value, int64_t old_index, int64_t leaseId);
std::string const& value, int64_t old_index,
const int64_t leaseId = 0);
/** /**
* Removes a single key. The key has to point to a plain, non directory entry. * Removes a single key. The key has to point to a plain, non directory entry.
* @param key is the key to be deleted * @param key is the key to be deleted
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the key does not exist.
*/ */
pplx::task<Response> rm(std::string const & key); pplx::task<Response> rm(std::string const & key);
/** /**
* Removes a single key but only if it has a specific value. Fails if the key * Removes a single key but only if it has a specific value. Fails if the key does not exists
* does not exists or the its value differs from the expected one. * or the its value differs from the expected one.
* @param key is the key to be deleted * @param key is the key to be deleted
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the key does not exist.
*/ */
pplx::task<Response> rm_if(std::string const& key, pplx::task<Response> rm_if(std::string const & key, std::string const & old_value);
std::string const& old_value);
/** /**
* Removes an existing key only if it has a specific modification index value. * Removes an existing key only if it has a specific modification index value. Fails if the key
* Fails if the key does not exists or the modification index of it differs * does not exists or the modification index of it differs from the expected one.
* from the expected one.
* @param key is the key to be deleted * @param key is the key to be deleted
* @param old_index is the expected index of the existing value * @param old_index is the expected index of the existing value
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the key does not exist.
*/ */
pplx::task<Response> rm_if(std::string const & key, int64_t old_index); pplx::task<Response> rm_if(std::string const & key, int64_t old_index);
/** /**
* Removes a directory node. Fails if the parent directory dos not exists or * Removes a directory node. Fails if the parent directory dos not exists or not a directory.
* not a directory.
* @param key is the directory to be created to be listed * @param key is the directory to be created to be listed
* @param recursive if true then delete a whole subtree, otherwise deletes * @param recursive if true then delete a whole subtree, otherwise deletes only an empty directory.
* only an empty directory.
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the no key been deleted.
*/ */
pplx::task<Response> rmdir(std::string const & key, bool recursive = false); pplx::task<Response> rmdir(std::string const & key, bool recursive = false);
/** /**
* Removes multiple keys between [key, range_end). * Removes multiple keys between [key, range_end).
* *
* This overload for `const char *` is to avoid const char * to bool implicit * This overload for `const char *` is to avoid const char * to bool implicit casting.
* casting.
* *
* @param key is the directory to be created to be listed * @param key is the directory to be created to be listed
* @param range_end is the end of key range to be removed. * @param range_end is the end of key range to be removed.
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the no key been deleted.
*/ */
pplx::task<Response> rmdir(std::string const & key, const char *range_end); pplx::task<Response> rmdir(std::string const & key, const char *range_end);
@ -399,11 +395,8 @@ class Client {
* *
* @param key is the directory to be created to be listed * @param key is the directory to be created to be listed
* @param range_end is the end of key range to be removed. * @param range_end is the end of key range to be removed.
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the no key been deleted.
*/ */
pplx::task<Response> rmdir(std::string const& key, pplx::task<Response> rmdir(std::string const & key, std::string const &range_end);
std::string const& range_end);
/** /**
* Gets a directory listing of the directory prefixed by the key. * Gets a directory listing of the directory prefixed by the key.
@ -416,28 +409,14 @@ class Client {
* Gets a directory listing of the directory prefixed by the key. * Gets a directory listing of the directory prefixed by the key.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param limit is the size limit of results to be listed, we don't use * @param limit is the size limit of results to be listed, we don't use default parameters
* default parameters to ensure backwards binary compatibility. 0 means no * to ensure backwards binary compatibility.
* limit.
*/ */
pplx::task<Response> ls(std::string const & key, size_t const limit); pplx::task<Response> ls(std::string const & key, size_t const limit);
/** /**
* Gets a directory listing of the directory prefixed by the key with given * Gets a directory listing of the directory identified by the key and range_end, i.e., get
* revision. * all keys in the range [key, range_end).
*
* @param key is the key to be listed
* @param limit is the size limit of results to be listed, we don't use
* default parameters to ensure backwards binary compatibility. 0 means no
* limit.
* @param revision is the revision to be listed
*/
pplx::task<Response> ls(std::string const& key, size_t const limit,
int64_t revision);
/**
* Gets a directory listing of the directory identified by the key and
* range_end, i.e., get all keys in the range [key, range_end).
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param range_end is the end of key range to be listed * @param range_end is the end of key range to be listed
@ -445,31 +424,15 @@ class Client {
pplx::task<Response> ls(std::string const & key, std::string const &range_end); pplx::task<Response> ls(std::string const & key, std::string const &range_end);
/** /**
* Gets a directory listing of the directory identified by the key and * Gets a directory listing of the directory identified by the key and range_end, i.e., get
* range_end, i.e., get all keys in the range [key, range_end), and respects * all keys in the range [key, range_end), and respects the given limit.
* the given limit.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param range_end is the end of key range to be listed * @param range_end is the end of key range to be listed
* @param limit is the size limit of results to be listed, we don't use * @param limit is the size limit of results to be listed, we don't use default parameters
* default parameters to ensure backwards binary compatibility. * to ensure backwards binary compatibility.
*/ */
pplx::task<Response> ls(std::string const& key, std::string const& range_end, pplx::task<Response> ls(std::string const & key, std::string const &range_end, size_t const limit);
size_t const limit);
/**
* Gets a directory listing of the directory identified by the key and
* range_end, i.e., get all keys in the range [key, range_end), and respects
* the given limit and revision.
*
* @param key is the key to be listed
* @param range_end is the end of key range to be listed
* @param limit is the size limit of results to be listed, we don't use
* default parameters to ensure backwards binary compatibility.
* @param revision is the revision to be listed
*/
pplx::task<Response> ls(std::string const& key, std::string const& range_end,
size_t const limit, int64_t revision);
/** /**
* Gets a directory listing of the directory prefixed by the key. * Gets a directory listing of the directory prefixed by the key.
@ -486,93 +449,57 @@ class Client {
* Note that only keys are included in the response. * Note that only keys are included in the response.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param limit is the size limit of results to be listed, we don't use * @param limit is the size limit of results to be listed, we don't use default parameters
* default parameters to ensure backwards binary compatibility. * to ensure backwards binary compatibility.
*/ */
pplx::task<Response> keys(std::string const & key, size_t const limit); pplx::task<Response> keys(std::string const & key, size_t const limit);
/** /**
* Gets a directory listing of the directory prefixed by the key. * List keys identified by the key and range_end, i.e., get all keys in the range [key,
* * range_end), and respects the given limit.
* Note that only keys are included in the response.
*
* @param key is the key to be listed
* @param limit is the size limit of results to be listed, we don't use
* default parameters to ensure backwards binary compatibility.
* @param revision is the revision to be listed
*/
pplx::task<Response> keys(std::string const& key, size_t const limit,
int64_t revision);
/**
* List keys identified by the key and range_end, i.e., get all keys in the
* range [key, range_end), and respects the given limit.
* *
* Note that only keys are included in the response. * Note that only keys are included in the response.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param range_end is the end of key range to be listed * @param range_end is the end of key range to be listed
*/ */
pplx::task<Response> keys(std::string const& key, pplx::task<Response> keys(std::string const & key, std::string const &range_end);
std::string const& range_end);
/** /**
* List keys identified by the key and range_end, i.e., get all keys in the * List keys identified by the key and range_end, i.e., get all keys in the range [key,
* range [key, range_end). * range_end).
* *
* Note that only keys are included in the response. * Note that only keys are included in the response.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param range_end is the end of key range to be listed * @param range_end is the end of key range to be listed
* @param limit is the size limit of results to be listed, we don't use * @param limit is the size limit of results to be listed, we don't use default parameters
* default parameters to ensure backwards binary compatibility. * to ensure backwards binary compatibility.
*/ */
pplx::task<Response> keys(std::string const& key, pplx::task<Response> keys(std::string const & key, std::string const &range_end, size_t const limit);
std::string const& range_end, size_t const limit);
/** /**
* List keys identified by the key and range_end, i.e., get all keys in the * Watches for changes of a key or a subtree. Please note that if you watch e.g. "/testdir" and
* range [key, range_end). * a new key is created, like "/testdir/newkey" then no change happened in the value of
* * "/testdir" so your watch will not detect this. If you want to detect addition and deletion of
* Note that only keys are included in the response. * directory entries then you have to do a recursive watch.
*
* @param key is the key to be listed
* @param range_end is the end of key range to be listed
* @param limit is the size limit of results to be listed, we don't use
* default parameters to ensure backwards binary compatibility.
* @param revision is the revision to be listed
*/
pplx::task<Response> keys(std::string const& key,
std::string const& range_end, size_t const limit,
int64_t revision);
/**
* Watches for changes of a key or a subtree. Please note that if you watch
* e.g. "/testdir" and a new key is created, like "/testdir/newkey" then no
* change happened in the value of
* "/testdir" so your watch will not detect this. If you want to detect
* addition and deletion of directory entries then you have to do a recursive
* watch.
* @param key is the value or directory to be watched * @param key is the value or directory to be watched
* @param recursive if true watch a whole subtree * @param recursive if true watch a whole subtree
*/ */
pplx::task<Response> watch(std::string const & key, bool recursive = false); pplx::task<Response> watch(std::string const & key, bool recursive = false);
/** /**
* Watches for changes of a key or a subtree from a specific index. The index * Watches for changes of a key or a subtree from a specific index. The index value can be in the "past".
* value can be in the "past".
* @param key is the value or directory to be watched * @param key is the value or directory to be watched
* @param fromIndex the first index we are interested in * @param fromIndex the first index we are interested in
* @param recursive if true watch a whole subtree * @param recursive if true watch a whole subtree
*/ */
pplx::task<Response> watch(std::string const& key, int64_t fromIndex, pplx::task<Response> watch(std::string const & key, int64_t fromIndex, bool recursive = false);
bool recursive = false);
/** /**
* Watches for changes of a range of keys inside [key, range_end). * Watches for changes of a range of keys inside [key, range_end).
* *
* This overload for `const char *` is to avoid const char * to bool implicit * This overload for `const char *` is to avoid const char * to bool implicit casting.
* casting.
* *
* @param key is the value or directory to be watched * @param key is the value or directory to be watched
* @param range_end is the end of key range to be removed. * @param range_end is the end of key range to be removed.
@ -585,21 +512,18 @@ class Client {
* @param key is the value or directory to be watched * @param key is the value or directory to be watched
* @param range_end is the end of key range to be removed. * @param range_end is the end of key range to be removed.
*/ */
pplx::task<Response> watch(std::string const& key, pplx::task<Response> watch(std::string const & key, std::string const &range_end);
std::string const& range_end);
/** /**
* Watches for changes of a range of keys inside [key, range_end) from a * Watches for changes of a range of keys inside [key, range_end) from a specific index. The index value
* specific index. The index value can be in the "past". * can be in the "past".
* *
* Watches for changes of a key or a subtree from a specific index. The index * Watches for changes of a key or a subtree from a specific index. The index value can be in the "past".
* value can be in the "past".
* @param key is the value or directory to be watched * @param key is the value or directory to be watched
* @param range_end is the end of key range to be removed. * @param range_end is the end of key range to be removed.
* @param fromIndex the first index we are interested in * @param fromIndex the first index we are interested in
*/ */
pplx::task<Response> watch(std::string const& key, pplx::task<Response> watch(std::string const & key, std::string const &range_end, int64_t fromIndex);
std::string const& range_end, int64_t fromIndex);
/** /**
* Grants a lease. * Grants a lease.
@ -631,52 +555,30 @@ class Client {
pplx::task<Response> leases(); pplx::task<Response> leases();
/** /**
* Add an etcd member to the etcd cluster, equivalent to `etcdctl member add`. * Gains a lock at a key, using a default created lease, using the default lease (10 seconds), with
* @param peer_urls is comma separated list of URLs for the new member. * keeping alive has already been taken care of by the library.
* @param is_learner is true if the added member is a learner.
*/
pplx::task<Response> add_member(std::string const& peer_urls,
bool is_learner);
/**
* List all members, equivalent to `etcdctl member list`.
*/
pplx::task<Response> list_member();
/**
* Add an etcd member to the etcd cluster, equivalent to `etcdctl member add`.
* @param member_id is the ID of the member to be removed.
*/
pplx::task<Response> remove_member(const uint64_t member_id);
/**
* Gains a lock at a key, using a default created lease, using the default
* lease (10 seconds), with keeping alive has already been taken care of by
* the library.
* @param key is the key to be used to request the lock. * @param key is the key to be used to request the lock.
*/ */
pplx::task<Response> lock(std::string const &key); pplx::task<Response> lock(std::string const &key);
/** /**
* Lock is special, as waiting for lock may cause the thread resources (in the * Lock is special, as waiting for lock may cause the thread resources (in the pplx thread pool) to
* pplx thread pool) to be exhausted. So we need to provide a way to let the * be exhausted. So we need to provide a way to let the user to issue a lock without taking the a
* user to issue a lock without taking the a shared thread. * shared thread.
* *
* This works like what we have in the sync client, but we can issue such * This works like what we have in the sync client, but we can issue such method from the async client
* method from the async client directly. * directly.
* *
* That would be useful when use already issue the lock from a controlled * That would be useful when use already issue the lock from a controlled thread. See more discussion
* thread. See more discussion about the target scenario in * about the target scenario in https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/issues/139
* https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/issues/139
* *
* @param key is the key to be used to request the lock. * @param key is the key to be used to request the lock.
*/ */
pplx::task<Response> lock(std::string const &key, const bool sync); pplx::task<Response> lock(std::string const &key, const bool sync);
/** /**
* Gains a lock at a key, using a default created lease, using the specified * Gains a lock at a key, using a default created lease, using the specified lease TTL (in seconds), with
* lease TTL (in seconds), with keeping alive has already been taken care of * keeping alive has already been taken care of by the library.
* by the library.
* @param key is the key to be used to request the lock. * @param key is the key to be used to request the lock.
* @param lease_ttl is the TTL used to create a lease for the key. * @param lease_ttl is the TTL used to create a lease for the key.
*/ */
@ -685,22 +587,19 @@ class Client {
/** /**
* Lock, but the sync version. * Lock, but the sync version.
*/ */
pplx::task<Response> lock(std::string const& key, int lease_ttl, pplx::task<Response> lock(std::string const &key, int lease_ttl, const bool sync);
const bool sync);
/** /**
* Gains a lock at a key, using a user-provided lease, the lifetime of the * Gains a lock at a key, using a user-provided lease, the lifetime of the lease won't be taken care
* lease won't be taken care of by the library. * of by the library.
* @param key is the key to be used to request the lock. * @param key is the key to be used to request the lock.
*/ */
pplx::task<Response> lock_with_lease(std::string const& key, pplx::task<Response> lock_with_lease(std::string const &key, int64_t lease_id);
int64_t lease_id);
/** /**
* Lock, but the sync version. * Lock, but the sync version.
*/ */
pplx::task<Response> lock_with_lease(std::string const& key, int64_t lease_id, pplx::task<Response> lock_with_lease(std::string const &key, int64_t lease_id, const bool sync);
const bool sync);
/** /**
* Releases a lock at a key. * Releases a lock at a key.
@ -742,8 +641,7 @@ class Client {
* @param value is the new value to set. * @param value is the new value to set.
*/ */
pplx::task<Response> proclaim(std::string const &name, int64_t lease_id, pplx::task<Response> proclaim(std::string const &name, int64_t lease_id,
std::string const& key, int64_t revision, std::string const &key, int64_t revision, std::string const &value);
std::string const& value);
/** /**
* Get the current leader proclamation. * Get the current leader proclamation.
@ -761,8 +659,7 @@ class Client {
* *
* @param name is the names of election to watch. * @param name is the names of election to watch.
* *
* @returns an observer that holds that action and will cancel the request * @returns an observer that holds that action and will cancel the request when being destructed.
* when being destructed.
*/ */
std::unique_ptr<Observer> observe(std::string const &name); std::unique_ptr<Observer> observe(std::string const &name);
@ -796,8 +693,7 @@ class Client {
* Set a timeout value for grpc operations. * Set a timeout value for grpc operations.
*/ */
template <typename Period = std::micro> template <typename Period = std::micro>
void set_grpc_timeout(std::chrono::duration<std::chrono::microseconds::rep, void set_grpc_timeout(std::chrono::duration<std::chrono::microseconds::rep, Period> const &timeout) {
Period> const& timeout) {
this->client->set_grpc_timeout(timeout); this->client->set_grpc_timeout(timeout);
} }
@ -818,6 +714,6 @@ class Client {
SyncClient *client = nullptr; SyncClient *client = nullptr;
}; };
} // namespace etcd }
#endif #endif

View File

@ -3,50 +3,66 @@
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <condition_variable>
#include <exception> #include <exception>
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include "etcd/Response.hpp"
#include "etcd/SyncClient.hpp" #include "etcd/SyncClient.hpp"
#include "etcd/Response.hpp"
namespace etcd { #include <boost/config.hpp>
#if BOOST_VERSION >= 106600
#include <boost/asio/io_context.hpp>
#else
#include <boost/asio/io_service.hpp>
#endif
#include <boost/asio/steady_timer.hpp>
namespace etcd
{
// forward declaration to avoid header/library dependency // forward declaration to avoid header/library dependency
class Client; class Client;
/** /**
* If ID is set to 0, the library will choose an ID, and can be accessed from * If ID is set to 0, the library will choose an ID, and can be accessed from ".Lease()".
* ".Lease()".
*/ */
class KeepAlive { class KeepAlive
{
public: public:
KeepAlive(Client const& client, int ttl, int64_t lease_id = 0); KeepAlive(Client const &client,
KeepAlive(SyncClient const& client, int ttl, int64_t lease_id = 0); int ttl, int64_t lease_id = 0);
KeepAlive(std::string const& address, int ttl, int64_t lease_id = 0); KeepAlive(SyncClient const &client,
KeepAlive(std::string const& address, std::string const& username, int ttl, int64_t lease_id = 0);
std::string const& password, int ttl, int64_t lease_id = 0, KeepAlive(std::string const & address,
int ttl, int64_t lease_id = 0);
KeepAlive(std::string const & address,
std::string const & username, std::string const & password,
int ttl, int64_t lease_id = 0,
int const auth_token_ttl = 300); int const auth_token_ttl = 300);
KeepAlive(Client const &client, KeepAlive(Client const &client,
std::function<void(std::exception_ptr)> const& handler, int ttl, std::function<void (std::exception_ptr)> const &handler,
int64_t lease_id = 0); int ttl, int64_t lease_id = 0);
KeepAlive(SyncClient const &client, KeepAlive(SyncClient const &client,
std::function<void(std::exception_ptr)> const& handler, int ttl, std::function<void (std::exception_ptr)> const &handler,
int64_t lease_id = 0); int ttl, int64_t lease_id = 0);
KeepAlive(std::string const & address, KeepAlive(std::string const & address,
std::function<void(std::exception_ptr)> const& handler, int ttl, std::function<void (std::exception_ptr)> const &handler,
int64_t lease_id = 0); int ttl, int64_t lease_id = 0);
KeepAlive(std::string const& address, std::string const& username, KeepAlive(std::string const & address,
std::string const& password, std::string const & username, std::string const & password,
std::function<void(std::exception_ptr)> const& handler, int ttl, std::function<void (std::exception_ptr)> const &handler,
int64_t lease_id = 0, int const auth_token_ttl = 300); int ttl, int64_t lease_id = 0,
KeepAlive(std::string const& address, std::string const& ca, int const auth_token_ttl = 300);
std::string const& cert, std::string const& privkey, KeepAlive(std::string const & address,
std::function<void(std::exception_ptr)> const& handler, int ttl, std::string const & ca,
int64_t lease_id = 0, std::string const& target_name_override = ""); std::string const & cert,
std::string const & privkey,
std::function<void (std::exception_ptr)> const &handler,
int ttl, int64_t lease_id = 0,
std::string const & target_name_override = "");
KeepAlive(KeepAlive const &) = delete; KeepAlive(KeepAlive const &) = delete;
KeepAlive(KeepAlive &&) = delete; KeepAlive(KeepAlive &&) = delete;
@ -59,8 +75,7 @@ class KeepAlive {
void Cancel(); void Cancel();
/** /**
* Check if the keep alive is still valid (invalid when there's an async * Check if the keep alive is still valid (invalid when there's an async exception).
* exception).
* *
* Nothing will happen if valid and an exception will be rethrowed if invalid. * Nothing will happen if valid and an exception will be rethrowed if invalid.
*/ */
@ -71,8 +86,7 @@ class KeepAlive {
*/ */
template <typename Rep = std::micro> template <typename Rep = std::micro>
void set_grpc_timeout(std::chrono::duration<Rep> const &timeout) { void set_grpc_timeout(std::chrono::duration<Rep> const &timeout) {
this->grpc_timeout = this->grpc_timeout = std::chrono::duration_cast<std::chrono::milliseconds>(timeout);
std::chrono::duration_cast<std::chrono::milliseconds>(timeout);
} }
/** /**
@ -85,10 +99,10 @@ class KeepAlive {
~KeepAlive(); ~KeepAlive();
protected: protected:
// automatically refresh loop, returns the error message if failed // automatically refresh loop
std::string refresh(); void refresh();
// refresh once immediately, returns the error message if failed // refresh once immediately
std::string refresh_once(); void refresh_once();
struct EtcdServerStubs; struct EtcdServerStubs;
struct EtcdServerStubsDeleter { struct EtcdServerStubsDeleter {
@ -101,23 +115,27 @@ class KeepAlive {
std::exception_ptr eptr_; std::exception_ptr eptr_;
std::function<void (std::exception_ptr)> handler_; std::function<void (std::exception_ptr)> handler_;
// Don't use `pplx::task` to avoid sharing thread pool with other actions on // Don't use `pplx::task` to avoid sharing thread pool with other actions on the client
// the client to avoid any potential blocking, which may block the keepalive // to avoid any potential blocking, which may block the keepalive loop and evict the lease.
// loop and evict the lease. std::thread task_;
std::thread refresh_task_;
int ttl; int ttl;
int64_t lease_id; int64_t lease_id;
// protect the initializing status of `timer`. // protect the initializing status of `timer`.
std::mutex mutex_for_refresh_; std::recursive_mutex mutex_for_refresh_;
std::condition_variable cv_for_refresh_;
std::atomic_bool continue_next; std::atomic_bool continue_next;
// grpc timeout in `refresh()` // grpc timeout in `refresh()`
mutable std::chrono::microseconds grpc_timeout = mutable std::chrono::microseconds grpc_timeout = std::chrono::microseconds::zero();
std::chrono::microseconds::zero();
#if BOOST_VERSION >= 106600
boost::asio::io_context context;
#else
boost::asio::io_service context;
#endif
std::unique_ptr<boost::asio::steady_timer> keepalive_timer_;
}; };
} // namespace etcd }
#endif #endif

View File

@ -2,23 +2,23 @@
#define __ETCD_RESPONSE_HPP__ #define __ETCD_RESPONSE_HPP__
#include <chrono> #include <chrono>
#include <functional>
#include <iostream> #include <iostream>
#include <functional>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "etcd/Value.hpp" #include "etcd/Value.hpp"
#include "etcd/v3/Member.hpp"
namespace etcdv3 { namespace etcdv3 {
class AsyncWatchAction; class AsyncWatchAction;
class AsyncLeaseKeepAliveAction; class AsyncLeaseKeepAliveAction;
class AsyncObserveAction; class AsyncObserveAction;
class V3Response; class V3Response;
} // namespace etcdv3 }
namespace etcd { namespace etcd
{
typedef std::vector<std::string> Keys; typedef std::vector<std::string> Keys;
namespace detail { namespace detail {
@ -28,60 +28,62 @@ inline const std::chrono::microseconds duration_till_now(
return std::chrono::duration_cast<std::chrono::microseconds>( return std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now() - start_timepoint); std::chrono::high_resolution_clock::now() - start_timepoint);
} }
} // namespace detail }
// forward declaration // forward declaration
class KeepAlive; class KeepAlive;
class Watcher; class Watcher;
/** /**
* The Response object received for the requests of etcd::Client * The Reponse object received for the requests of etcd::Client
*/ */
class Response { class Response
{
public: public:
template <typename T> template <typename T>
static etcd::Response create(std::unique_ptr<T> call) { static etcd::Response create(std::unique_ptr<T> call)
{
call->waitForResponse(); call->waitForResponse();
auto v3resp = call->ParseResponse(); auto v3resp = call->ParseResponse();
return etcd::Response(v3resp, return etcd::Response(v3resp, detail::duration_till_now(call->startTimepoint()));
detail::duration_till_now(call->startTimepoint()));
} }
template <typename T> template <typename T>
static etcd::Response create(std::shared_ptr<T> call) { static etcd::Response create(std::shared_ptr<T> call)
{
call->waitForResponse(); call->waitForResponse();
auto v3resp = call->ParseResponse(); auto v3resp = call->ParseResponse();
return etcd::Response(v3resp, return etcd::Response(v3resp, detail::duration_till_now(call->startTimepoint()));
detail::duration_till_now(call->startTimepoint()));
} }
template <typename T> template <typename T>
static etcd::Response create(std::unique_ptr<T> call, static etcd::Response create(std::unique_ptr<T> call,
std::function<void(Response)> callback) { std::function<void(Response)> callback)
{
call->waitForResponse(callback); call->waitForResponse(callback);
auto v3resp = call->ParseResponse(); auto v3resp = call->ParseResponse();
return etcd::Response(v3resp, return etcd::Response(v3resp, detail::duration_till_now(call->startTimepoint()));
detail::duration_till_now(call->startTimepoint()));
} }
template <typename T> template <typename T>
static etcd::Response create(std::function<std::unique_ptr<T>()> fn) { static etcd::Response create(std::function<std::unique_ptr<T>()> fn)
{
auto call = fn(); auto call = fn();
call->waitForResponse(); call->waitForResponse();
auto v3resp = call->ParseResponse(); auto v3resp = call->ParseResponse();
return etcd::Response(v3resp, return etcd::Response(v3resp, detail::duration_till_now(call->startTimepoint()));
detail::duration_till_now(call->startTimepoint()));
} }
template <typename T> template <typename T>
static etcd::Response create(std::function<std::shared_ptr<T>()> fn) { static etcd::Response create(std::function<std::shared_ptr<T>()> fn)
{
auto call = fn(); auto call = fn();
call->waitForResponse(); call->waitForResponse();
auto v3resp = call->ParseResponse(); auto v3resp = call->ParseResponse();
return etcd::Response(v3resp, return etcd::Response(v3resp, detail::duration_till_now(call->startTimepoint()));
detail::duration_till_now(call->startTimepoint()));
} }
Response(); Response();
@ -89,8 +91,7 @@ class Response {
Response(const Response &); Response(const Response &);
/** /**
* Returns the error code received from the etcd server. In case of success * Returns the error code received from the etcd server. In case of success the error code is 0.
* the error code is 0.
*/ */
int error_code() const; int error_code() const;
@ -130,20 +131,17 @@ class Response {
Value const & value() const; Value const & value() const;
/** /**
* Returns the previous value object of the response to a set/modify/rm * Returns the previous value object of the response to a set/modify/rm operation.
* operation.
*/ */
Value const & prev_value() const; Value const & prev_value() const;
/** /**
* Returns the index-th value of the response to an 'ls' operation. Equivalent * Returns the index-th value of the response to an 'ls' operation. Equivalent to values()[index]
* to values()[index]
*/ */
Value const & value(int index) const; Value const & value(int index) const;
/** /**
* Returns the vector of values in a directory in response to an 'ls' * Returns the vector of values in a directory in response to an 'ls' operation.
* operation.
*/ */
Values const & values() const; Values const & values() const;
@ -163,12 +161,6 @@ class Response {
*/ */
int64_t compact_revision() const; int64_t compact_revision() const;
/**
* Returns the watcher id for client.watch() requests. `-1` means
* uninitialized (the response is not for watch).
*/
int64_t watch_id() const;
/** /**
* Returns the lock key. * Returns the lock key.
*/ */
@ -209,14 +201,8 @@ class Response {
*/ */
std::vector<int64_t> const & leases() const; std::vector<int64_t> const & leases() const;
/**
* Returns the member list.
*/
std::vector<etcdv3::Member> const& members() const;
protected: protected:
Response(const etcdv3::V3Response& response, Response(const etcdv3::V3Response& response, std::chrono::microseconds const& duration);
std::chrono::microseconds const& duration);
Response(int error_code, std::string const& error_message); Response(int error_code, std::string const& error_message);
Response(int error_code, char const * error_message); Response(int error_code, char const * error_message);
@ -229,12 +215,10 @@ class Response {
Values _values; Values _values;
Keys _keys; Keys _keys;
int64_t _compact_revision = -1; // for watch int64_t _compact_revision = -1; // for watch
int64_t _watch_id = -1; // for watch
std::string _lock_key; // for lock std::string _lock_key; // for lock
std::string _name; // for campaign (in v3election) std::string _name; // for campaign (in v3election)
std::vector<Event> _events; // for watch std::vector<Event> _events; // for watch
// execute duration (in microseconds), during the action created and response // execute duration (in microseconds), during the action created and response parsed
// parsed
std::chrono::microseconds _duration; std::chrono::microseconds _duration;
// cluster metadata // cluster metadata
@ -245,9 +229,6 @@ class Response {
// for lease list // for lease list
std::vector<int64_t> _leases; std::vector<int64_t> _leases;
// for member list
std::vector<etcdv3::Member> _members;
friend class Client; friend class Client;
friend class SyncClient; friend class SyncClient;
friend class KeepAlive; friend class KeepAlive;
@ -257,6 +238,6 @@ class Response {
friend class etcdv3::AsyncLeaseKeepAliveAction; friend class etcdv3::AsyncLeaseKeepAliveAction;
friend class etcdv3::AsyncObserveAction; friend class etcdv3::AsyncObserveAction;
}; };
} // namespace etcd }
#endif #endif

View File

@ -29,9 +29,6 @@ class AsyncLeaseKeepAliveAction;
class AsyncLeaseTimeToLiveAction; class AsyncLeaseTimeToLiveAction;
class AsyncLeaseLeasesAction; class AsyncLeaseLeasesAction;
class AsyncLockAction; class AsyncLockAction;
class AsyncAddMemberAction;
class AsyncListMemberAction;
class AsyncRemoveMemberAction;
class AsyncUnlockAction; class AsyncUnlockAction;
class AsyncPutAction; class AsyncPutAction;
class AsyncRangeAction; class AsyncRangeAction;
@ -46,26 +43,27 @@ class Transaction;
namespace detail { namespace detail {
std::string string_plus_one(std::string const &value); std::string string_plus_one(std::string const &value);
std::string resolve_etcd_endpoints(std::string const &default_endpoints); std::string resolve_etcd_endpoints(std::string const &default_endpoints);
} // namespace detail }
} // namespace etcdv3 }
#if defined(WITH_GRPC_CHANNEL_CLASS) #if defined(WITH_GRPC_CHANNEL_CLASS)
namespace grpc { namespace grpc {
class Channel; class Channel;
class ChannelArguments; class ChannelArguments;
} // namespace grpc }
#else #else
namespace grpc_impl { namespace grpc_impl {
class Channel; class Channel;
class ChannelArguments; class ChannelArguments;
} // namespace grpc_impl }
#endif #endif
namespace etcd { namespace etcd
using etcdv3::ERROR_ACTION_CANCELLED; {
using etcdv3::ERROR_KEY_NOT_FOUND;
using etcdv3::ERROR_COMPARE_FAILED; using etcdv3::ERROR_COMPARE_FAILED;
using etcdv3::ERROR_KEY_ALREADY_EXISTS; using etcdv3::ERROR_KEY_ALREADY_EXISTS;
using etcdv3::ERROR_KEY_NOT_FOUND; using etcdv3::ERROR_ACTION_CANCELLED;
class KeepAlive; class KeepAlive;
class Watcher; class Watcher;
@ -76,16 +74,15 @@ class Client;
* Etcd operations can be reached via the methods of the client. * Etcd operations can be reached via the methods of the client.
*/ */
/** /**
* SyncClient is a wrapper around etcd::Client and provides a simplified sync * SyncClient is a wrapper around etcd::Client and provides a simplified sync interface with blocking operations.
* interface with blocking operations.
* *
* In case of any exceptions occur in the backend the Response value's * In case of any exceptions occur in the backend the Response value's error_message will contain the
* error_message will contain the text of the exception and the error code will * text of the exception and the error code will be 600
* be 600
* *
* Use this class only if performance does not matter. * Use this class only if performance does not matter.
*/ */
class SyncClient { class SyncClient
{
private: private:
class TokenAuthenticator; class TokenAuthenticator;
class TokenAuthenticatorDeleter { class TokenAuthenticatorDeleter {
@ -97,10 +94,9 @@ class SyncClient {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param load_balancer is the load balance strategy, can be one of * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* round_robin/pick_first/grpclb/xds.
*/ */
SyncClient(std::string const & etcd_url, SyncClient(std::string const & etcd_url,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
@ -108,8 +104,8 @@ class SyncClient {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
*/ */
SyncClient(std::string const & etcd_url, SyncClient(std::string const & etcd_url,
@ -123,10 +119,9 @@ class SyncClient {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param load_balancer is the load balance strategy, can be one of * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* round_robin/pick_first/grpclb/xds.
*/ */
static SyncClient *WithUrl(std::string const & etcd_url, static SyncClient *WithUrl(std::string const & etcd_url,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
@ -134,8 +129,8 @@ class SyncClient {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
*/ */
static SyncClient *WithUrl(std::string const & etcd_url, static SyncClient *WithUrl(std::string const & etcd_url,
@ -149,32 +144,34 @@ class SyncClient {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param username username of etcd auth * @param username username of etcd auth
* @param password password of etcd auth * @param password password of etcd auth
* @param load_balancer is the load balance strategy, can be one of * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* round_robin/pick_first/grpclb/xds. * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd.
* @param auth_token_ttl TTL seconds for auth token, see also
* `--auth-token-ttl` flags of etcd.
*/ */
SyncClient(std::string const& etcd_url, std::string const& username, SyncClient(std::string const & etcd_url,
std::string const& password, int const auth_token_ttl = 300, std::string const & username,
std::string const & password,
int const auth_token_ttl = 300,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param username username of etcd auth * @param username username of etcd auth
* @param password password of etcd auth * @param password password of etcd auth
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
* @param auth_token_ttl TTL seconds for auth token, see also * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd.
* `--auth-token-ttl` flags of etcd. Default value should be 300. * Default value should be 300.
*/ */
SyncClient(std::string const& etcd_url, std::string const& username, SyncClient(std::string const & etcd_url,
std::string const& password, int const auth_token_ttl, std::string const & username,
std::string const & password,
int const auth_token_ttl,
#if defined(WITH_GRPC_CHANNEL_CLASS) #if defined(WITH_GRPC_CHANNEL_CLASS)
grpc::ChannelArguments const & arguments grpc::ChannelArguments const & arguments
#else #else
@ -182,17 +179,16 @@ class SyncClient {
#endif #endif
); );
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param username username of etcd auth * @param username username of etcd auth
* @param password password of etcd auth * @param password password of etcd auth
* @param load_balancer is the load balance strategy, can be one of * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* round_robin/pick_first/grpclb/xds. * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd.
* @param auth_token_ttl TTL seconds for auth token, see also
* `--auth-token-ttl` flags of etcd.
*/ */
static SyncClient *WithUser(std::string const & etcd_url, static SyncClient *WithUser(std::string const & etcd_url,
std::string const & username, std::string const & username,
@ -203,13 +199,13 @@ class SyncClient {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param username username of etcd auth * @param username username of etcd auth
* @param password password of etcd auth * @param password password of etcd auth
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
* @param auth_token_ttl TTL seconds for auth token, see also * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd.
* `--auth-token-ttl` flags of etcd. Default value should be 300. * Default value should be 300.
*/ */
static SyncClient *WithUser(std::string const & etcd_url, static SyncClient *WithUser(std::string const & etcd_url,
std::string const & username, std::string const & username,
@ -222,38 +218,38 @@ class SyncClient {
#endif #endif
); );
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param ca root CA file for SSL/TLS connection. * @param ca root CA file for SSL/TLS connection.
* @param cert cert chain file for SSL/TLS authentication, could be empty * @param cert cert chain file for SSL/TLS authentication, could be empty string.
* string. * @param privkey private key file for SSL/TLS authentication, could be empty string.
* @param privkey private key file for SSL/TLS authentication, could be empty * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* string.
* @param load_balancer is the load balance strategy, can be one of
* round_robin/pick_first/grpclb/xds.
*/ */
SyncClient(std::string const& etcd_url, std::string const& ca, SyncClient(std::string const & etcd_url,
std::string const& cert, std::string const& privkey, std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & target_name_override, std::string const & target_name_override,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param ca root CA file for SSL/TLS connection. * @param ca root CA file for SSL/TLS connection.
* @param cert cert chain file for SSL/TLS authentication, could be empty * @param cert cert chain file for SSL/TLS authentication, could be empty string.
* string. * @param privkey private key file for SSL/TLS authentication, could be empty string.
* @param privkey private key file for SSL/TLS authentication, could be empty
* string.
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
*/ */
SyncClient(std::string const& etcd_url, std::string const& ca, SyncClient(std::string const & etcd_url,
std::string const& cert, std::string const& privkey, std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & target_name_override, std::string const & target_name_override,
#if defined(WITH_GRPC_CHANNEL_CLASS) #if defined(WITH_GRPC_CHANNEL_CLASS)
grpc::ChannelArguments const & arguments grpc::ChannelArguments const & arguments
@ -262,23 +258,22 @@ class SyncClient {
#endif #endif
); );
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param ca root CA file for SSL/TLS connection. * @param ca root CA file for SSL/TLS connection.
* @param cert cert chain file for SSL/TLS authentication, could be empty * @param cert cert chain file for SSL/TLS authentication, could be empty string.
* string. * @param privkey private key file for SSL/TLS authentication, could be empty string.
* @param privkey private key file for SSL/TLS authentication, could be empty * @param target_name_override Override the target host name if you want to pass multiple address
* string. * for load balancing with SSL, and there's no DNS. The @target_name_override@ must exist in the
* @param target_name_override Override the target host name if you want to * SANS of your SSL certificate.
* pass multiple address for load balancing with SSL, and there's no DNS. The * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds.
* @target_name_override@ must exist in the SANS of your SSL certificate.
* @param load_balancer is the load balance strategy, can be one of
* round_robin/pick_first/grpclb/xds.
*/ */
static SyncClient* WithSSL(std::string const& etcd_url, std::string const& ca, static SyncClient *WithSSL(std::string const & etcd_url,
std::string const & ca,
std::string const & cert = "", std::string const & cert = "",
std::string const & privkey = "", std::string const & privkey = "",
std::string const & target_name_override = "", std::string const & target_name_override = "",
@ -287,16 +282,14 @@ class SyncClient {
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
* @param etcd_url is the url of the etcd server to connect to, like * @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* "http://127.0.0.1:2379", or multiple url, separated by ',' or ';'. * or multiple url, separated by ',' or ';'.
* @param ca root CA file for SSL/TLS connection. * @param ca root CA file for SSL/TLS connection.
* @param cert cert chain file for SSL/TLS authentication, could be empty * @param cert cert chain file for SSL/TLS authentication, could be empty string.
* string. * @param privkey private key file for SSL/TLS authentication, could be empty string.
* @param privkey private key file for SSL/TLS authentication, could be empty * @param target_name_override Override the target host name if you want to pass multiple address
* string. * for load balancing with SSL, and there's no DNS. The @target_name_override@ must exist in the
* @param target_name_override Override the target host name if you want to * SANS of your SSL certificate.
* pass multiple address for load balancing with SSL, and there's no DNS. The
* @target_name_override@ must exist in the SANS of your SSL certificate.
* @param arguments user provided grpc channel arguments. * @param arguments user provided grpc channel arguments.
*/ */
static SyncClient *WithSSL(std::string const & etcd_url, static SyncClient *WithSSL(std::string const & etcd_url,
@ -324,21 +317,28 @@ class SyncClient {
Response get(std::string const & key); Response get(std::string const & key);
/** /**
* Get the value of specified key of specified revision from the etcd server * Sets the value of a key. The key will be modified if already exists or created
* @param key is the key to be read * if it does not exists.
* @param revision is the revision of the key to be read * @param key is the key to be created or modified
* @param value is the new value to be set
*/ */
Response get(std::string const& key, int64_t revision); Response set(std::string const & key, std::string const & value, int ttl = 0);
/** /**
* Sets the value of a key. The key will be modified if already exists or * Sets the value of a key. The key will be modified if already exists or created
* created if it does not exists. * if it does not exists.
* @param key is the key to be created or modified * @param key is the key to be created or modified
* @param value is the new value to be set * @param value is the new value to be set
* @param leaseId is the lease attached to the key * @param leaseId is the lease attached to the key
*/ */
Response set(std::string const& key, std::string const& value, Response set(std::string const & key, std::string const & value, int64_t leaseId);
const int64_t leaseId = 0);
/**
* Creates a new key and sets it's value. Fails if the key already exists.
* @param key is the key to be created
* @param value is the value to be set
*/
Response add(std::string const & key, std::string const & value, int ttl = 0);
/** /**
* Creates a new key and sets it's value. Fails if the key already exists. * Creates a new key and sets it's value. Fails if the key already exists.
@ -346,8 +346,7 @@ class SyncClient {
* @param value is the value to be set * @param value is the value to be set
* @param leaseId is the lease attached to the key * @param leaseId is the lease attached to the key
*/ */
Response add(std::string const& key, std::string const& value, Response add(std::string const & key, std::string const & value, int64_t leaseId);
const int64_t leaseId = 0);
/** /**
* Put a new key-value pair. * Put a new key-value pair.
@ -357,13 +356,11 @@ class SyncClient {
Response put(std::string const & key, std::string const & value); Response put(std::string const & key, std::string const & value);
/** /**
* Put a new key-value pair. * Modifies an existing key. Fails if the key does not exists.
* @param key is the key to be put * @param key is the key to be modified
* @param value is the value to be put * @param value is the new value to be set
* @param leaseId is the lease id to be associated with the key
*/ */
Response put(std::string const& key, std::string const& value, Response modify(std::string const & key, std::string const & value, int ttl = 0);
const int64_t leaseId);
/** /**
* Modifies an existing key. Fails if the key does not exists. * Modifies an existing key. Fails if the key does not exists.
@ -371,81 +368,81 @@ class SyncClient {
* @param value is the new value to be set * @param value is the new value to be set
* @param leaseId is the lease attached to the key * @param leaseId is the lease attached to the key
*/ */
Response modify(std::string const& key, std::string const& value, Response modify(std::string const & key, std::string const & value, int64_t leaseId);
const int64_t leaseId = 0);
/** /**
* Modifies an existing key only if it has a specific value. Fails if the key * Modifies an existing key only if it has a specific value. Fails if the key does not exists
* does not exists or the original value differs from the expected one. * or the original value differs from the expected one.
* @param key is the key to be modified
* @param value is the new value to be set
* @param old_value is the value to be replaced
*/
Response modify_if(std::string const & key, std::string const & value, std::string const & old_value, int ttl = 0);
/**
* Modifies an existing key only if it has a specific value. Fails if the key does not exists
* or the original value differs from the expected one.
* @param key is the key to be modified * @param key is the key to be modified
* @param value is the new value to be set * @param value is the new value to be set
* @param old_value is the value to be replaced * @param old_value is the value to be replaced
* @param leaseId is the lease attached to the key * @param leaseId is the lease attached to the key
*/ */
Response modify_if(std::string const& key, std::string const& value, Response modify_if(std::string const & key, std::string const & value, std::string const & old_value, int64_t leaseId);
std::string const& old_value, const int64_t leaseId = 0);
/** /**
* Modifies an existing key only if it has a specific modification index * Modifies an existing key only if it has a specific modification index value. Fails if the key
* value. Fails if the key does not exists or the modification index of the * does not exists or the modification index of the previous value differs from the expected one.
* previous value differs from the expected one. * @param key is the key to be modified
* @param value is the new value to be set
* @param old_index is the expected index of the original value
*/
Response modify_if(std::string const & key, std::string const & value, int64_t old_index, int ttl = 0);
/**
* Modifies an existing key only if it has a specific modification index value. Fails if the key
* does not exists or the modification index of the previous value differs from the expected one.
* @param key is the key to be modified * @param key is the key to be modified
* @param value is the new value to be set * @param value is the new value to be set
* @param old_index is the expected index of the original value * @param old_index is the expected index of the original value
* @param leaseId is the lease attached to the key * @param leaseId is the lease attached to the key
*/ */
Response modify_if(std::string const& key, std::string const& value, Response modify_if(std::string const & key, std::string const & value, int64_t old_index, int64_t leaseId);
int64_t old_index, const int64_t leaseId = 0);
/** /**
* Removes a single key. The key has to point to a plain, non directory entry. * Removes a single key. The key has to point to a plain, non directory entry.
* @param key is the key to be deleted * @param key is the key to be deleted
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the key does not exist.
*/ */
Response rm(std::string const & key); Response rm(std::string const & key);
/** /**
* Removes a single key but only if it has a specific value. Fails if the key * Removes a single key but only if it has a specific value. Fails if the key does not exists
* does not exists or the its value differs from the expected one. * or the its value differs from the expected one.
* @param key is the key to be deleted * @param key is the key to be deleted
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the key does not exist.
*/ */
Response rm_if(std::string const & key, std::string const & old_value); Response rm_if(std::string const & key, std::string const & old_value);
/** /**
* Removes an existing key only if it has a specific modification index value. * Removes an existing key only if it has a specific modification index value. Fails if the key
* Fails if the key does not exists or the modification index of it differs * does not exists or the modification index of it differs from the expected one.
* from the expected one.
* @param key is the key to be deleted * @param key is the key to be deleted
* @param old_index is the expected index of the existing value * @param old_index is the expected index of the existing value
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the key does not exist.
*/ */
Response rm_if(std::string const & key, int64_t old_index); Response rm_if(std::string const & key, int64_t old_index);
/** /**
* Removes a directory node. Fails if the parent directory dos not exists or * Removes a directory node. Fails if the parent directory dos not exists or not a directory.
* not a directory.
* @param key is the directory to be created to be listed * @param key is the directory to be created to be listed
* @param recursive if true then delete a whole subtree, otherwise deletes * @param recursive if true then delete a whole subtree, otherwise deletes only an empty directory.
* only an empty directory.
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the no key been deleted.
*/ */
Response rmdir(std::string const & key, bool recursive = false); Response rmdir(std::string const & key, bool recursive = false);
/** /**
* Removes multiple keys between [key, range_end). * Removes multiple keys between [key, range_end).
* *
* This overload for `const char *` is to avoid const char * to bool implicit * This overload for `const char *` is to avoid const char * to bool implicit casting.
* casting.
* *
* @param key is the directory to be created to be listed * @param key is the directory to be created to be listed
* @param range_end is the end of key range to be removed. * @param range_end is the end of key range to be removed.
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the no key been deleted.
*/ */
Response rmdir(std::string const & key, const char *range_end); Response rmdir(std::string const & key, const char *range_end);
@ -454,8 +451,6 @@ class SyncClient {
* *
* @param key is the directory to be created to be listed * @param key is the directory to be created to be listed
* @param range_end is the end of key range to be removed. * @param range_end is the end of key range to be removed.
*
* @return Returns etcdv3::ERROR_KEY_NOT_FOUND if the no key been deleted.
*/ */
Response rmdir(std::string const & key, std::string const &range_end); Response rmdir(std::string const & key, std::string const &range_end);
@ -470,27 +465,14 @@ class SyncClient {
* Gets a directory listing of the directory prefixed by the key. * Gets a directory listing of the directory prefixed by the key.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param limit is the size limit of results to be listed, we don't use * @param limit is the size limit of results to be listed, we don't use default parameters
* default parameters to ensure backwards binary compatibility. 0 means no * to ensure backwards binary compatibility.
* limit.
*/ */
Response ls(std::string const & key, size_t const limit); Response ls(std::string const & key, size_t const limit);
/** /**
* Gets a directory listing of the directory prefixed by the key with given * Gets a directory listing of the directory identified by the key and range_end, i.e., get
* revision. * all keys in the range [key, range_end).
*
* @param key is the key to be listed
* @param limit is the size limit of results to be listed, we don't use
* default parameters to ensure backwards binary compatibility. 0 means no
* limit.
* @param revision is the revision to be listed
*/
Response ls(std::string const& key, size_t const limit, int64_t revision);
/**
* Gets a directory listing of the directory identified by the key and
* range_end, i.e., get all keys in the range [key, range_end).
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param range_end is the end of key range to be listed * @param range_end is the end of key range to be listed
@ -498,33 +480,15 @@ class SyncClient {
Response ls(std::string const & key, std::string const &range_end); Response ls(std::string const & key, std::string const &range_end);
/** /**
* Gets a directory listing of the directory identified by the key and * Gets a directory listing of the directory identified by the key and range_end, i.e., get
* range_end, i.e., get all keys in the range [key, range_end), and respects * all keys in the range [key, range_end), and respects the given limit.
* the given limit.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param range_end is the end of key range to be listed * @param range_end is the end of key range to be listed
* @param limit is the size limit of results to be listed, we don't use * @param limit is the size limit of results to be listed, we don't use default parameters
* default parameters to ensure backwards binary compatibility. 0 means no * to ensure backwards binary compatibility.
* limit.
*/ */
Response ls(std::string const& key, std::string const& range_end, Response ls(std::string const & key, std::string const &range_end, size_t const limit);
size_t const limit);
/**
* Gets a directory listing of the directory identified by the key and
* range_end, i.e., get all keys in the range [key, range_end), and respects
* the given limit and revision.
*
* @param key is the key to be listed
* @param range_end is the end of key range to be listed
* @param limit is the size limit of results to be listed, we don't use
* default parameters to ensure backwards binary compatibility. 0 means no
* limit.
* @param revision is the revision to be listed
*/
Response ls(std::string const& key, std::string const& range_end,
size_t const limit, int64_t revision);
/** /**
* Gets a directory listing of the directory prefixed by the key. * Gets a directory listing of the directory prefixed by the key.
@ -541,27 +505,14 @@ class SyncClient {
* Note that only keys are included in the response. * Note that only keys are included in the response.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param limit is the size limit of results to be listed, we don't use * @param limit is the size limit of results to be listed, we don't use default parameters
* default parameters to ensure backwards binary compatibility. * to ensure backwards binary compatibility.
*/ */
Response keys(std::string const & key, size_t const limit); Response keys(std::string const & key, size_t const limit);
/** /**
* Gets a directory listing of the directory prefixed by the key with * List keys identified by the key and range_end, i.e., get all keys in the range [key,
* specified revision. * range_end).
*
* Note that only keys are included in the response.
*
* @param key is the key to be listed
* @param limit is the size limit of results to be listed, we don't use
* default parameters to ensure backwards binary compatibility.
* @param revision is the revision to be listed
*/
Response keys(std::string const& key, size_t const limit, int64_t revision);
/**
* List keys identified by the key and range_end, i.e., get all keys in the
* range [key, range_end).
* *
* Note that only keys are included in the response. * Note that only keys are included in the response.
* *
@ -571,61 +522,40 @@ class SyncClient {
Response keys(std::string const & key, std::string const &range_end); Response keys(std::string const & key, std::string const &range_end);
/** /**
* List keys identified by the key and range_end, i.e., get all keys in the * List keys identified by the key and range_end, i.e., get all keys in the range [key,
* range [key, range_end), and respects the given limit. * range_end), and respects the given limit.
* *
* Note that only keys are included in the response. * Note that only keys are included in the response.
* *
* @param key is the key to be listed * @param key is the key to be listed
* @param range_end is the end of key range to be listed * @param range_end is the end of key range to be listed
* @param limit is the size limit of results to be listed, we don't use * @param limit is the size limit of results to be listed, we don't use default parameters
* default parameters to ensure backwards binary compatibility. * to ensure backwards binary compatibility.
*/ */
Response keys(std::string const& key, std::string const& range_end, Response keys(std::string const & key, std::string const &range_end, size_t const limit);
size_t const limit);
/** /**
* List keys identified by the key and range_end, i.e., get all keys in the * Watches for changes of a key or a subtree. Please note that if you watch e.g. "/testdir" and
* range [key, range_end), and respects the given limit and revision. * a new key is created, like "/testdir/newkey" then no change happened in the value of
* * "/testdir" so your watch will not detect this. If you want to detect addition and deletion of
* Note that only keys are included in the response. * directory entries then you have to do a recursive watch.
*
* @param key is the key to be listed
* @param range_end is the end of key range to be listed
* @param limit is the size limit of results to be listed, we don't use
* default parameters to ensure backwards binary compatibility.
* @param revision is the revision to be listed
*/
Response keys(std::string const& key, std::string const& range_end,
size_t const limit, int64_t revision);
/**
* Watches for changes of a key or a subtree. Please note that if you watch
* e.g. "/testdir" and a new key is created, like "/testdir/newkey" then no
* change happened in the value of
* "/testdir" so your watch will not detect this. If you want to detect
* addition and deletion of directory entries then you have to do a recursive
* watch.
* @param key is the value or directory to be watched * @param key is the value or directory to be watched
* @param recursive if true watch a whole subtree * @param recursive if true watch a whole subtree
*/ */
Response watch(std::string const & key, bool recursive = false); Response watch(std::string const & key, bool recursive = false);
/** /**
* Watches for changes of a key or a subtree from a specific index. The index * Watches for changes of a key or a subtree from a specific index. The index value can be in the "past".
* value can be in the "past".
* @param key is the value or directory to be watched * @param key is the value or directory to be watched
* @param fromIndex the first index we are interested in * @param fromIndex the first index we are interested in
* @param recursive if true watch a whole subtree * @param recursive if true watch a whole subtree
*/ */
Response watch(std::string const& key, int64_t fromIndex, Response watch(std::string const & key, int64_t fromIndex, bool recursive = false);
bool recursive = false);
/** /**
* Watches for changes of a range of keys inside [key, range_end). * Watches for changes of a range of keys inside [key, range_end).
* *
* This overload for `const char *` is to avoid const char * to bool implicit * This overload for `const char *` is to avoid const char * to bool implicit casting.
* casting.
* *
* @param key is the value or directory to be watched * @param key is the value or directory to be watched
* @param range_end is the end of key range to be removed. * @param range_end is the end of key range to be removed.
@ -641,17 +571,15 @@ class SyncClient {
Response watch(std::string const & key, std::string const &range_end); Response watch(std::string const & key, std::string const &range_end);
/** /**
* Watches for changes of a range of keys inside [key, range_end) from a * Watches for changes of a range of keys inside [key, range_end) from a specific index. The index value
* specific index. The index value can be in the "past". * can be in the "past".
* *
* Watches for changes of a key or a subtree from a specific index. The index * Watches for changes of a key or a subtree from a specific index. The index value can be in the "past".
* value can be in the "past".
* @param key is the value or directory to be watched * @param key is the value or directory to be watched
* @param range_end is the end of key range to be removed. * @param range_end is the end of key range to be removed.
* @param fromIndex the first index we are interested in * @param fromIndex the first index we are interested in
*/ */
Response watch(std::string const& key, std::string const& range_end, Response watch(std::string const & key, std::string const &range_end, int64_t fromIndex);
int64_t fromIndex);
/** /**
* Grants a lease. * Grants a lease.
@ -683,43 +611,23 @@ class SyncClient {
Response leases(); Response leases();
/** /**
* Add an etcd member to the etcd cluster, equivalent to `etcdctl member add`. * Gains a lock at a key, using a default created lease, using the default lease (10 seconds), with
* @param peer_urls is comma separated list of URLs for the new member. * keeping alive has already been taken care of by the library.
* @param is_learner is true if the added member is a learner.
*/
Response add_member(std::string const& peer_urls, bool is_learner);
/**
* List all members, equivalent to `etcdctl member list`.
*/
Response list_member();
/**
* Remove a member of an etcd cluster, equivalent to `etcdctl member remove`.
* @param member_id is the id of the member to be removed.
*/
Response remove_member(uint64_t member_id);
/**
* Gains a lock at a key, using a default created lease, using the default
* lease (10 seconds), with keeping alive has already been taken care of by
* the library.
* @param key is the key to be used to request the lock. * @param key is the key to be used to request the lock.
*/ */
Response lock(std::string const &key); Response lock(std::string const &key);
/** /**
* Gains a lock at a key, using a default created lease, using the specified * Gains a lock at a key, using a default created lease, using the specified lease TTL (in seconds), with
* lease TTL (in seconds), with keeping alive has already been taken care of * keeping alive has already been taken care of by the library.
* by the library.
* @param key is the key to be used to request the lock. * @param key is the key to be used to request the lock.
* @param lease_ttl is the TTL used to create a lease for the key. * @param lease_ttl is the TTL used to create a lease for the key.
*/ */
Response lock(std::string const &key, int lease_ttl); Response lock(std::string const &key, int lease_ttl);
/** /**
* Gains a lock at a key, using a user-provided lease, the lifetime of the * Gains a lock at a key, using a user-provided lease, the lifetime of the lease won't be taken care
* lease won't be taken care of by the library. * of by the library.
* @param key is the key to be used to request the lock. * @param key is the key to be used to request the lock.
*/ */
Response lock_with_lease(std::string const &key, int64_t lease_id); Response lock_with_lease(std::string const &key, int64_t lease_id);
@ -750,8 +658,7 @@ class SyncClient {
* - created rev: the revision of the generated key * - created rev: the revision of the generated key
* - lease: the lease id of the election leader * - lease: the lease id of the election leader
*/ */
Response campaign(std::string const& name, int64_t lease_id, Response campaign(std::string const &name, int64_t lease_id, std::string const &value);
std::string const& value);
/** /**
* Updates the value of election with a new value, with leader key returns by * Updates the value of election with a new value, with leader key returns by
@ -764,8 +671,7 @@ class SyncClient {
* @param value is the new value to set. * @param value is the new value to set.
*/ */
Response proclaim(std::string const &name, int64_t lease_id, Response proclaim(std::string const &name, int64_t lease_id,
std::string const& key, int64_t revision, std::string const &key, int64_t revision, std::string const &value);
std::string const& value);
/** /**
* Get the current leader proclamation. * Get the current leader proclamation.
@ -785,7 +691,6 @@ class SyncClient {
~Observer(); ~Observer();
// wait at least *one* response from the observer. // wait at least *one* response from the observer.
Response WaitOnce(); Response WaitOnce();
private: private:
std::shared_ptr<etcdv3::AsyncObserveAction> action = nullptr; std::shared_ptr<etcdv3::AsyncObserveAction> action = nullptr;
@ -797,8 +702,7 @@ class SyncClient {
* *
* @param name is the names of election to watch. * @param name is the names of election to watch.
* *
* @returns an observer that holds that action and will cancel the request * @returns an observer that holds that action and will cancel the request when being destructed.
* when being destructed.
*/ */
std::unique_ptr<Observer> observe(std::string const &name); std::unique_ptr<Observer> observe(std::string const &name);
@ -817,68 +721,34 @@ class SyncClient {
private: private:
// TODO: use std::unique_ptr<> // TODO: use std::unique_ptr<>
std::shared_ptr<etcdv3::AsyncHeadAction> head_internal(); std::shared_ptr<etcdv3::AsyncHeadAction> head_internal();
std::shared_ptr<etcdv3::AsyncRangeAction> get_internal( std::shared_ptr<etcdv3::AsyncRangeAction> get_internal(std::string const & key);
std::string const& key, const int64_t revision = 0); std::shared_ptr<etcdv3::AsyncSetAction> set_internal(std::string const & key, std::string const & value, int64_t leaseId);
std::shared_ptr<etcdv3::AsyncSetAction> add_internal( std::shared_ptr<etcdv3::AsyncSetAction> add_internal(std::string const & key, std::string const & value, int64_t leaseId);
std::string const& key, std::string const& value, std::shared_ptr<etcdv3::AsyncPutAction> put_internal(std::string const & key, std::string const & value);
const int64_t leaseId = 0); std::shared_ptr<etcdv3::AsyncUpdateAction> modify_internal(std::string const & key, std::string const & value, int64_t leaseId);
std::shared_ptr<etcdv3::AsyncPutAction> put_internal( std::shared_ptr<etcdv3::AsyncCompareAndSwapAction> modify_if_internal(std::string const & key, std::string const & value, int64_t old_index, std::string const & old_value, int64_t leaseId, etcdv3::AtomicityType const & atomicity_type);
std::string const& key, std::string const& value, std::shared_ptr<etcdv3::AsyncDeleteAction> rm_internal(std::string const & key);
const int64_t leaseId = 0); std::shared_ptr<etcdv3::AsyncCompareAndDeleteAction> rm_if_internal(std::string const & key, int64_t old_index, const std::string & old_value, etcdv3::AtomicityType const & atomicity_type);
std::shared_ptr<etcdv3::AsyncUpdateAction> modify_internal( std::shared_ptr<etcdv3::AsyncDeleteAction> rmdir_internal(std::string const & key, bool recursive = false);
std::string const& key, std::string const& value, std::shared_ptr<etcdv3::AsyncDeleteAction> rmdir_internal(std::string const & key, std::string const &range_end);
const int64_t leaseId = 0); std::shared_ptr<etcdv3::AsyncRangeAction> ls_internal(std::string const & key, size_t const limit, bool const keys_only = false);
std::shared_ptr<etcdv3::AsyncCompareAndSwapAction> modify_if_internal( std::shared_ptr<etcdv3::AsyncRangeAction> ls_internal(std::string const & key, std::string const &range_end, size_t const limit, bool const keys_only = false);
std::string const& key, std::string const& value, int64_t old_index, std::shared_ptr<etcdv3::AsyncWatchAction> watch_internal(std::string const & key, int64_t fromIndex, bool recursive = false);
std::string const& old_value, etcdv3::AtomicityType const& atomicity_type, std::shared_ptr<etcdv3::AsyncWatchAction> watch_internal(std::string const & key, std::string const &range_end, int64_t fromIndex);
const int64_t leaseId = 0); std::shared_ptr<etcdv3::AsyncLeaseRevokeAction> leaserevoke_internal(int64_t lease_id);
std::shared_ptr<etcdv3::AsyncDeleteAction> rm_internal( std::shared_ptr<etcdv3::AsyncLeaseTimeToLiveAction> leasetimetolive_internal(int64_t lease_id);
std::string const& key);
std::shared_ptr<etcdv3::AsyncCompareAndDeleteAction> rm_if_internal(
std::string const& key, int64_t old_index, const std::string& old_value,
etcdv3::AtomicityType const& atomicity_type);
std::shared_ptr<etcdv3::AsyncDeleteAction> rmdir_internal(
std::string const& key, bool recursive = false);
std::shared_ptr<etcdv3::AsyncDeleteAction> rmdir_internal(
std::string const& key, std::string const& range_end);
std::shared_ptr<etcdv3::AsyncRangeAction> ls_internal(
std::string const& key, size_t const limit, bool const keys_only = false,
int64_t revision = 0);
std::shared_ptr<etcdv3::AsyncRangeAction> ls_internal(
std::string const& key, std::string const& range_end, size_t const limit,
bool const keys_only = false, int64_t revision = 0);
std::shared_ptr<etcdv3::AsyncWatchAction> watch_internal(
std::string const& key, int64_t fromIndex, bool recursive = false);
std::shared_ptr<etcdv3::AsyncWatchAction> watch_internal(
std::string const& key, std::string const& range_end, int64_t fromIndex);
std::shared_ptr<etcdv3::AsyncLeaseRevokeAction> leaserevoke_internal(
int64_t lease_id);
std::shared_ptr<etcdv3::AsyncLeaseTimeToLiveAction> leasetimetolive_internal(
int64_t lease_id);
std::shared_ptr<etcdv3::AsyncLeaseLeasesAction> leases_internal(); std::shared_ptr<etcdv3::AsyncLeaseLeasesAction> leases_internal();
std::shared_ptr<etcdv3::AsyncAddMemberAction> add_member_internal( Response lock_internal(std::string const &key, std::shared_ptr<etcd::KeepAlive> const &keepalive);
std::string const& peer_urls, bool is_learner); std::shared_ptr<etcdv3::AsyncLockAction> lock_with_lease_internal(std::string const &key, int64_t lease_id);
std::shared_ptr<etcdv3::AsyncListMemberAction> list_member_internal(); std::shared_ptr<etcdv3::AsyncUnlockAction> unlock_internal(std::string const &lock_key);
std::shared_ptr<etcdv3::AsyncRemoveMemberAction> remove_member_internal( std::shared_ptr<etcdv3::AsyncTxnAction> txn_internal(etcdv3::Transaction const &txn);
const uint64_t member_id); std::shared_ptr<etcdv3::AsyncCampaignAction> campaign_internal(std::string const &name, int64_t lease_id, std::string const &value);
Response lock_internal(std::string const& key,
std::shared_ptr<etcd::KeepAlive> const& keepalive);
std::shared_ptr<etcdv3::AsyncLockAction> lock_with_lease_internal(
std::string const& key, int64_t lease_id);
std::shared_ptr<etcdv3::AsyncUnlockAction> unlock_internal(
std::string const& lock_key);
std::shared_ptr<etcdv3::AsyncTxnAction> txn_internal(
etcdv3::Transaction const& txn);
std::shared_ptr<etcdv3::AsyncCampaignAction> campaign_internal(
std::string const& name, int64_t lease_id, std::string const& value);
std::shared_ptr<etcdv3::AsyncProclaimAction> proclaim_internal( std::shared_ptr<etcdv3::AsyncProclaimAction> proclaim_internal(
std::string const& name, int64_t lease_id, std::string const& key, std::string const &name, int64_t lease_id,
int64_t revision, std::string const& value); std::string const &key, int64_t revision, std::string const &value);
std::shared_ptr<etcdv3::AsyncLeaderAction> leader_internal( std::shared_ptr<etcdv3::AsyncLeaderAction> leader_internal(std::string const &name);
std::string const& name); std::shared_ptr<etcdv3::AsyncResignAction> resign_internal(std::string const &name, int64_t lease_id,
std::shared_ptr<etcdv3::AsyncResignAction> resign_internal( std::string const &key, int64_t revision);
std::string const& name, int64_t lease_id, std::string const& key,
int64_t revision);
public: public:
/** /**
@ -899,10 +769,8 @@ class SyncClient {
* Set a timeout value for grpc operations. * Set a timeout value for grpc operations.
*/ */
template <typename Period = std::micro> template <typename Period = std::micro>
void set_grpc_timeout(std::chrono::duration<std::chrono::microseconds::rep, void set_grpc_timeout(std::chrono::duration<std::chrono::microseconds::rep, Period> const &timeout) {
Period> const& timeout) { grpc_timeout = std::chrono::duration_cast<std::chrono::milliseconds>(timeout);
grpc_timeout =
std::chrono::duration_cast<std::chrono::milliseconds>(timeout);
} }
/** /**
@ -919,10 +787,8 @@ class SyncClient {
std::shared_ptr<grpc_impl::Channel> channel; std::shared_ptr<grpc_impl::Channel> channel;
#endif #endif
mutable std::unique_ptr<TokenAuthenticator, TokenAuthenticatorDeleter> mutable std::unique_ptr<TokenAuthenticator, TokenAuthenticatorDeleter> token_authenticator;
token_authenticator; mutable std::chrono::microseconds grpc_timeout = std::chrono::microseconds::zero();
mutable std::chrono::microseconds grpc_timeout =
std::chrono::microseconds::zero();
struct EtcdServerStubs; struct EtcdServerStubs;
struct EtcdServerStubsDeleter { struct EtcdServerStubsDeleter {
@ -939,6 +805,6 @@ class SyncClient {
friend class Client; friend class Client;
}; };
} // namespace etcd }
#endif #endif

View File

@ -11,13 +11,14 @@ class KeyValue;
namespace mvccpb { namespace mvccpb {
class KeyValue; class KeyValue;
class Event; class Event;
} // namespace mvccpb }
namespace electionpb { namespace electionpb {
class LeaderKey; class LeaderKey;
} }
namespace etcd { namespace etcd
{
class Value; class Value;
class Event; class Event;
class Response; class Response;
@ -27,11 +28,12 @@ class SyncClient;
/** /**
* Represents a value object received from the etcd server * Represents a value object received from the etcd server
*/ */
class Value { class Value
{
public: public:
/** /**
* Returns true if this value represents a directory on the server. If true * Returns true if this value represents a directory on the server. If true the as_string()
* the as_string() method is meaningless. * method is meaningless.
*/ */
bool is_dir() const; bool is_dir() const;
@ -71,8 +73,7 @@ class Value {
friend class Client; friend class Client;
friend class SyncClient; friend class SyncClient;
friend class Response; friend class Response;
friend class BaseResponse; // deliberately done since Value class will be friend class BaseResponse; //deliberately done since Value class will be removed during full V3
// removed during full V3
friend class DeleteRpcResponse; friend class DeleteRpcResponse;
friend class AsyncDeleteResponse; friend class AsyncDeleteResponse;
@ -91,11 +92,10 @@ class Value {
int64_t leaseId; int64_t leaseId;
}; };
using Values = std::vector<Value>; typedef std::vector<Value> Values;
std::ostream& operator<<(std::ostream& os, const Value& value); class Event
{
class Event {
public: public:
enum class EventType { enum class EventType {
PUT, PUT,
@ -124,12 +124,7 @@ class Event {
bool _has_kv, _has_prev_kv; bool _has_kv, _has_prev_kv;
}; };
using Events = std::vector<Event>; typedef std::vector<Event> Events;
}
std::ostream& operator<<(std::ostream& os, const Event::EventType& value);
std::ostream& operator<<(std::ostream& os, const Event& event);
} // namespace etcd
#endif #endif

View File

@ -8,24 +8,32 @@
#include "etcd/Response.hpp" #include "etcd/Response.hpp"
namespace etcd { namespace etcd
{
// forward declaration to avoid header/library dependency // forward declaration to avoid header/library dependency
class Client; class Client;
class Watcher { class Watcher
{
public: public:
Watcher(Client const &client, std::string const & key, Watcher(Client const &client, std::string const & key,
std::function<void(Response)> callback, bool recursive = false); std::function<void(Response)> callback,
bool recursive=false);
Watcher(SyncClient const &client, std::string const & key, Watcher(SyncClient const &client, std::string const & key,
std::function<void(Response)> callback, bool recursive = false); std::function<void(Response)> callback,
bool recursive=false);
Watcher(Client const &client, std::string const & key, Watcher(Client const &client, std::string const & key,
std::string const& range_end, std::function<void(Response)> callback); std::string const &range_end,
std::function<void(Response)> callback);
Watcher(SyncClient const &client, std::string const & key, Watcher(SyncClient const &client, std::string const & key,
std::string const& range_end, std::function<void(Response)> callback); std::string const &range_end,
std::function<void(Response)> callback);
Watcher(Client const &client, std::string const & key, int64_t fromIndex, Watcher(Client const &client, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback, bool recursive = false); std::function<void(Response)> callback,
bool recursive=false);
Watcher(SyncClient const &client, std::string const & key, int64_t fromIndex, Watcher(SyncClient const &client, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback, bool recursive = false); std::function<void(Response)> callback,
bool recursive=false);
Watcher(Client const &client, std::string const & key, Watcher(Client const &client, std::string const & key,
std::string const &range_end, int64_t fromIndex, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback); std::function<void(Response)> callback);
@ -33,60 +41,78 @@ class Watcher {
std::string const &range_end, int64_t fromIndex, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback); std::function<void(Response)> callback);
Watcher(std::string const & address, std::string const & key, Watcher(std::string const & address, std::string const & key,
std::function<void(Response)> callback, bool recursive = false); std::function<void(Response)> callback,
bool recursive=false);
Watcher(std::string const & address, std::string const & key, Watcher(std::string const & address, std::string const & key,
std::string const& range_end, std::function<void(Response)> callback); std::string const &range_end,
std::function<void(Response)> callback);
Watcher(std::string const & address, std::string const & key, int64_t fromIndex, Watcher(std::string const & address, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback, bool recursive = false); std::function<void(Response)> callback,
bool recursive=false);
Watcher(std::string const & address, std::string const & key, Watcher(std::string const & address, std::string const & key,
std::string const &range_end, int64_t fromIndex, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback); std::function<void(Response)> callback);
Watcher(std::string const& address, std::string const& username, Watcher(std::string const & address,
std::string const& password, std::string const& key, std::string const & username, std::string const & password,
std::function<void(Response)> callback, bool recursive = false, std::string const & key,
std::function<void(Response)> callback,
bool recursive=false,
int const auth_token_ttl = 300); int const auth_token_ttl = 300);
Watcher(std::string const& address, std::string const& username, Watcher(std::string const & address,
std::string const& password, std::string const& key, std::string const & username, std::string const & password,
std::string const& range_end, std::function<void(Response)> callback, std::string const & key, std::string const &range_end,
int const auth_token_ttl = 300);
Watcher(std::string const& address, std::string const& username,
std::string const& password, std::string const& key,
int64_t fromIndex, std::function<void(Response)> callback,
bool recursive = false, int const auth_token_ttl = 300);
Watcher(std::string const& address, std::string const& username,
std::string const& password, std::string const& key,
std::string const& range_end, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
int const auth_token_ttl = 300); int const auth_token_ttl = 300);
Watcher(std::string const& address, std::string const& ca, Watcher(std::string const & address,
std::string const& cert, std::string const& privkey, std::string const & username, std::string const & password,
std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback,
bool recursive=false,
int const auth_token_ttl = 300);
Watcher(std::string const & address,
std::string const & username, std::string const & password,
std::string const & key, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback,
int const auth_token_ttl = 300);
Watcher(std::string const & address,
std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & key, int64_t fromIndex, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback, bool recursive=false, std::function<void(Response)> callback, bool recursive=false,
std::string const & target_name_override = ""); std::string const & target_name_override = "");
Watcher(std::string const& address, std::string const& ca, Watcher(std::string const & address,
std::string const& cert, std::string const& privkey, std::string const & ca,
std::string const& key, std::string const& range_end, std::string const & cert,
int64_t fromIndex, std::function<void(Response)> callback, std::string const & privkey,
std::string const & key, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback,
std::string const & target_name_override = ""); std::string const & target_name_override = "");
Watcher(Client const &client, std::string const & key, Watcher(Client const &client, std::string const & key,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive = false); std::function<void(bool)> wait_callback,
bool recursive=false);
Watcher(SyncClient const &client, std::string const & key, Watcher(SyncClient const &client, std::string const & key,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive = false); std::function<void(bool)> wait_callback,
bool recursive=false);
Watcher(Client const &client, std::string const & key, Watcher(Client const &client, std::string const & key,
std::string const& range_end, std::function<void(Response)> callback, std::string const &range_end,
std::function<void(Response)> callback,
std::function<void(bool)> wait_callback); std::function<void(bool)> wait_callback);
Watcher(SyncClient const &client, std::string const & key, Watcher(SyncClient const &client, std::string const & key,
std::string const& range_end, std::function<void(Response)> callback, std::string const &range_end,
std::function<void(Response)> callback,
std::function<void(bool)> wait_callback); std::function<void(bool)> wait_callback);
Watcher(Client const &client, std::string const & key, int64_t fromIndex, Watcher(Client const &client, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive = false); std::function<void(bool)> wait_callback,
bool recursive=false);
Watcher(SyncClient const &client, std::string const & key, int64_t fromIndex, Watcher(SyncClient const &client, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive = false); std::function<void(bool)> wait_callback,
bool recursive=false);
Watcher(Client const &client, std::string const & key, Watcher(Client const &client, std::string const & key,
std::string const &range_end, int64_t fromIndex, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
@ -97,48 +123,61 @@ class Watcher {
std::function<void(bool)> wait_callback); std::function<void(bool)> wait_callback);
Watcher(std::string const & address, std::string const & key, Watcher(std::string const & address, std::string const & key,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive = false); std::function<void(bool)> wait_callback,
bool recursive=false);
Watcher(std::string const & address, std::string const & key, Watcher(std::string const & address, std::string const & key,
std::string const& range_end, std::function<void(Response)> callback, std::string const &range_end,
std::function<void(Response)> callback,
std::function<void(bool)> wait_callback); std::function<void(bool)> wait_callback);
Watcher(std::string const & address, std::string const & key, int64_t fromIndex, Watcher(std::string const & address, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive = false); std::function<void(bool)> wait_callback,
bool recursive=false);
Watcher(std::string const & address, std::string const & key, Watcher(std::string const & address, std::string const & key,
std::string const &range_end, int64_t fromIndex, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback); std::function<void(bool)> wait_callback);
Watcher(std::string const& address, std::string const& username, Watcher(std::string const & address,
std::string const& password, std::string const& key, std::string const & username, std::string const & password,
std::string const & key,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive = false,
int const auth_token_ttl = 300);
Watcher(std::string const& address, std::string const& username,
std::string const& password, std::string const& key,
std::string const& range_end, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, std::function<void(bool)> wait_callback,
bool recursive=false,
int const auth_token_ttl = 300); int const auth_token_ttl = 300);
Watcher(std::string const& address, std::string const& username, Watcher(std::string const & address,
std::string const& password, std::string const& key, std::string const & username, std::string const & password,
int64_t fromIndex, std::function<void(Response)> callback, std::string const & key, std::string const &range_end,
std::function<void(bool)> wait_callback, bool recursive = false,
int const auth_token_ttl = 300);
Watcher(std::string const& address, std::string const& username,
std::string const& password, std::string const& key,
std::string const& range_end, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, std::function<void(bool)> wait_callback,
int const auth_token_ttl = 300); int const auth_token_ttl = 300);
Watcher(std::string const& address, std::string const& ca, Watcher(std::string const & address,
std::string const& cert, std::string const& privkey, std::string const & username, std::string const & password,
std::string const & key, int64_t fromIndex, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive = false, std::function<void(bool)> wait_callback,
bool recursive=false,
int const auth_token_ttl = 300);
Watcher(std::string const & address,
std::string const & username, std::string const & password,
std::string const & key, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback,
std::function<void(bool)> wait_callback,
int const auth_token_ttl = 300);
Watcher(std::string const & address,
std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback,
std::function<void(bool)> wait_callback,
bool recursive=false,
std::string const & target_name_override = ""); std::string const & target_name_override = "");
Watcher(std::string const& address, std::string const& ca, Watcher(std::string const & address,
std::string const& cert, std::string const& privkey, std::string const & ca,
std::string const& key, std::string const& range_end, std::string const & cert,
int64_t fromIndex, std::function<void(Response)> callback, std::string const & privkey,
std::string const & key, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, std::function<void(bool)> wait_callback,
std::string const & target_name_override = ""); std::string const & target_name_override = "");
@ -146,8 +185,8 @@ class Watcher {
Watcher(Watcher &&) = delete; Watcher(Watcher &&) = delete;
/** /**
* Wait util the task has been stopped, actively or passively, e.g., the * Wait util the task has been stopped, actively or passively, e.g., the watcher
* watcher get cancelled or the server closes the connection. * get cancelled or the server closes the connection.
* *
* Returns true if the watcher is been normally cancelled, otherwise false. * Returns true if the watcher is been normally cancelled, otherwise false.
*/ */
@ -156,17 +195,13 @@ class Watcher {
/** /**
* An async wait, the callback will be called when the task has been stopped. * An async wait, the callback will be called when the task has been stopped.
* *
* The callback parameter would be true if the watch is been normally * The callback parameter would be true if the watch is been normally cancelled.
* cancelled.
* *
* Note that you shouldn't use the watcher itself inside the `Wait()` callback * Note that you shouldn't use the watcher itself inside the `Wait()` callback
* as the callback will be invoked in a separate **detached** thread where the * as the callback will be invoked in a separate **detached** thread where the
* watcher may have been destroyed. * watcher may have been destroyed.
*
* @return true if the callback has been set successfully (no existing
* callback).
*/ */
bool Wait(std::function<void(bool)> callback); void Wait(std::function<void(bool)> callback);
/** /**
* Stop the watching action. * Stop the watching action.
@ -181,16 +216,16 @@ class Watcher {
~Watcher(); ~Watcher();
protected: protected:
void doWatch(std::string const& key, std::string const& range_end, void doWatch(std::string const & key,
std::string const & range_end,
std::string const & auth_token, std::string const & auth_token,
std::function<void(Response)> callback); std::function<void(Response)> callback);
std::function<void(Response)> callback; std::function<void(Response)> callback;
std::function<void(bool)> wait_callback; std::function<void(bool)> wait_callback;
// Don't use `pplx::task` to avoid sharing thread pool with other actions on // Don't use `pplx::task` to avoid sharing thread pool with other actions on the client
// the client to avoid any potential blocking, which may block the keepalive // to avoid any potential blocking, which may block the keepalive loop and evict the lease.
// loop and evict the lease.
std::thread task_; std::thread task_;
struct EtcdServerStubs; struct EtcdServerStubs;
@ -204,6 +239,6 @@ class Watcher {
bool recursive; bool recursive;
std::atomic_bool cancelled; std::atomic_bool cancelled;
}; };
} // namespace etcd }
#endif #endif

View File

@ -6,34 +6,37 @@
#include <grpc++/grpc++.h> #include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h" #include "proto/rpc.grpc.pb.h"
#include "proto/v3election.grpc.pb.h"
#include "proto/v3lock.grpc.pb.h" #include "proto/v3lock.grpc.pb.h"
#include "proto/v3election.grpc.pb.h"
#include "etcd/v3/action_constants.hpp"
using grpc::ClientContext; using grpc::ClientContext;
using grpc::CompletionQueue; using grpc::CompletionQueue;
using grpc::Status; using grpc::Status;
using etcdserverpb::Cluster;
using etcdserverpb::KV; using etcdserverpb::KV;
using etcdserverpb::Lease;
using etcdserverpb::Watch; using etcdserverpb::Watch;
using v3electionpb::Election; using etcdserverpb::Lease;
using v3lockpb::Lock; using v3lockpb::Lock;
using v3electionpb::Election;
namespace etcd { namespace etcd {
class Response; class Response;
} }
namespace etcdv3 { namespace etcdv3
enum class AtomicityType { PREV_INDEX = 0, PREV_VALUE = 1 }; {
enum class AtomicityType
{
PREV_INDEX = 0,
PREV_VALUE = 1
};
struct ActionParameters { struct ActionParameters
{
ActionParameters(); ActionParameters();
bool withPrefix; bool withPrefix;
int64_t revision = 0; int64_t revision;
int64_t old_revision = 0; int64_t old_revision;
int64_t lease_id = 0; // no lease int64_t lease_id = 0; // no lease
int ttl; int ttl;
int limit; int limit;
@ -45,16 +48,9 @@ struct ActionParameters {
std::string value; std::string value;
std::string old_value; std::string old_value;
std::string auth_token; std::string auth_token;
// for cluster management apis
std::vector<std::string> peer_urls;
bool is_learner;
uint64_t member_id;
std::chrono::microseconds grpc_timeout = std::chrono::microseconds::zero(); std::chrono::microseconds grpc_timeout = std::chrono::microseconds::zero();
KV::Stub* kv_stub; KV::Stub* kv_stub;
Watch::Stub* watch_stub; Watch::Stub* watch_stub;
Cluster::Stub* cluster_stub;
Lease::Stub* lease_stub; Lease::Stub* lease_stub;
Lock::Stub* lock_stub; Lock::Stub* lock_stub;
Election::Stub* election_stub; Election::Stub* election_stub;
@ -65,7 +61,8 @@ struct ActionParameters {
void dump(std::ostream &os) const; void dump(std::ostream &os) const;
}; };
class Action { class Action
{
public: public:
Action(etcdv3::ActionParameters const &params); Action(etcdv3::ActionParameters const &params);
Action(etcdv3::ActionParameters && params); Action(etcdv3::ActionParameters && params);
@ -73,14 +70,12 @@ class Action {
void waitForResponse(); void waitForResponse();
const std::chrono::high_resolution_clock::time_point startTimepoint(); const std::chrono::high_resolution_clock::time_point startTimepoint();
protected: protected:
Status status; Status status;
ClientContext context; ClientContext context;
CompletionQueue cq_; CompletionQueue cq_;
etcdv3::ActionParameters parameters; etcdv3::ActionParameters parameters;
std::chrono::high_resolution_clock::time_point start_timepoint; std::chrono::high_resolution_clock::time_point start_timepoint;
private: private:
// Init things like auth token, etc. // Init things like auth token, etc.
void InitAction(); void InitAction();
@ -91,27 +86,6 @@ class Action {
namespace detail { namespace detail {
std::string string_plus_one(std::string const &value); std::string string_plus_one(std::string const &value);
std::string resolve_etcd_endpoints(std::string const &default_endpoints); std::string resolve_etcd_endpoints(std::string const &default_endpoints);
template <typename Req>
void make_request_with_ranges(Req& req, std::string const& key,
std::string const& range_end,
bool const recursive) {
if (!recursive) {
req.set_key(key);
} else {
if (key.empty()) {
req.set_key(etcdv3::NUL);
req.set_range_end(etcdv3::NUL);
} else {
req.set_key(key);
req.set_range_end(detail::string_plus_one(key));
} }
} }
if (!range_end.empty()) {
req.set_range_end(range_end);
}
}
} // namespace detail
} // namespace etcdv3
#endif #endif

View File

@ -0,0 +1,27 @@
#ifndef __ASYNC_COMPAREANDDELETE_HPP__
#define __ASYNC_COMPAREANDDELETE_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncTxnResponse.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::TxnResponse;
using etcdserverpb::KV;
namespace etcdv3
{
class AsyncCompareAndDeleteAction : public etcdv3::Action
{
public:
AsyncCompareAndDeleteAction(etcdv3::ActionParameters && params, etcdv3::AtomicityType type);
AsyncTxnResponse ParseResponse();
private:
TxnResponse reply;
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,27 @@
#ifndef __ASYNC_COMPAREANDSWAP_HPP__
#define __ASYNC_COMPAREANDSWAP_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncTxnResponse.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::TxnResponse;
using etcdserverpb::KV;
namespace etcdv3
{
class AsyncCompareAndSwapAction : public etcdv3::Action
{
public:
AsyncCompareAndSwapAction(etcdv3::ActionParameters && params, etcdv3::AtomicityType type);
AsyncTxnResponse ParseResponse();
private:
TxnResponse reply;
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,26 @@
#ifndef __ASYNC_DELETE_HPP__
#define __ASYNC_DELETE_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncDeleteResponse.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::DeleteRangeResponse;
namespace etcdv3
{
class AsyncDeleteAction : public etcdv3::Action
{
public:
AsyncDeleteAction(etcdv3::ActionParameters && params);
AsyncDeleteResponse ParseResponse();
private:
DeleteRangeResponse reply;
std::unique_ptr<ClientAsyncResponseReader<DeleteRangeResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,23 @@
#ifndef __ASYNC_DELETERESPONSE_HPP__
#define __ASYNC_DELETERESPONSE_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/V3Response.hpp"
#include "etcd/v3/Action.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::DeleteRangeResponse;
namespace etcdv3
{
class AsyncDeleteResponse : public etcdv3::V3Response
{
public:
AsyncDeleteResponse(){};
void ParseResponse(std::string const& key, bool prefix, DeleteRangeResponse& resp);
};
}
#endif

View File

@ -0,0 +1,80 @@
#ifndef __ASYNC_ELECTIONACTION_HPP__
#define __ASYNC_ELECTIONACTION_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "proto/v3election.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncElectionResponse.hpp"
#include "etcd/Response.hpp"
using grpc::ClientAsyncResponseReader;
using grpc::ClientAsyncReader;
using v3electionpb::CampaignRequest;
using v3electionpb::CampaignResponse;
using v3electionpb::ProclaimRequest;
using v3electionpb::ProclaimResponse;
using v3electionpb::LeaderRequest;
using v3electionpb::LeaderResponse;
using v3electionpb::ResignRequest;
using v3electionpb::ResignResponse;
namespace etcdv3
{
class AsyncCampaignAction : public etcdv3::Action
{
public:
AsyncCampaignAction(etcdv3::ActionParameters && params);
AsyncCampaignResponse ParseResponse();
private:
CampaignResponse reply;
std::unique_ptr<ClientAsyncResponseReader<CampaignResponse>> response_reader;
};
class AsyncProclaimAction : public etcdv3::Action
{
public:
AsyncProclaimAction(etcdv3::ActionParameters && params);
AsyncProclaimResponse ParseResponse();
private:
ProclaimResponse reply;
std::unique_ptr<ClientAsyncResponseReader<ProclaimResponse>> response_reader;
};
class AsyncLeaderAction : public etcdv3::Action
{
public:
AsyncLeaderAction(etcdv3::ActionParameters && params);
AsyncLeaderResponse ParseResponse();
private:
LeaderResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LeaderResponse>> response_reader;
};
class AsyncObserveAction : public etcdv3::Action
{
public:
AsyncObserveAction(etcdv3::ActionParameters && params);
AsyncObserveResponse ParseResponse();
void waitForResponse();
void CancelObserve();
bool Cancelled() const;
private:
LeaderResponse reply;
std::unique_ptr<ClientAsyncReader<LeaderResponse>> response_reader;
std::atomic_bool isCancelled;
std::mutex protect_is_cancalled;
};
class AsyncResignAction : public etcdv3::Action
{
public:
AsyncResignAction(etcdv3::ActionParameters && params);
AsyncResignResponse ParseResponse();
private:
ResignResponse reply;
std::unique_ptr<ClientAsyncResponseReader<ResignResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,51 @@
#ifndef __ASYNC_ELECTIONRESPONSE_HPP__
#define __ASYNC_ELECTIONRESPONSE_HPP__
#include "proto/rpc.pb.h"
#include "proto/v3election.pb.h"
#include "etcd/v3/V3Response.hpp"
using v3electionpb::CampaignResponse;
using v3electionpb::ProclaimResponse;
using v3electionpb::LeaderResponse;
using v3electionpb::ResignResponse;
namespace etcdv3
{
class AsyncCampaignResponse : public etcdv3::V3Response
{
public:
AsyncCampaignResponse(){};
void ParseResponse(CampaignResponse& resp);
};
class AsyncProclaimResponse : public etcdv3::V3Response
{
public:
AsyncProclaimResponse(){};
void ParseResponse(ProclaimResponse& resp);
};
class AsyncLeaderResponse : public etcdv3::V3Response
{
public:
AsyncLeaderResponse(){};
void ParseResponse(LeaderResponse& resp);
};
class AsyncObserveResponse : public etcdv3::V3Response
{
public:
AsyncObserveResponse(){};
void ParseResponse(LeaderResponse& resp);
};
class AsyncResignResponse : public etcdv3::V3Response
{
public:
AsyncResignResponse(){};
void ParseResponse(ResignResponse& resp);
};
}
#endif

View File

@ -1,491 +0,0 @@
#ifndef __ASYNC_GRPC_HPP__
#define __ASYNC_GRPC_HPP__
#include <atomic>
#include <mutex>
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "proto/rpc.pb.h"
#include "proto/v3election.grpc.pb.h"
#include "proto/v3election.pb.h"
#include "proto/v3lock.grpc.pb.h"
#include "proto/v3lock.pb.h"
#include "etcd/Response.hpp"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/V3Response.hpp"
using grpc::ClientAsyncReader;
using grpc::ClientAsyncReaderWriter;
using grpc::ClientAsyncResponseReader;
using etcdserverpb::KV;
using etcdserverpb::DeleteRangeRequest;
using etcdserverpb::DeleteRangeResponse;
using etcdserverpb::LeaseCheckpointRequest;
using etcdserverpb::LeaseCheckpointResponse;
using etcdserverpb::LeaseGrantRequest;
using etcdserverpb::LeaseGrantResponse;
using etcdserverpb::LeaseKeepAliveRequest;
using etcdserverpb::LeaseKeepAliveResponse;
using etcdserverpb::LeaseLeasesRequest;
using etcdserverpb::LeaseLeasesResponse;
using etcdserverpb::LeaseRevokeRequest;
using etcdserverpb::LeaseRevokeResponse;
using etcdserverpb::LeaseTimeToLiveRequest;
using etcdserverpb::LeaseTimeToLiveResponse;
using etcdserverpb::MemberAddRequest;
using etcdserverpb::MemberAddResponse;
using etcdserverpb::MemberListRequest;
using etcdserverpb::MemberListResponse;
using etcdserverpb::MemberRemoveRequest;
using etcdserverpb::MemberRemoveResponse;
using etcdserverpb::PutRequest;
using etcdserverpb::PutResponse;
using etcdserverpb::RangeRequest;
using etcdserverpb::RangeResponse;
using etcdserverpb::TxnRequest;
using etcdserverpb::TxnResponse;
using etcdserverpb::WatchRequest;
using etcdserverpb::WatchResponse;
using v3electionpb::CampaignRequest;
using v3electionpb::CampaignResponse;
using v3electionpb::LeaderRequest;
using v3electionpb::LeaderResponse;
using v3electionpb::ProclaimRequest;
using v3electionpb::ProclaimResponse;
using v3electionpb::ResignRequest;
using v3electionpb::ResignResponse;
using v3lockpb::LockRequest;
using v3lockpb::LockResponse;
using v3lockpb::UnlockRequest;
using v3lockpb::UnlockResponse;
namespace etcd {
class KeepAlive;
}
namespace etcdv3 {
class Transaction;
}
namespace etcdv3 {
class AsyncCampaignResponse : public etcdv3::V3Response {
public:
AsyncCampaignResponse(){};
void ParseResponse(CampaignResponse& resp);
};
class AsyncDeleteResponse : public etcdv3::V3Response {
public:
AsyncDeleteResponse(){};
void ParseResponse(DeleteRangeResponse& resp);
};
class AsyncHeadResponse : public etcdv3::V3Response {
public:
AsyncHeadResponse(){};
void ParseResponse(RangeResponse& resp);
};
class AsyncLeaderResponse : public etcdv3::V3Response {
public:
AsyncLeaderResponse(){};
void ParseResponse(LeaderResponse& resp);
};
class AsyncLeaseGrantResponse : public etcdv3::V3Response {
public:
AsyncLeaseGrantResponse(){};
void ParseResponse(LeaseGrantResponse& resp);
};
class AsyncLeaseKeepAliveResponse : public etcdv3::V3Response {
public:
AsyncLeaseKeepAliveResponse(){};
void ParseResponse(LeaseKeepAliveResponse& resp);
};
class AsyncMemberAddResponse : public etcdv3::V3Response {
public:
AsyncMemberAddResponse(){};
void ParseResponse(MemberAddResponse& resp);
};
class AsyncMemberListResponse : public etcdv3::V3Response {
public:
AsyncMemberListResponse(){};
void ParseResponse(MemberListResponse& resp);
};
class AsyncMemberRemoveResponse : public etcdv3::V3Response {
public:
AsyncMemberRemoveResponse(){};
void ParseResponse(MemberRemoveResponse& resp);
};
class AsyncLeaseLeasesResponse : public etcdv3::V3Response {
public:
AsyncLeaseLeasesResponse(){};
void ParseResponse(LeaseLeasesResponse& resp);
};
class AsyncLeaseRevokeResponse : public etcdv3::V3Response {
public:
AsyncLeaseRevokeResponse(){};
void ParseResponse(LeaseRevokeResponse& resp);
};
class AsyncLeaseTimeToLiveResponse : public etcdv3::V3Response {
public:
AsyncLeaseTimeToLiveResponse(){};
void ParseResponse(LeaseTimeToLiveResponse& resp);
};
class AsyncLockResponse : public etcdv3::V3Response {
public:
AsyncLockResponse(){};
void ParseResponse(LockResponse& resp);
};
class AsyncObserveResponse : public etcdv3::V3Response {
public:
AsyncObserveResponse(){};
void ParseResponse(LeaderResponse& resp);
};
class AsyncProclaimResponse : public etcdv3::V3Response {
public:
AsyncProclaimResponse(){};
void ParseResponse(ProclaimResponse& resp);
};
class AsyncPutResponse : public etcdv3::V3Response {
public:
AsyncPutResponse(){};
void ParseResponse(PutResponse& resp);
};
class AsyncRangeResponse : public etcdv3::V3Response {
public:
AsyncRangeResponse(){};
void ParseResponse(RangeResponse& resp, bool prefix = false);
};
class AsyncResignResponse : public etcdv3::V3Response {
public:
AsyncResignResponse(){};
void ParseResponse(ResignResponse& resp);
};
class AsyncTxnResponse : public etcdv3::V3Response {
public:
AsyncTxnResponse(){};
void ParseResponse(TxnResponse& resp);
};
class AsyncUnlockResponse : public etcdv3::V3Response {
public:
AsyncUnlockResponse(){};
void ParseResponse(UnlockResponse& resp);
};
class AsyncWatchResponse : public etcdv3::V3Response {
public:
AsyncWatchResponse(){};
void ParseResponse(WatchResponse& resp);
};
} // namespace etcdv3
namespace etcdv3 {
class AsyncCampaignAction : public etcdv3::Action {
public:
AsyncCampaignAction(etcdv3::ActionParameters&& params);
AsyncCampaignResponse ParseResponse();
private:
CampaignResponse reply;
std::unique_ptr<ClientAsyncResponseReader<CampaignResponse>> response_reader;
};
class AsyncCompareAndDeleteAction : public etcdv3::Action {
public:
AsyncCompareAndDeleteAction(etcdv3::ActionParameters&& params,
etcdv3::AtomicityType type);
AsyncTxnResponse ParseResponse();
private:
TxnResponse reply;
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
};
class AsyncCompareAndSwapAction : public etcdv3::Action {
public:
AsyncCompareAndSwapAction(etcdv3::ActionParameters&& params,
etcdv3::AtomicityType type);
AsyncTxnResponse ParseResponse();
private:
TxnResponse reply;
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
};
class AsyncDeleteAction : public etcdv3::Action {
public:
AsyncDeleteAction(etcdv3::ActionParameters&& params);
AsyncDeleteResponse ParseResponse();
private:
DeleteRangeResponse reply;
std::unique_ptr<ClientAsyncResponseReader<DeleteRangeResponse>>
response_reader;
};
class AsyncHeadAction : public etcdv3::Action {
public:
AsyncHeadAction(etcdv3::ActionParameters&& params);
AsyncHeadResponse ParseResponse();
private:
RangeResponse reply;
std::unique_ptr<ClientAsyncResponseReader<RangeResponse>> response_reader;
};
class AsyncLeaderAction : public etcdv3::Action {
public:
AsyncLeaderAction(etcdv3::ActionParameters&& params);
AsyncLeaderResponse ParseResponse();
private:
LeaderResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LeaderResponse>> response_reader;
};
class AsyncLeaseGrantAction : public etcdv3::Action {
public:
AsyncLeaseGrantAction(etcdv3::ActionParameters&& params);
AsyncLeaseGrantResponse ParseResponse();
private:
LeaseGrantResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LeaseGrantResponse>>
response_reader;
};
class AsyncLeaseKeepAliveAction : public etcdv3::Action {
public:
AsyncLeaseKeepAliveAction(etcdv3::ActionParameters&& params);
AsyncLeaseKeepAliveResponse ParseResponse();
etcd::Response Refresh();
void CancelKeepAlive();
bool Cancelled() const;
private:
etcdv3::ActionParameters& mutable_parameters();
LeaseKeepAliveResponse reply;
std::unique_ptr<
ClientAsyncReaderWriter<LeaseKeepAliveRequest, LeaseKeepAliveResponse>>
stream;
LeaseKeepAliveRequest req;
std::atomic_bool isCancelled;
std::recursive_mutex protect_is_cancelled;
friend class etcd::KeepAlive;
};
class AsyncAddMemberAction : public etcdv3::Action {
public:
AsyncAddMemberAction(etcdv3::ActionParameters&& params);
AsyncMemberAddResponse ParseResponse();
private:
MemberAddResponse reply;
std::unique_ptr<ClientAsyncResponseReader<MemberAddResponse>> response_reader;
};
class AsyncListMemberAction : public etcdv3::Action {
public:
AsyncListMemberAction(etcdv3::ActionParameters&& params);
AsyncMemberListResponse ParseResponse();
private:
MemberListResponse reply;
std::unique_ptr<ClientAsyncResponseReader<MemberListResponse>>
response_reader;
};
class AsyncRemoveMemberAction : public etcdv3::Action {
public:
AsyncRemoveMemberAction(etcdv3::ActionParameters&& params);
AsyncMemberRemoveResponse ParseResponse();
private:
MemberRemoveResponse reply;
std::unique_ptr<ClientAsyncResponseReader<MemberRemoveResponse>>
response_reader;
};
class AsyncLeaseLeasesAction : public etcdv3::Action {
public:
AsyncLeaseLeasesAction(etcdv3::ActionParameters&& params);
AsyncLeaseLeasesResponse ParseResponse();
private:
LeaseLeasesResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LeaseLeasesResponse>>
response_reader;
};
class AsyncLeaseRevokeAction : public etcdv3::Action {
public:
AsyncLeaseRevokeAction(etcdv3::ActionParameters&& params);
AsyncLeaseRevokeResponse ParseResponse();
private:
LeaseRevokeResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LeaseRevokeResponse>>
response_reader;
};
class AsyncLeaseTimeToLiveAction : public etcdv3::Action {
public:
AsyncLeaseTimeToLiveAction(etcdv3::ActionParameters&& params);
AsyncLeaseTimeToLiveResponse ParseResponse();
private:
LeaseTimeToLiveResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LeaseTimeToLiveResponse>>
response_reader;
};
class AsyncLockAction : public etcdv3::Action {
public:
AsyncLockAction(etcdv3::ActionParameters&& params);
AsyncLockResponse ParseResponse();
private:
LockResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LockResponse>> response_reader;
};
class AsyncObserveAction : public etcdv3::Action {
public:
AsyncObserveAction(etcdv3::ActionParameters&& params);
AsyncObserveResponse ParseResponse();
void waitForResponse();
void CancelObserve();
bool Cancelled() const;
private:
LeaderResponse reply;
std::unique_ptr<ClientAsyncReader<LeaderResponse>> response_reader;
std::atomic_bool isCancelled;
std::mutex protect_is_cancelled;
};
class AsyncProclaimAction : public etcdv3::Action {
public:
AsyncProclaimAction(etcdv3::ActionParameters&& params);
AsyncProclaimResponse ParseResponse();
private:
ProclaimResponse reply;
std::unique_ptr<ClientAsyncResponseReader<ProclaimResponse>> response_reader;
};
class AsyncPutAction : public etcdv3::Action {
public:
AsyncPutAction(etcdv3::ActionParameters&& params);
AsyncPutResponse ParseResponse();
private:
PutResponse reply;
std::unique_ptr<ClientAsyncResponseReader<PutResponse>> response_reader;
};
class AsyncRangeAction : public etcdv3::Action {
public:
AsyncRangeAction(etcdv3::ActionParameters&& params);
AsyncRangeResponse ParseResponse();
private:
RangeResponse reply;
std::unique_ptr<ClientAsyncResponseReader<RangeResponse>> response_reader;
};
class AsyncResignAction : public etcdv3::Action {
public:
AsyncResignAction(etcdv3::ActionParameters&& params);
AsyncResignResponse ParseResponse();
private:
ResignResponse reply;
std::unique_ptr<ClientAsyncResponseReader<ResignResponse>> response_reader;
};
class AsyncSetAction : public etcdv3::Action {
public:
AsyncSetAction(etcdv3::ActionParameters&& params, bool create = false);
AsyncTxnResponse ParseResponse();
private:
TxnResponse reply;
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
bool isCreate;
};
class AsyncTxnAction : public etcdv3::Action {
public:
AsyncTxnAction(etcdv3::ActionParameters&& params,
etcdv3::Transaction const& tx);
AsyncTxnResponse ParseResponse();
private:
TxnResponse reply;
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
};
class AsyncUnlockAction : public etcdv3::Action {
public:
AsyncUnlockAction(etcdv3::ActionParameters&& params);
AsyncUnlockResponse ParseResponse();
private:
UnlockResponse reply;
std::unique_ptr<ClientAsyncResponseReader<UnlockResponse>> response_reader;
};
class AsyncUpdateAction : public etcdv3::Action {
public:
AsyncUpdateAction(etcdv3::ActionParameters&& params);
AsyncTxnResponse ParseResponse();
private:
TxnResponse reply;
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
};
class AsyncWatchAction : public etcdv3::Action {
public:
AsyncWatchAction(etcdv3::ActionParameters&& params);
AsyncWatchResponse ParseResponse();
void waitForResponse();
void waitForResponse(std::function<void(etcd::Response)> callback);
void CancelWatch();
bool Cancelled() const;
private:
int64_t watch_id = -1;
WatchResponse reply;
std::unique_ptr<ClientAsyncReaderWriter<WatchRequest, WatchResponse>> stream;
std::atomic_bool isCancelled;
};
} // namespace etcdv3
#endif

View File

@ -0,0 +1,26 @@
#ifndef __ASYNC_HEAD_HPP__
#define __ASYNC_HEAD_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncHeadResponse.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::RangeResponse;
namespace etcdv3
{
class AsyncHeadAction : public etcdv3::Action
{
public:
AsyncHeadAction(etcdv3::ActionParameters && params);
AsyncHeadResponse ParseResponse();
private:
RangeResponse reply;
std::unique_ptr<ClientAsyncResponseReader<RangeResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,22 @@
#ifndef __ASYNC_HEADRESPONSE_HPP__
#define __ASYNC_HEADRESPONSE_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/V3Response.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::RangeResponse;
namespace etcdv3
{
class AsyncHeadResponse : public etcdv3::V3Response
{
public:
AsyncHeadResponse(){};
void ParseResponse(RangeResponse& resp);
};
}
#endif

View File

@ -0,0 +1,89 @@
#ifndef __ASYNC_LEASEACTION_HPP__
#define __ASYNC_LEASEACTION_HPP__
#include <mutex>
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncLeaseResponse.hpp"
#include "etcd/Response.hpp"
using grpc::ClientAsyncResponseReader;
using grpc::ClientAsyncReaderWriter;
using etcdserverpb::LeaseGrantResponse;
using etcdserverpb::LeaseRevokeResponse;
using etcdserverpb::LeaseCheckpoint;
using etcdserverpb::LeaseCheckpointResponse;
using etcdserverpb::LeaseKeepAliveRequest;
using etcdserverpb::LeaseKeepAliveResponse;
using etcdserverpb::LeaseTimeToLiveResponse;
using etcdserverpb::LeaseStatus;
using etcdserverpb::LeaseLeasesResponse;
namespace etcd {
class KeepAlive;
}
namespace etcdv3
{
class AsyncLeaseGrantAction : public etcdv3::Action {
public:
AsyncLeaseGrantAction(etcdv3::ActionParameters && params);
AsyncLeaseGrantResponse ParseResponse();
private:
LeaseGrantResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LeaseGrantResponse>> response_reader;
};
class AsyncLeaseRevokeAction: public etcdv3::Action {
public:
AsyncLeaseRevokeAction(etcdv3::ActionParameters && params);
AsyncLeaseRevokeResponse ParseResponse();
private:
LeaseRevokeResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LeaseRevokeResponse>> response_reader;
};
class AsyncLeaseKeepAliveAction: public etcdv3::Action {
public:
AsyncLeaseKeepAliveAction(etcdv3::ActionParameters && params);
AsyncLeaseKeepAliveResponse ParseResponse();
etcd::Response Refresh();
void CancelKeepAlive();
bool Cancelled() const;
private:
etcdv3::ActionParameters& mutable_parameters();
LeaseKeepAliveResponse reply;
std::unique_ptr<ClientAsyncReaderWriter<LeaseKeepAliveRequest, LeaseKeepAliveResponse>> stream;
LeaseKeepAliveRequest req;
bool isCancelled;
std::recursive_mutex protect_is_cancelled;
friend class etcd::KeepAlive;
};
class AsyncLeaseTimeToLiveAction: public etcdv3::Action {
public:
AsyncLeaseTimeToLiveAction(etcdv3::ActionParameters && params);
AsyncLeaseTimeToLiveResponse ParseResponse();
private:
LeaseTimeToLiveResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LeaseTimeToLiveResponse>> response_reader;
};
class AsyncLeaseLeasesAction: public etcdv3::Action {
public:
AsyncLeaseLeasesAction(etcdv3::ActionParameters && params);
AsyncLeaseLeasesResponse ParseResponse();
private:
LeaseLeasesResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LeaseLeasesResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,55 @@
#ifndef __ASYNC_LEASERESPONSE_HPP__
#define __ASYNC_LEASERESPONSE_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/V3Response.hpp"
using etcdserverpb::LeaseGrantResponse;
using etcdserverpb::LeaseRevokeResponse;
using etcdserverpb::LeaseCheckpoint;
using etcdserverpb::LeaseCheckpointResponse;
using etcdserverpb::LeaseKeepAliveResponse;
using etcdserverpb::LeaseTimeToLiveResponse;
using etcdserverpb::LeaseStatus;
using etcdserverpb::LeaseLeasesResponse;
namespace etcdv3
{
class AsyncLeaseGrantResponse : public etcdv3::V3Response
{
public:
AsyncLeaseGrantResponse(){};
void ParseResponse(LeaseGrantResponse& resp);
};
class AsyncLeaseRevokeResponse : public etcdv3::V3Response
{
public:
AsyncLeaseRevokeResponse(){};
void ParseResponse(LeaseRevokeResponse& resp);
};
class AsyncLeaseKeepAliveResponse : public etcdv3::V3Response
{
public:
AsyncLeaseKeepAliveResponse(){};
void ParseResponse(LeaseKeepAliveResponse& resp);
};
class AsyncLeaseTimeToLiveResponse : public etcdv3::V3Response
{
public:
AsyncLeaseTimeToLiveResponse(){};
void ParseResponse(LeaseTimeToLiveResponse& resp);
};
class AsyncLeaseLeasesResponse : public etcdv3::V3Response
{
public:
AsyncLeaseLeasesResponse(){};
void ParseResponse(LeaseLeasesResponse& resp);
};
}
#endif

View File

@ -0,0 +1,40 @@
#ifndef __ASYNC_LOCKACTION_HPP__
#define __ASYNC_LOCKACTION_HPP__
#include <grpc++/grpc++.h>
#include "proto/v3lock.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncLockResponse.hpp"
#include "etcd/Response.hpp"
using grpc::ClientAsyncResponseReader;
using v3lockpb::LockRequest;
using v3lockpb::LockResponse;
using v3lockpb::UnlockRequest;
using v3lockpb::UnlockResponse;
namespace etcdv3
{
class AsyncLockAction : public etcdv3::Action
{
public:
AsyncLockAction(etcdv3::ActionParameters && params);
AsyncLockResponse ParseResponse();
private:
LockResponse reply;
std::unique_ptr<ClientAsyncResponseReader<LockResponse>> response_reader;
};
class AsyncUnlockAction : public etcdv3::Action
{
public:
AsyncUnlockAction(etcdv3::ActionParameters && params);
AsyncUnlockResponse ParseResponse();
private:
UnlockResponse reply;
std::unique_ptr<ClientAsyncResponseReader<UnlockResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,32 @@
#ifndef __ASYNC_LOCK_HPP__
#define __ASYNC_LOCK_HPP__
#include <grpc++/grpc++.h>
#include "proto/v3lock.grpc.pb.h"
#include "etcd/v3/V3Response.hpp"
using grpc::ClientAsyncResponseReader;
using v3lockpb::LockRequest;
using v3lockpb::LockResponse;
using v3lockpb::UnlockRequest;
using v3lockpb::UnlockResponse;
namespace etcdv3
{
class AsyncLockResponse : public etcdv3::V3Response
{
public:
AsyncLockResponse(){};
void ParseResponse(LockResponse& resp);
};
class AsyncUnlockResponse : public etcdv3::V3Response
{
public:
AsyncUnlockResponse(){};
void ParseResponse(UnlockResponse& resp);
};
}
#endif

View File

@ -0,0 +1,26 @@
#ifndef __ASYNC_PUT_HPP__
#define __ASYNC_PUT_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncPutResponse.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::PutResponse;
namespace etcdv3
{
class AsyncPutAction : public etcdv3::Action
{
public:
AsyncPutAction(etcdv3::ActionParameters && params);
AsyncPutResponse ParseResponse();
private:
PutResponse reply;
std::unique_ptr<ClientAsyncResponseReader<PutResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,23 @@
#ifndef __ASYNC_PUTRESPONSE_HPP__
#define __ASYNC_PUTRESPONSE_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/V3Response.hpp"
#include "etcd/v3/Action.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::PutResponse;
namespace etcdv3
{
class AsyncPutResponse : public etcdv3::V3Response
{
public:
AsyncPutResponse(){};
void ParseResponse(PutResponse& resp);
};
}
#endif

View File

@ -0,0 +1,26 @@
#ifndef __ASYNC_RANGE_HPP__
#define __ASYNC_RANGE_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncRangeResponse.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::RangeResponse;
namespace etcdv3
{
class AsyncRangeAction : public etcdv3::Action
{
public:
AsyncRangeAction(etcdv3::ActionParameters && params);
AsyncRangeResponse ParseResponse();
private:
RangeResponse reply;
std::unique_ptr<ClientAsyncResponseReader<RangeResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,22 @@
#ifndef __ASYNC_RANGERESPONSE_HPP__
#define __ASYNC_RANGERESPONSE_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/V3Response.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::RangeResponse;
namespace etcdv3
{
class AsyncRangeResponse : public etcdv3::V3Response
{
public:
AsyncRangeResponse(){};
void ParseResponse(RangeResponse& resp, bool prefix=false);
};
}
#endif

View File

@ -0,0 +1,28 @@
#ifndef __ASYNC_SET_HPP__
#define __ASYNC_SET_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncTxnResponse.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::TxnResponse;
using etcdserverpb::KV;
namespace etcdv3
{
class AsyncSetAction : public etcdv3::Action
{
public:
AsyncSetAction(etcdv3::ActionParameters && params, bool create=false);
AsyncTxnResponse ParseResponse();
private:
TxnResponse reply;
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
bool isCreate;
};
}
#endif

View File

@ -0,0 +1,29 @@
#ifndef __ASYNC_TXNACTION_HPP__
#define __ASYNC_TXNACTION_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncTxnResponse.hpp"
#include "etcd/v3/Transaction.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::TxnRequest;
using etcdserverpb::TxnResponse;
using etcdserverpb::KV;
namespace etcdv3
{
class AsyncTxnAction : public etcdv3::Action
{
public:
AsyncTxnAction(etcdv3::ActionParameters && params, etcdv3::Transaction const &tx);
AsyncTxnResponse ParseResponse();
private:
TxnResponse reply;
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,20 @@
#ifndef __ASYNC_TXNRESPONSE_HPP__
#define __ASYNC_TXNRESPONSE_HPP__
#include "proto/rpc.pb.h"
#include "etcd/v3/V3Response.hpp"
using etcdserverpb::TxnResponse;
namespace etcdv3
{
class AsyncTxnResponse : public etcdv3::V3Response
{
public:
AsyncTxnResponse(){};
void ParseResponse(TxnResponse& resp);
void ParseResponse(std::string const& key, bool prefix, TxnResponse& resp);
};
}
#endif

View File

@ -0,0 +1,27 @@
#ifndef __ASYNC_UPDATE_HPP__
#define __ASYNC_UPDATE_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncTxnResponse.hpp"
using grpc::ClientAsyncResponseReader;
using etcdserverpb::TxnResponse;
using etcdserverpb::KV;
namespace etcdv3
{
class AsyncUpdateAction : public etcdv3::Action
{
public:
AsyncUpdateAction(etcdv3::ActionParameters && params);
AsyncTxnResponse ParseResponse();
private:
TxnResponse reply;
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
};
}
#endif

View File

@ -0,0 +1,36 @@
#ifndef __ASYNC_WATCHACTION_HPP__
#define __ASYNC_WATCHACTION_HPP__
#include <atomic>
#include <mutex>
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncWatchResponse.hpp"
#include "etcd/Response.hpp"
using grpc::ClientAsyncReaderWriter;
using etcdserverpb::WatchRequest;
using etcdserverpb::WatchResponse;
namespace etcdv3
{
class AsyncWatchAction : public etcdv3::Action
{
public:
AsyncWatchAction(etcdv3::ActionParameters && params);
AsyncWatchResponse ParseResponse();
void waitForResponse();
void waitForResponse(std::function<void(etcd::Response)> callback);
void CancelWatch();
bool Cancelled() const;
private:
int64_t watch_id = -1;
WatchResponse reply;
std::unique_ptr<ClientAsyncReaderWriter<WatchRequest,WatchResponse>> stream;
std::atomic_bool isCancelled;
};
}
#endif

View File

@ -0,0 +1,25 @@
#ifndef __ASYNC_WATCH_HPP__
#define __ASYNC_WATCH_HPP__
#include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h"
#include "proto/rpc.pb.h"
#include "etcd/v3/V3Response.hpp"
using etcdserverpb::WatchRequest;
using etcdserverpb::WatchResponse;
using etcdserverpb::KV;
namespace etcdv3
{
class AsyncWatchResponse : public etcdv3::V3Response
{
public:
AsyncWatchResponse(){};
void ParseResponse(WatchResponse& resp);
};
}
#endif

View File

@ -3,16 +3,18 @@
#include "proto/kv.pb.h" #include "proto/kv.pb.h"
namespace etcdv3 {
class KeyValue { namespace etcdv3
{
class KeyValue
{
public: public:
KeyValue(); KeyValue();
mvccpb::KeyValue kvs; mvccpb::KeyValue kvs;
void set_ttl(int ttl); void set_ttl(int ttl);
int get_ttl() const; int get_ttl() const;
private: private:
int ttl; int ttl;
}; };
} // namespace etcdv3 }
#endif #endif

View File

@ -1,33 +0,0 @@
#ifndef __V3_ETCDV3MEMBERS_HPP__
#define __V3_ETCDV3MEMBERS_HPP__
#include <cstdint>
#include <string>
#include <vector>
using namespace std;
namespace etcdv3 {
class Member {
public:
Member();
void set_id(uint64_t const& id);
uint64_t const& get_id() const;
void set_name(std::string const& name);
std::string const& get_name() const;
void set_peerURLs(std::vector<std::string> const& peerURLs);
std::vector<std::string> const& get_peerURLs() const;
void set_clientURLs(std::vector<std::string> const& clientURLs);
std::vector<std::string> const& get_clientURLs() const;
void set_learner(bool isLearner);
bool get_learner() const;
private:
uint64_t id;
std::string name;
std::vector<std::string> peerURLs;
std::vector<std::string> clientURLs;
bool isLearner;
};
} // namespace etcdv3
#endif

View File

@ -1,8 +1,8 @@
#ifndef V3_SRC_TRANSACTION_HPP_ #ifndef V3_SRC_TRANSACTION_HPP_
#define V3_SRC_TRANSACTION_HPP_ #define V3_SRC_TRANSACTION_HPP_
#include <memory>
#include <string> #include <string>
#include <memory>
namespace etcdserverpb { namespace etcdserverpb {
class TxnRequest; class TxnRequest;
@ -28,196 +28,33 @@ enum class CompareTarget {
class Transaction { class Transaction {
public: public:
Transaction(); Transaction();
~Transaction(); Transaction(std::string const&);
virtual ~Transaction();
union Value { // Set a new key for different comparisons and /put/get/delete requests.
int64_t version; void reset_key(std::string const& newkey);
int64_t create_revision;
int64_t mod_revision;
std::string value;
int64_t lease;
};
void add_compare(std::string const& key, CompareTarget const& target, void init_compare(CompareResult, CompareTarget);
CompareResult const& result, Value const& target_value, void init_compare(std::string const &old_value, CompareResult, CompareTarget);
std::string const& range_end = ""); void init_compare(int64_t old_value, CompareResult, CompareTarget);
void add_compare_version(std::string const& key, int64_t const& version, void setup_basic_failure_operation(std::string const &key);
std::string const& range_end = ""); void setup_set_failure_operation(std::string const &key, std::string const &value, int64_t leaseid);
void add_compare_version(std::string const& key, CompareResult const& result, void setup_basic_create_sequence(std::string const &key, std::string const &value, int64_t leaseid);
int64_t const& version, void setup_compare_and_swap_sequence(std::string const &valueToSwap, int64_t leaseid);
std::string const& range_end = ""); void setup_delete_sequence(std::string const &key, std::string const &range_end, bool recursive);
void add_compare_create(std::string const& key, void setup_delete_failure_operation(std::string const &key, std::string const &range_end, bool recursive);
int64_t const& create_revision, void setup_compare_and_delete_operation(std::string const& key);
std::string const& range_end = "");
void add_compare_create(std::string const& key, CompareResult const& result,
int64_t const& create_revision,
std::string const& range_end = "");
void add_compare_mod(std::string const& key, int64_t const& mod_revision,
std::string const& range_end = "");
void add_compare_mod(std::string const& key, CompareResult const& result,
int64_t const& mod_revision,
std::string const& range_end = "");
void add_compare_value(std::string const& key, std::string const& value,
std::string const& range_end = "");
void add_compare_value(std::string const& key, CompareResult const& result,
std::string const& value,
std::string const& range_end = "");
void add_compare_lease(std::string const& key, int64_t const& lease,
std::string const& range_end = "");
void add_compare_lease(std::string const& key, CompareResult const& result,
int64_t const& lease,
std::string const& range_end = "");
void add_success_range(std::string const& key,
std::string const& range_end = "",
bool const recursive = false, const int64_t limit = 0);
void add_success_put(std::string const& key, std::string const& value,
int64_t const leaseid = 0, const bool prev_kv = false);
void add_success_delete(std::string const& key,
std::string const& range_end = "",
bool const recursive = false,
const bool prev_kv = false);
void add_success_txn(const std::shared_ptr<Transaction> txn);
void add_failure_range(std::string const& key,
std::string const& range_end = "",
bool const recursive = false, const int64_t limit = 0);
void add_failure_put(std::string const& key, std::string const& value,
int64_t const leaseid = 0, const bool prev_kv = false);
void add_failure_delete(std::string const& key,
std::string const& range_end = "",
bool const recursive = false,
const bool prev_kv = false);
void add_failure_txn(const std::shared_ptr<Transaction> txn);
/**
* @brief Compare, and create if succeed. If failed, the response will
* contains the previous value in "values()" field.
*/
void setup_compare_and_create(std::string const& key,
std::string const& prev_value,
std::string const& create_key,
std::string const& value,
int64_t const leaseid = 0);
/**
* @brief Compare, or create if failed. If failed, the response will contains
* the previous value in "values()" field.
*/
void setup_compare_or_create(std::string const& key,
std::string const& prev_value,
std::string const& create_key,
std::string const& value,
int64_t const leaseid = 0);
/**
* @brief Compare, and swap if succeed. If failed, the response will contains
* the previous value in "values()" field.
*/
void setup_compare_and_swap(std::string const& key,
std::string const& prev_value,
std::string const& value,
int64_t const leaseid = 0);
/**
* @brief Compare, and swap if failed. If failed, the response will contains
* the previous value in "values()" field.
*/
void setup_compare_or_swap(std::string const& key,
std::string const& prev_value,
std::string const& value,
int64_t const leaseid = 0);
/**
* @brief Compare, and delete if succeed. If failed, the response will
* contains the previous value in "values()" field.
*/
void setup_compare_and_delete(std::string const& key,
std::string const& prev_value,
std::string const& delete_key,
std::string const& range_end = "",
const bool recursive = false);
/**
* @brief Compare, or delete if failed. If failed, the response will contains
* the previous value in "values()" field.
*/
void setup_compare_or_delete(std::string const& key,
std::string const& prev_value,
std::string const& delete_key,
std::string const& range_end = "",
const bool recursive = false);
/**
* @brief Compare, and create if succeed. If failed, the response will
* contains the previous value in "values()" field.
*/
void setup_compare_and_create(std::string const& key,
const int64_t prev_revision,
std::string const& create_key,
std::string const& value,
int64_t const leaseid = 0);
/**
* @brief Compare, or create if failed. If failed, the response will contains
* the previous value in "values()" field.
*/
void setup_compare_or_create(std::string const& key,
const int64_t prev_revision,
std::string const& create_key,
std::string const& value,
int64_t const leaseid = 0);
/**
* @brief Compare, and swap if succeed. If failed, the response will contains
* the previous value in "values()" field.
*/
void setup_compare_and_swap(std::string const& key,
const int64_t prev_revision,
std::string const& value,
int64_t const leaseid = 0);
/**
* @brief Compare, and swap if failed. If failed, the response will contains
* the previous value in "values()" field.
*/
void setup_compare_or_swap(std::string const& key,
const int64_t prev_revision,
std::string const& value,
int64_t const leaseid = 0);
/**
* @brief Compare, and delete if succeed. If failed, the response will
* contains the previous value in "values()" field.
*/
void setup_compare_and_delete(std::string const& key,
const int64_t prev_revision,
std::string const& delete_key,
std::string const& range_end = "",
const bool recursive = false);
/**
* @brief Compare, or delete if failed. If failed, the response will contains
* the previous value in "values()" field.
*/
void setup_compare_or_delete(std::string const& key,
const int64_t prev_revision,
std::string const& delete_key,
std::string const& range_end = "",
const bool recursive = false);
// Keep for backwards compatibility.
// update without `get` and no `prev_kv` returned // update without `get` and no `prev_kv` returned
void setup_put(std::string const &key, std::string const &value); void setup_put(std::string const &key, std::string const &value);
void setup_delete(std::string const &key); void setup_delete(std::string const &key);
void setup_delete(std::string const& key, std::string const& range_end,
const bool recursive = false);
std::shared_ptr<etcdserverpb::TxnRequest> txn_request; std::shared_ptr<etcdserverpb::TxnRequest> txn_request;
private:
std::string key;
}; };
} // namespace etcdv3 }
#endif #endif

View File

@ -6,10 +6,11 @@
#include "proto/v3election.pb.h" #include "proto/v3election.pb.h"
#include "etcd/v3/KeyValue.hpp" #include "etcd/v3/KeyValue.hpp"
#include "etcd/v3/Member.hpp"
namespace etcdv3 { namespace etcdv3
class V3Response { {
class V3Response
{
public: public:
V3Response(): error_code(0), index(0){}; V3Response(): error_code(0), index(0){};
void set_error_code(int code); void set_error_code(int code);
@ -25,9 +26,6 @@ class V3Response {
etcdv3::KeyValue const & get_prev_value() const; etcdv3::KeyValue const & get_prev_value() const;
bool has_values() const; bool has_values() const;
int64_t get_compact_revision() const; int64_t get_compact_revision() const;
void set_compact_revision(const int64_t compact_revision);
int64_t get_watch_id() const;
void set_watch_id(const int64_t watch_id);
void set_lock_key(std::string const &key); void set_lock_key(std::string const &key);
std::string const &get_lock_key() const; std::string const &get_lock_key() const;
void set_name(std::string const &name); void set_name(std::string const &name);
@ -37,8 +35,6 @@ class V3Response {
uint64_t get_member_id() const; uint64_t get_member_id() const;
uint64_t get_raft_term() const; uint64_t get_raft_term() const;
std::vector<int64_t> const &get_leases() const; std::vector<int64_t> const &get_leases() const;
std::vector<etcdv3::Member> const& get_members() const;
protected: protected:
int error_code; int error_code;
int64_t index; int64_t index;
@ -49,7 +45,6 @@ class V3Response {
std::vector<etcdv3::KeyValue> values; std::vector<etcdv3::KeyValue> values;
std::vector<etcdv3::KeyValue> prev_values; std::vector<etcdv3::KeyValue> prev_values;
int64_t compact_revision = -1; int64_t compact_revision = -1;
int64_t watch_id = -1;
std::string lock_key; // for lock std::string lock_key; // for lock
std::string name; // for campaign (in v3election) std::string name; // for campaign (in v3election)
std::vector<mvccpb::Event> events; // for watch std::vector<mvccpb::Event> events; // for watch
@ -61,8 +56,6 @@ class V3Response {
// for lease list // for lease list
std::vector<int64_t> leases; std::vector<int64_t> leases;
// for member list
std::vector<etcdv3::Member> members;
}; };
} // namespace etcdv3 }
#endif #endif

View File

@ -3,7 +3,8 @@
#include <string> #include <string>
namespace etcdv3 { namespace etcdv3
{
extern char const * CREATE_ACTION; extern char const * CREATE_ACTION;
extern char const * UPDATE_ACTION; extern char const * UPDATE_ACTION;
extern char const * SET_ACTION; extern char const * SET_ACTION;
@ -23,10 +24,6 @@ extern char const* LEASEKEEPALIVE;
extern char const * LEASETIMETOLIVE; extern char const * LEASETIMETOLIVE;
extern char const * LEASELEASES; extern char const * LEASELEASES;
extern char const* ADDMEMBER;
extern char const* LISTMEMBER;
extern char const* REMOVEMEMBER;
extern char const * CAMPAIGN_ACTION; extern char const * CAMPAIGN_ACTION;
extern char const * PROCLAIM_ACTION; extern char const * PROCLAIM_ACTION;
extern char const * LEADER_ACTION; extern char const * LEADER_ACTION;
@ -48,7 +45,6 @@ extern char const* WATCH_WRITES_DONE;
extern char const * WATCH_FINISH; extern char const * WATCH_FINISH;
extern char const * ELECTION_OBSERVE_CREATE; extern char const * ELECTION_OBSERVE_CREATE;
extern char const* ELECTION_OBSERVE_FINISH;
extern const int ERROR_GRPC_OK; extern const int ERROR_GRPC_OK;
extern const int ERROR_GRPC_CANCELLED; extern const int ERROR_GRPC_CANCELLED;
@ -72,6 +68,6 @@ extern const int ERROR_KEY_NOT_FOUND;
extern const int ERROR_COMPARE_FAILED; extern const int ERROR_COMPARE_FAILED;
extern const int ERROR_KEY_ALREADY_EXISTS; extern const int ERROR_KEY_ALREADY_EXISTS;
extern const int ERROR_ACTION_CANCELLED; extern const int ERROR_ACTION_CANCELLED;
} // namespace etcdv3 }
#endif #endif

View File

@ -19,50 +19,38 @@ file(GLOB_RECURSE CPP_CLIENT_CORE_SRC
add_library(etcd-cpp-api-core-objects OBJECT ${CPP_CLIENT_CORE_SRC} ${PROTOBUF_GENERATES}) add_library(etcd-cpp-api-core-objects OBJECT ${CPP_CLIENT_CORE_SRC} ${PROTOBUF_GENERATES})
use_cxx(etcd-cpp-api-core-objects) use_cxx(etcd-cpp-api-core-objects)
set_exceptions(etcd-cpp-api-core-objects)
add_dependencies(etcd-cpp-api-core-objects protobuf_generates) add_dependencies(etcd-cpp-api-core-objects protobuf_generates)
include_generated_protobuf_files(etcd-cpp-api-core-objects) include_generated_protobuf_files(etcd-cpp-api-core-objects)
target_link_libraries(etcd-cpp-api-core-objects PUBLIC target_link_libraries(etcd-cpp-api-core-objects PUBLIC
${Boost_LIBRARIES}
${PROTOBUF_LIBRARIES}
${OPENSSL_LIBRARIES} ${OPENSSL_LIBRARIES}
${GRPC_LIBRARIES} ${GRPC_LIBRARIES}
) )
if(TARGET protobuf::libprotobuf)
target_link_libraries(etcd-cpp-api-core-objects PUBLIC protobuf::libprotobuf)
else()
target_link_libraries(etcd-cpp-api-core-objects PUBLIC ${PROTOBUF_LIBRARIES})
endif()
if(BUILD_ETCD_CORE_ONLY) if(BUILD_ETCD_CORE_ONLY)
# add the core library, includes the sycnhronous client only # add the core library, includes the sycnhronous client only
add_library(etcd-cpp-api-core $<TARGET_OBJECTS:etcd-cpp-api-core-objects>) add_library(etcd-cpp-api-core $<TARGET_OBJECTS:etcd-cpp-api-core-objects>)
use_cxx(etcd-cpp-api-core) use_cxx(etcd-cpp-api-core)
set_exceptions(etcd-cpp-api-core)
target_link_libraries(etcd-cpp-api-core PUBLIC target_link_libraries(etcd-cpp-api-core PUBLIC
${Boost_LIBRARIES}
${PROTOBUF_LIBRARIES}
${OPENSSL_LIBRARIES} ${OPENSSL_LIBRARIES}
${GRPC_LIBRARIES} ${GRPC_LIBRARIES}
) )
if(TARGET protobuf::libprotobuf)
target_link_libraries(etcd-cpp-api-core PUBLIC protobuf::libprotobuf)
else()
target_link_libraries(etcd-cpp-api-core PUBLIC ${PROTOBUF_LIBRARIES})
endif()
include_generated_protobuf_files(etcd-cpp-api-core) include_generated_protobuf_files(etcd-cpp-api-core)
else() else()
# add the client with asynchronus client # add the client with asynchronus client
add_library(etcd-cpp-api $<TARGET_OBJECTS:etcd-cpp-api-core-objects> add_library(etcd-cpp-api $<TARGET_OBJECTS:etcd-cpp-api-core-objects>
"${CMAKE_CURRENT_SOURCE_DIR}/Client.cpp") "${CMAKE_CURRENT_SOURCE_DIR}/Client.cpp")
use_cxx(etcd-cpp-api) use_cxx(etcd-cpp-api)
set_exceptions(etcd-cpp-api)
target_link_libraries(etcd-cpp-api PUBLIC target_link_libraries(etcd-cpp-api PUBLIC
${Boost_LIBRARIES}
${CPPREST_LIB} # n.b.: the asynchronous client requires pplx in cpprestsdk ${CPPREST_LIB} # n.b.: the asynchronous client requires pplx in cpprestsdk
${PROTOBUF_LIBRARIES}
${OPENSSL_LIBRARIES} ${OPENSSL_LIBRARIES}
${GRPC_LIBRARIES} ${GRPC_LIBRARIES}
) )
if(TARGET protobuf::libprotobuf)
target_link_libraries(etcd-cpp-api PUBLIC protobuf::libprotobuf)
else()
target_link_libraries(etcd-cpp-api PUBLIC ${PROTOBUF_LIBRARIES})
endif()
include_generated_protobuf_files(etcd-cpp-api) include_generated_protobuf_files(etcd-cpp-api)
endif() endif()

View File

@ -1,6 +1,5 @@
#if defined(_WIN32) #if defined(_WIN32)
// see also: // see also: https://stackoverflow.com/questions/2561368/illegal-token-on-right-side-of
// https://stackoverflow.com/questions/2561368/illegal-token-on-right-side-of
#define NOMINMAX #define NOMINMAX
#endif #endif
@ -20,8 +19,8 @@
#endif #endif
#include <chrono> #include <chrono>
#include <fstream>
#include <iostream> #include <iostream>
#include <fstream>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@ -29,36 +28,62 @@
#include <thread> #include <thread>
#include <utility> #include <utility>
#include <boost/algorithm/string.hpp>
#include <grpc++/grpc++.h> #include <grpc++/grpc++.h>
#include <grpc++/security/credentials.h> #include <grpc++/security/credentials.h>
#include "proto/rpc.grpc.pb.h" #include "proto/rpc.grpc.pb.h"
#include "proto/v3election.grpc.pb.h"
#include "proto/v3lock.grpc.pb.h" #include "proto/v3lock.grpc.pb.h"
#include "proto/v3election.grpc.pb.h"
#include "etcd/Client.hpp" #include "etcd/Client.hpp"
#include "etcd/KeepAlive.hpp" #include "etcd/KeepAlive.hpp"
#include "etcd/Watcher.hpp" #include "etcd/Watcher.hpp"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncGRPC.hpp"
#include "etcd/v3/Transaction.hpp"
#include "etcd/v3/action_constants.hpp" #include "etcd/v3/action_constants.hpp"
#include "etcd/v3/Action.hpp"
#include "etcd/v3/AsyncRangeResponse.hpp"
#include "etcd/v3/AsyncWatchResponse.hpp"
#include "etcd/v3/AsyncDeleteResponse.hpp"
#include "etcd/v3/AsyncPutResponse.hpp"
#include "etcd/v3/AsyncLockResponse.hpp"
#include "etcd/v3/AsyncElectionResponse.hpp"
#include "etcd/v3/AsyncTxnResponse.hpp"
#include "etcd/v3/Transaction.hpp"
etcd::Client::Client(etcd::SyncClient* client) : client(client) { #include "etcd/v3/AsyncSetAction.hpp"
#include "etcd/v3/AsyncCompareAndSwapAction.hpp"
#include "etcd/v3/AsyncCompareAndDeleteAction.hpp"
#include "etcd/v3/AsyncUpdateAction.hpp"
#include "etcd/v3/AsyncHeadAction.hpp"
#include "etcd/v3/AsyncRangeAction.hpp"
#include "etcd/v3/AsyncDeleteAction.hpp"
#include "etcd/v3/AsyncPutAction.hpp"
#include "etcd/v3/AsyncWatchAction.hpp"
#include "etcd/v3/AsyncLeaseAction.hpp"
#include "etcd/v3/AsyncLockAction.hpp"
#include "etcd/v3/AsyncElectionAction.hpp"
#include "etcd/v3/AsyncTxnAction.hpp"
etcd::Client::Client(etcd::SyncClient *client): client(client)
{
this->own_client = false; this->own_client = false;
} }
etcd::Client* WithClient(etcd::SyncClient* client) { etcd::Client* WithClient(etcd::SyncClient *client)
{
return new etcd::Client(client); return new etcd::Client(client);
} }
etcd::Client::Client(std::string const & address, etcd::Client::Client(std::string const & address,
std::string const& load_balancer) { std::string const & load_balancer)
{
this->own_client = true; this->own_client = true;
this->client = new SyncClient(address, load_balancer); this->client = new SyncClient(address, load_balancer);
} }
etcd::Client::Client(std::string const & address, etcd::Client::Client(std::string const & address,
grpc::ChannelArguments const& arguments) { grpc::ChannelArguments const & arguments)
{
this->own_client = true; this->own_client = true;
this->client = new SyncClient(address, arguments); this->client = new SyncClient(address, arguments);
} }
@ -73,20 +98,24 @@ etcd::Client* etcd::Client::WithUrl(std::string const& etcd_url,
return new etcd::Client(etcd_url, arguments); return new etcd::Client(etcd_url, arguments);
} }
etcd::Client::Client(std::string const& address, std::string const& username, etcd::Client::Client(std::string const & address,
std::string const& password, int const auth_token_ttl, std::string const & username,
std::string const& load_balancer) { std::string const & password,
int const auth_token_ttl,
std::string const & load_balancer)
{
this->own_client = true; this->own_client = true;
this->client = new SyncClient(address, username, password, auth_token_ttl, this->client = new SyncClient(address, username, password, auth_token_ttl, load_balancer);
load_balancer);
} }
etcd::Client::Client(std::string const& address, std::string const& username, etcd::Client::Client(std::string const & address,
std::string const& password, int const auth_token_ttl, std::string const & username,
grpc::ChannelArguments const& arguments) { std::string const & password,
int const auth_token_ttl,
grpc::ChannelArguments const & arguments)
{
this->own_client = true; this->own_client = true;
this->client = this->client = new SyncClient(address, username, password, auth_token_ttl, arguments);
new SyncClient(address, username, password, auth_token_ttl, arguments);
} }
etcd::Client *etcd::Client::WithUser(std::string const & etcd_url, etcd::Client *etcd::Client::WithUser(std::string const & etcd_url,
@ -94,8 +123,7 @@ etcd::Client* etcd::Client::WithUser(std::string const& etcd_url,
std::string const & password, std::string const & password,
int const auth_token_ttl, int const auth_token_ttl,
std::string const & load_balancer) { std::string const & load_balancer) {
return new etcd::Client(etcd_url, username, password, auth_token_ttl, return new etcd::Client(etcd_url, username, password, auth_token_ttl, load_balancer);
load_balancer);
} }
etcd::Client *etcd::Client::WithUser(std::string const & etcd_url, etcd::Client *etcd::Client::WithUser(std::string const & etcd_url,
@ -103,26 +131,30 @@ etcd::Client* etcd::Client::WithUser(std::string const& etcd_url,
std::string const & password, std::string const & password,
int const auth_token_ttl, int const auth_token_ttl,
grpc::ChannelArguments const & arguments) { grpc::ChannelArguments const & arguments) {
return new etcd::Client(etcd_url, username, password, auth_token_ttl, return new etcd::Client(etcd_url, username, password, auth_token_ttl, arguments);
arguments);
} }
etcd::Client::Client(std::string const& address, std::string const& ca,
std::string const& cert, std::string const& privkey, etcd::Client::Client(std::string const & address,
std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & target_name_override, std::string const & target_name_override,
std::string const& load_balancer) { std::string const & load_balancer)
{
this->own_client = true; this->own_client = true;
this->client = new SyncClient(address, ca, cert, privkey, this->client = new SyncClient(address, ca, cert, privkey, target_name_override, load_balancer);
target_name_override, load_balancer);
} }
etcd::Client::Client(std::string const& address, std::string const& ca, etcd::Client::Client(std::string const & address,
std::string const& cert, std::string const& privkey, std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & target_name_override, std::string const & target_name_override,
grpc::ChannelArguments const& arguments) { grpc::ChannelArguments const & arguments)
{
this->own_client = true; this->own_client = true;
this->client = new SyncClient(address, ca, cert, privkey, this->client = new SyncClient(address, ca, cert, privkey, target_name_override, arguments);
target_name_override, arguments);
} }
etcd::Client *etcd::Client::WithSSL(std::string const & etcd_url, etcd::Client *etcd::Client::WithSSL(std::string const & etcd_url,
@ -131,8 +163,7 @@ etcd::Client* etcd::Client::WithSSL(std::string const& etcd_url,
std::string const & privkey, std::string const & privkey,
std::string const & target_name_override, std::string const & target_name_override,
std::string const & load_balancer) { std::string const & load_balancer) {
return new etcd::Client(etcd_url, ca, cert, privkey, target_name_override, return new etcd::Client(etcd_url, ca, cert, privkey, target_name_override, load_balancer);
load_balancer);
} }
etcd::Client *etcd::Client::WithSSL(std::string const & etcd_url, etcd::Client *etcd::Client::WithSSL(std::string const & etcd_url,
@ -141,8 +172,7 @@ etcd::Client* etcd::Client::WithSSL(std::string const& etcd_url,
std::string const & cert, std::string const & cert,
std::string const & privkey, std::string const & privkey,
std::string const & target_name_override) { std::string const & target_name_override) {
return new etcd::Client(etcd_url, ca, cert, privkey, target_name_override, return new etcd::Client(etcd_url, ca, cert, privkey, target_name_override, arguments);
arguments);
} }
etcd::Client::~Client() { etcd::Client::~Client() {
@ -151,6 +181,7 @@ etcd::Client::~Client() {
} }
} }
namespace etcd { namespace etcd {
namespace detail { namespace detail {
@ -159,46 +190,37 @@ namespace detail {
// Inspired by: https://stackoverflow.com/a/20669290/5080177 // Inspired by: https://stackoverflow.com/a/20669290/5080177
template <typename A, typename F> template <typename A, typename F>
class capture_impl { class capture_impl
{
mutable A a; mutable A a;
mutable F f; mutable F f;
public: public:
capture_impl(A&& a, F&& f) : a{std::forward<A>(a)}, f{std::forward<F>(f)} {} capture_impl(A && a, F && f)
: a{std::forward<A>(a)}, f{std::forward<F>(f)}
{}
template <typename... Ts> template <typename ...Ts> auto operator()( Ts&&...args )
auto operator()(Ts&&... args) -> decltype(std::forward<F>(f)(std::forward<A>(a), std::forward<Ts>(args)... ))
-> decltype(std::forward<F>(f)(std::forward<A>(a), {
std::forward<Ts>(args)...)) {
return std::forward<F>(f)(std::forward<A>(a), std::forward<Ts>(args)... ); return std::forward<F>(f)(std::forward<A>(a), std::forward<Ts>(args)... );
} }
template <typename... Ts> template <typename ...Ts> auto operator()( Ts&&...args ) const
auto operator()(Ts&&... args) const -> decltype(std::forward<F>(f)(std::forward<A>(a), std::forward<Ts>(args)... ))
-> decltype(std::forward<F>(f)(std::forward<A>(a), {
std::forward<Ts>(args)...)) {
return std::forward<F>(f)(std::forward<A>(a), std::forward<Ts>(args)... ); return std::forward<F>(f)(std::forward<A>(a), std::forward<Ts>(args)... );
} }
}; };
template <typename A, typename F> template <typename A, typename F>
static capture_impl<A, F> capture(A&& a, F&& f) { static capture_impl<A, F> capture(A && a, F && f)
{
return capture_impl<A, F>(std::forward<A>(a), std::forward<F>(f)); return capture_impl<A, F>(std::forward<A>(a), std::forward<F>(f));
} }
template <typename F, typename Params, typename... Ts> template <typename F, typename Params, typename... Ts>
static auto asyncify(F&& fn, Params&& params, Ts... args) static auto asyncify(F && fn, Params && params, Ts... args) -> pplx::task<decltype(capture(std::forward<Params>(params), std::bind(std::forward<F>(fn), std::placeholders::_1, std::forward<Ts>(args)...))())> {
-> pplx::task<decltype(capture(std::forward<Params>(params), return pplx::task<decltype(capture(std::forward<Params>(params), std::bind(std::forward<F>(fn), std::placeholders::_1, std::forward<Ts>(args)...))())>(capture(std::forward<Params>(params), std::bind(std::forward<F>(fn), std::placeholders::_1, std::forward<Ts>(args)...)));
std::bind(std::forward<F>(fn),
std::placeholders::_1,
std::forward<Ts>(args)...))())> {
return pplx::task<decltype(
capture(std::forward<Params>(params),
std::bind(std::forward<F>(fn), std::placeholders::_1,
std::forward<Ts>(args)...))())>(
capture(std::forward<Params>(params),
std::bind(std::forward<F>(fn), std::placeholders::_1,
std::forward<Ts>(args)...)));
} }
} // namespace detail } // namespace detail
@ -208,323 +230,334 @@ using responser_t = etcd::Response (*)(std::shared_ptr<T>);
} // namespace etcd } // namespace etcd
pplx::task<etcd::Response> etcd::Client::head() { pplx::task<etcd::Response> etcd::Client::head()
{
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<etcd::responser_t<etcdv3::AsyncHeadAction>>(Response::create), static_cast<etcd::responser_t<etcdv3::AsyncHeadAction>>(Response::create),
this->client->head_internal()); this->client->head_internal());
} }
pplx::task<etcd::Response> etcd::Client::get(std::string const& key) { pplx::task<etcd::Response> etcd::Client::get(std::string const & key)
{
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<etcd::responser_t<etcdv3::AsyncRangeAction>>( static_cast<etcd::responser_t<etcdv3::AsyncRangeAction>>(Response::create),
Response::create),
this->client->get_internal(key)); this->client->get_internal(key));
} }
pplx::task<etcd::Response> etcd::Client::get(std::string const& key, pplx::task<etcd::Response> etcd::Client::set(std::string const & key, std::string const & value, int ttl)
int64_t revision) { {
if (ttl > 0) {
return this->leasegrant(ttl).then([this, key, value](pplx::task<etcd::Response> const &task) {
auto resp = task.get();
if (resp.error_code() == 0) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<etcd::responser_t<etcdv3::AsyncRangeAction>>( static_cast<responser_t<etcdv3::AsyncSetAction>>(Response::create),
Response::create), this->client->set_internal(key, value, resp.value().lease()));
this->client->get_internal(key, revision)); } else {
return pplx::task_from_result(resp);
}
});
} else {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncSetAction>>(Response::create),
this->client->set_internal(key, value, 0));
}
} }
pplx::task<etcd::Response> etcd::Client::set(std::string const& key, pplx::task<etcd::Response> etcd::Client::set(std::string const & key, std::string const & value, int64_t leaseid)
std::string const& value, {
const int64_t leaseid) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncPutAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncSetAction>>(Response::create),
this->client->put_internal(key, value, leaseid)); this->client->set_internal(key, value, leaseid));
} }
pplx::task<etcd::Response> etcd::Client::add(std::string const& key, pplx::task<etcd::Response> etcd::Client::add(std::string const & key, std::string const & value, int ttl)
std::string const& value, {
const int64_t leaseid) { if (ttl > 0) {
return this->leasegrant(ttl).then([this, key, value](pplx::task<etcd::Response> const &task) {
auto resp = task.get();
if (resp.error_code() == 0) {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncSetAction>>(Response::create),
this->client->add_internal(key, value, resp.value().lease()));
} else {
return pplx::task_from_result(resp);
}
});
} else {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncSetAction>>(Response::create),
this->client->add_internal(key, value, 0));
}
}
pplx::task<etcd::Response> etcd::Client::add(std::string const & key, std::string const & value, int64_t leaseid)
{
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncSetAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncSetAction>>(Response::create),
this->client->add_internal(key, value, leaseid)); this->client->add_internal(key, value, leaseid));
} }
pplx::task<etcd::Response> etcd::Client::put(std::string const& key, pplx::task<etcd::Response> etcd::Client::put(std::string const & key, std::string const & value) {
std::string const& value) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncPutAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncPutAction>>(Response::create),
this->client->put_internal(key, value)); this->client->put_internal(key, value));
} }
pplx::task<etcd::Response> etcd::Client::put(std::string const& key, pplx::task<etcd::Response> etcd::Client::modify(std::string const & key, std::string const & value, int ttl)
std::string const& value, {
const int64_t leaseId) { if (ttl > 0) {
return this->leasegrant(ttl).then([this, key, value](pplx::task<etcd::Response> const &task) {
auto resp = task.get();
if (resp.error_code() == 0) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncPutAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncUpdateAction>>(Response::create),
this->client->put_internal(key, value, leaseId)); this->client->modify_internal(key, value, resp.value().lease()));
} else {
return pplx::task_from_result(resp);
}
});
} else {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncUpdateAction>>(Response::create),
this->client->modify_internal(key, value, 0));
}
} }
pplx::task<etcd::Response> etcd::Client::modify(std::string const& key, pplx::task<etcd::Response> etcd::Client::modify(std::string const & key, std::string const & value, int64_t leaseid)
std::string const& value, {
const int64_t leaseid) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncUpdateAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncUpdateAction>>(Response::create),
this->client->modify_internal(key, value, leaseid)); this->client->modify_internal(key, value, leaseid));
} }
pplx::task<etcd::Response> etcd::Client::modify_if(std::string const& key, pplx::task<etcd::Response> etcd::Client::modify_if(std::string const & key, std::string const & value, std::string const & old_value, int ttl)
std::string const& value, {
std::string const& old_value, if (ttl > 0) {
const int64_t leaseid) { return this->leasegrant(ttl).then([this, key, value, old_value](pplx::task<etcd::Response> const &task) {
auto resp = task.get();
if (resp.error_code() == 0) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncCompareAndSwapAction>>( static_cast<responser_t<etcdv3::AsyncCompareAndSwapAction>>(Response::create),
Response::create), this->client->modify_if_internal(key, value, 0, old_value, resp.value().lease(), etcdv3::AtomicityType::PREV_VALUE));
this->client->modify_if_internal(key, value, 0, old_value, } else {
etcdv3::AtomicityType::PREV_VALUE, return pplx::task_from_result(resp);
leaseid)); }
});
} else {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncCompareAndSwapAction>>(Response::create),
this->client->modify_if_internal(key, value, 0, old_value, 0, etcdv3::AtomicityType::PREV_VALUE));
}
} }
pplx::task<etcd::Response> etcd::Client::modify_if(std::string const& key, pplx::task<etcd::Response> etcd::Client::modify_if(std::string const & key, std::string const & value, std::string const & old_value, int64_t leaseid)
std::string const& value, {
int64_t old_index,
const int64_t leaseid) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncCompareAndSwapAction>>( static_cast<responser_t<etcdv3::AsyncCompareAndSwapAction>>(Response::create),
Response::create), this->client->modify_if_internal(key, value, 0, old_value, leaseid, etcdv3::AtomicityType::PREV_VALUE));
this->client->modify_if_internal(key, value, old_index, "",
etcdv3::AtomicityType::PREV_INDEX,
leaseid));
} }
pplx::task<etcd::Response> etcd::Client::rm(std::string const& key) { pplx::task<etcd::Response> etcd::Client::modify_if(std::string const & key, std::string const & value, int64_t old_index, int ttl)
{
if (ttl > 0) {
return this->leasegrant(ttl).then([this, key, value, old_index](pplx::task<etcd::Response> const &task) {
auto resp = task.get();
if (resp.error_code() == 0) {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncCompareAndSwapAction>>(Response::create),
this->client->modify_if_internal(key, value, old_index, "", resp.value().lease(), etcdv3::AtomicityType::PREV_INDEX));
} else {
return pplx::task_from_result(resp);
}
});
} else {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncCompareAndSwapAction>>(Response::create),
this->client->modify_if_internal(key, value, old_index, "", 0, etcdv3::AtomicityType::PREV_INDEX));
}
}
pplx::task<etcd::Response> etcd::Client::modify_if(std::string const & key, std::string const & value, int64_t old_index, int64_t leaseid)
{
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncCompareAndSwapAction>>(Response::create),
this->client->modify_if_internal(key, value, old_index, "", leaseid, etcdv3::AtomicityType::PREV_INDEX));
}
pplx::task<etcd::Response> etcd::Client::rm(std::string const & key)
{
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncDeleteAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncDeleteAction>>(Response::create),
this->client->rm_internal(key)); this->client->rm_internal(key));
} }
pplx::task<etcd::Response> etcd::Client::rm_if(std::string const& key, pplx::task<etcd::Response> etcd::Client::rm_if(std::string const & key, std::string const & old_value)
std::string const& old_value) { {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncCompareAndDeleteAction>>( static_cast<responser_t<etcdv3::AsyncCompareAndDeleteAction>>(Response::create),
Response::create), this->client->rm_if_internal(key, 0, old_value, etcdv3::AtomicityType::PREV_VALUE));
this->client->rm_if_internal(key, 0, old_value,
etcdv3::AtomicityType::PREV_VALUE));
} }
pplx::task<etcd::Response> etcd::Client::rm_if(std::string const& key, pplx::task<etcd::Response> etcd::Client::rm_if(std::string const & key, int64_t old_index)
int64_t old_index) { {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncCompareAndDeleteAction>>( static_cast<responser_t<etcdv3::AsyncCompareAndDeleteAction>>(Response::create),
Response::create), this->client->rm_if_internal(key, old_index, "", etcdv3::AtomicityType::PREV_INDEX));
this->client->rm_if_internal(key, old_index, "",
etcdv3::AtomicityType::PREV_INDEX));
} }
pplx::task<etcd::Response> etcd::Client::rmdir(std::string const& key, pplx::task<etcd::Response> etcd::Client::rmdir(std::string const & key, bool recursive)
bool recursive) { {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncDeleteAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncDeleteAction>>(Response::create),
this->client->rmdir_internal(key, recursive)); this->client->rmdir_internal(key, recursive));
} }
pplx::task<etcd::Response> etcd::Client::rmdir(std::string const& key, pplx::task<etcd::Response> etcd::Client::rmdir(std::string const & key, const char *range_end)
const char* range_end) { {
return this->rmdir(key, std::string(range_end)); return this->rmdir(key, std::string(range_end));
} }
pplx::task<etcd::Response> etcd::Client::rmdir(std::string const& key, pplx::task<etcd::Response> etcd::Client::rmdir(std::string const & key, std::string const &range_end)
std::string const& range_end) { {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncDeleteAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncDeleteAction>>(Response::create),
this->client->rmdir_internal(key, range_end)); this->client->rmdir_internal(key, range_end));
} }
pplx::task<etcd::Response> etcd::Client::ls(std::string const& key) { pplx::task<etcd::Response> etcd::Client::ls(std::string const & key)
{
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, 0)); this->client->ls_internal(key, 0));
} }
pplx::task<etcd::Response> etcd::Client::ls(std::string const& key, pplx::task<etcd::Response> etcd::Client::ls(std::string const & key, size_t const limit)
size_t const limit) { {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, limit)); this->client->ls_internal(key, limit));
} }
pplx::task<etcd::Response> etcd::Client::ls(std::string const& key, pplx::task<etcd::Response> etcd::Client::ls(std::string const & key, std::string const &range_end)
size_t const limit, {
int64_t revision) {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, limit, false, revision));
}
pplx::task<etcd::Response> etcd::Client::ls(std::string const& key,
std::string const& range_end) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, range_end, 0)); this->client->ls_internal(key, range_end, 0));
} }
pplx::task<etcd::Response> etcd::Client::ls(std::string const& key, pplx::task<etcd::Response> etcd::Client::ls(std::string const & key, std::string const &range_end, size_t const limit)
std::string const& range_end, {
size_t const limit) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, range_end, limit)); this->client->ls_internal(key, range_end, limit));
} }
pplx::task<etcd::Response> etcd::Client::ls(std::string const& key, pplx::task<etcd::Response> etcd::Client::keys(std::string const & key)
std::string const& range_end, {
size_t const limit,
int64_t revision) {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, range_end, limit, false, revision));
}
pplx::task<etcd::Response> etcd::Client::keys(std::string const& key) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, 0, true)); this->client->ls_internal(key, 0, true));
} }
pplx::task<etcd::Response> etcd::Client::keys(std::string const& key, pplx::task<etcd::Response> etcd::Client::keys(std::string const & key, size_t const limit)
size_t const limit) { {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, limit, true)); this->client->ls_internal(key, limit, true));
} }
pplx::task<etcd::Response> etcd::Client::keys(std::string const& key, pplx::task<etcd::Response> etcd::Client::keys(std::string const & key, std::string const &range_end)
size_t const limit, {
int64_t revision) {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, limit, true, revision));
}
pplx::task<etcd::Response> etcd::Client::keys(std::string const& key,
std::string const& range_end) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, range_end, 0, true)); this->client->ls_internal(key, range_end, 0, true));
} }
pplx::task<etcd::Response> etcd::Client::keys(std::string const& key, pplx::task<etcd::Response> etcd::Client::keys(std::string const & key, std::string const &range_end, size_t const limit)
std::string const& range_end, {
size_t const limit) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, range_end, limit, true)); this->client->ls_internal(key, range_end, limit, true));
} }
pplx::task<etcd::Response> etcd::Client::keys(std::string const& key, pplx::task<etcd::Response> etcd::Client::watch(std::string const & key, bool recursive)
std::string const& range_end, {
size_t const limit,
int64_t revision) {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRangeAction>>(Response::create),
this->client->ls_internal(key, range_end, limit, true, revision));
}
pplx::task<etcd::Response> etcd::Client::watch(std::string const& key,
bool recursive) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncWatchAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncWatchAction>>(Response::create),
this->client->watch_internal(key, 0, recursive)); this->client->watch_internal(key, 0, recursive));
} }
pplx::task<etcd::Response> etcd::Client::watch(std::string const& key, pplx::task<etcd::Response> etcd::Client::watch(std::string const & key, int64_t fromIndex, bool recursive)
int64_t fromIndex, {
bool recursive) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncWatchAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncWatchAction>>(Response::create),
this->client->watch_internal(key, fromIndex, recursive)); this->client->watch_internal(key, fromIndex, recursive));
} }
pplx::task<etcd::Response> etcd::Client::watch(std::string const& key, pplx::task<etcd::Response> etcd::Client::watch(std::string const & key, const char *range_end)
const char* range_end) { {
return this->watch(key, std::string(range_end)); return this->watch(key, std::string(range_end));
} }
pplx::task<etcd::Response> etcd::Client::watch(std::string const& key, pplx::task<etcd::Response> etcd::Client::watch(std::string const & key, std::string const & range_end)
std::string const& range_end) { {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncWatchAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncWatchAction>>(Response::create),
this->client->watch_internal(key, range_end, 0)); this->client->watch_internal(key, range_end, 0));
} }
pplx::task<etcd::Response> etcd::Client::watch(std::string const& key, pplx::task<etcd::Response> etcd::Client::watch(std::string const & key, std::string const & range_end, int64_t fromIndex)
std::string const& range_end, {
int64_t fromIndex) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncWatchAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncWatchAction>>(Response::create),
this->client->watch_internal(key, range_end, fromIndex)); this->client->watch_internal(key, range_end, fromIndex));
} }
pplx::task<etcd::Response> etcd::Client::leasegrant(int ttl) { pplx::task<etcd::Response> etcd::Client::leasegrant(int ttl)
{
// See Note [lease with TTL and issue the actual request] // See Note [lease with TTL and issue the actual request]
return pplx::task<etcd::Response>( return pplx::task<etcd::Response>([this, ttl]() {
[this, ttl]() { return this->client->leasegrant(ttl); }); return this->client->leasegrant(ttl);
});
} }
pplx::task<std::shared_ptr<etcd::KeepAlive>> etcd::Client::leasekeepalive( pplx::task<std::shared_ptr<etcd::KeepAlive>> etcd::Client::leasekeepalive(int ttl) {
int ttl) {
// See Note [lease with TTL and issue the actual request] // See Note [lease with TTL and issue the actual request]
return pplx::task<std::shared_ptr<etcd::KeepAlive>>( return pplx::task<std::shared_ptr<etcd::KeepAlive>>([this, ttl]() {
[this, ttl]() { return this->client->leasekeepalive(ttl); }); return this->client->leasekeepalive(ttl);
});
} }
pplx::task<etcd::Response> etcd::Client::leaserevoke(int64_t lease_id) { pplx::task<etcd::Response> etcd::Client::leaserevoke(int64_t lease_id)
{
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncLeaseRevokeAction>>( static_cast<responser_t<etcdv3::AsyncLeaseRevokeAction>>(Response::create),
Response::create),
this->client->leaserevoke_internal(lease_id)); this->client->leaserevoke_internal(lease_id));
} }
pplx::task<etcd::Response> etcd::Client::leasetimetolive(int64_t lease_id) { pplx::task<etcd::Response> etcd::Client::leasetimetolive(int64_t lease_id)
{
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncLeaseTimeToLiveAction>>( static_cast<responser_t<etcdv3::AsyncLeaseTimeToLiveAction>>(Response::create),
Response::create),
this->client->leasetimetolive_internal(lease_id)); this->client->leasetimetolive_internal(lease_id));
} }
pplx::task<etcd::Response> etcd::Client::leases() { pplx::task<etcd::Response> etcd::Client::leases()
{
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncLeaseLeasesAction>>( static_cast<responser_t<etcdv3::AsyncLeaseLeasesAction>>(Response::create),
Response::create),
this->client->leases_internal()); this->client->leases_internal());
} }
pplx::task<etcd::Response> etcd::Client::add_member(
std::string const& peer_urls, bool is_learner) {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncAddMemberAction>>(Response::create),
this->client->add_member_internal(peer_urls, is_learner));
}
pplx::task<etcd::Response> etcd::Client::list_member() {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncListMemberAction>>(Response::create),
this->client->list_member_internal());
}
pplx::task<etcd::Response> etcd::Client::remove_member(
const uint64_t member_id) {
return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncRemoveMemberAction>>(
Response::create),
this->client->remove_member_internal(member_id));
}
pplx::task<etcd::Response> etcd::Client::lock(std::string const &key) { pplx::task<etcd::Response> etcd::Client::lock(std::string const &key) {
static const int DEFAULT_LEASE_TTL_FOR_LOCK = static const int DEFAULT_LEASE_TTL_FOR_LOCK = 10; // see also etcd::SyncClient::lock
10; // see also etcd::SyncClient::lock
return this->lock(key, DEFAULT_LEASE_TTL_FOR_LOCK); return this->lock(key, DEFAULT_LEASE_TTL_FOR_LOCK);
} }
pplx::task<etcd::Response> etcd::Client::lock(std::string const &key, pplx::task<etcd::Response> etcd::Client::lock(std::string const &key,
const bool sync) { const bool sync) {
static const int DEFAULT_LEASE_TTL_FOR_LOCK = static const int DEFAULT_LEASE_TTL_FOR_LOCK = 10; // see also etcd::SyncClient::lock
10; // see also etcd::SyncClient::lock
return this->lock(key, DEFAULT_LEASE_TTL_FOR_LOCK, sync); return this->lock(key, DEFAULT_LEASE_TTL_FOR_LOCK, sync);
} }
@ -534,14 +567,16 @@ pplx::task<etcd::Response> etcd::Client::lock(std::string const& key,
// See also SyncClient::lock // See also SyncClient::lock
// //
// We don't separate the keepalive step and lock step to avoid leaving many // We don't separate the keepalive step and lock step to avoid leaving many
// busy-waiting keepalive tasks when waiting for the locks due to the // busy-waiting keepalive tasks when waiting for the locks due to the contention
// contention of async threadpool. // of async threadpool.
return pplx::task<etcd::Response>( return pplx::task<etcd::Response>([this, key, lease_ttl]() {
[this, key, lease_ttl]() { return this->client->lock(key, lease_ttl); }); return this->client->lock(key, lease_ttl);
});
} }
pplx::task<etcd::Response> etcd::Client::lock(std::string const &key, pplx::task<etcd::Response> etcd::Client::lock(std::string const &key,
int lease_ttl, const bool sync) { int lease_ttl,
const bool sync) {
if (sync) { if (sync) {
pplx::task_completion_event<etcd::Response> event; pplx::task_completion_event<etcd::Response> event;
event.set(this->client->lock(key, lease_ttl)); event.set(this->client->lock(key, lease_ttl));
@ -582,19 +617,16 @@ pplx::task<etcd::Response> etcd::Client::txn(etcdv3::Transaction const& txn) {
this->client->txn_internal(txn)); this->client->txn_internal(txn));
} }
pplx::task<etcd::Response> etcd::Client::campaign(std::string const& name, pplx::task<etcd::Response> etcd::Client::campaign(
int64_t lease_id, std::string const &name, int64_t lease_id, std::string const &value) {
std::string const& value) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncCampaignAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncCampaignAction>>(Response::create),
this->client->campaign_internal(name, lease_id, value)); this->client->campaign_internal(name, lease_id, value));
} }
pplx::task<etcd::Response> etcd::Client::proclaim(std::string const& name, pplx::task<etcd::Response> etcd::Client::proclaim(
int64_t lease_id, std::string const &name, int64_t lease_id,
std::string const& key, std::string const &key, int64_t revision, std::string const &value) {
int64_t revision,
std::string const& value) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncProclaimAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncProclaimAction>>(Response::create),
this->client->proclaim_internal(name, lease_id, key, revision, value)); this->client->proclaim_internal(name, lease_id, key, revision, value));
@ -606,15 +638,12 @@ pplx::task<etcd::Response> etcd::Client::leader(std::string const& name) {
this->client->leader_internal(name)); this->client->leader_internal(name));
} }
std::unique_ptr<etcd::Client::Observer> etcd::Client::observe( std::unique_ptr<etcd::Client::Observer> etcd::Client::observe(std::string const &name) {
std::string const& name) {
return this->client->observe(name); return this->client->observe(name);
} }
pplx::task<etcd::Response> etcd::Client::resign(std::string const& name, pplx::task<etcd::Response> etcd::Client::resign(
int64_t lease_id, std::string const &name, int64_t lease_id, std::string const &key, int64_t revision) {
std::string const& key,
int64_t revision) {
return etcd::detail::asyncify( return etcd::detail::asyncify(
static_cast<responser_t<etcdv3::AsyncResignAction>>(Response::create), static_cast<responser_t<etcdv3::AsyncResignAction>>(Response::create),
this->client->resign_internal(name, lease_id, key, revision)); this->client->resign_internal(name, lease_id, key, revision));
@ -628,61 +657,72 @@ std::shared_ptr<grpc::Channel> etcd::Client::grpc_channel() const {
return this->client->grpc_channel(); return this->client->grpc_channel();
} }
etcd::SyncClient* etcd::Client::sync_client() const { return this->client; } etcd::SyncClient* etcd::Client::sync_client() const {
return this->client;
}
// watchers from the async client // watchers from the async client
etcd::Watcher::Watcher(Client const &client, std::string const & key, etcd::Watcher::Watcher(Client const &client, std::string const & key,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive) std::function<void(bool)> wait_callback,
: Watcher(*client.sync_client(), key, callback, wait_callback, recursive) {} bool recursive):
Watcher(*client.sync_client(), key, callback, wait_callback, recursive) {
}
etcd::Watcher::Watcher(Client const &client, std::string const & key, etcd::Watcher::Watcher(Client const &client, std::string const & key,
std::string const &range_end, std::string const &range_end,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback) std::function<void(bool)> wait_callback):
: Watcher(*client.sync_client(), key, range_end, callback, wait_callback) {} Watcher(*client.sync_client(), key, range_end, callback, wait_callback) {
}
etcd::Watcher::Watcher(Client const& client, std::string const& key, etcd::Watcher::Watcher(Client const &client, std::string const & key, int64_t fromIndex,
int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive) std::function<void(bool)> wait_callback,
: Watcher(*client.sync_client(), key, fromIndex, callback, wait_callback, bool recursive):
recursive) {} Watcher(*client.sync_client(), key, fromIndex, callback, wait_callback, recursive) {
}
etcd::Watcher::Watcher(Client const &client, std::string const & key, etcd::Watcher::Watcher(Client const &client, std::string const & key,
std::string const &range_end, int64_t fromIndex, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback) std::function<void(bool)> wait_callback):
: Watcher(*client.sync_client(), key, range_end, fromIndex, callback, Watcher(*client.sync_client(), key, range_end, fromIndex, callback, wait_callback) {
wait_callback) {} }
etcd::Watcher::Watcher(Client const &client, std::string const & key, etcd::Watcher::Watcher(Client const &client, std::string const & key,
std::function<void(Response)> callback, bool recursive) std::function<void(Response)> callback,
: Watcher(*client.sync_client(), key, callback, recursive) {} bool recursive):
Watcher(*client.sync_client(), key, callback, recursive) {
}
etcd::Watcher::Watcher(Client const &client, std::string const & key, etcd::Watcher::Watcher(Client const &client, std::string const & key,
std::string const &range_end, std::string const &range_end,
std::function<void(Response)> callback) std::function<void(Response)> callback):
: Watcher(*client.sync_client(), key, range_end, callback) {} Watcher(*client.sync_client(), key, range_end, callback) {
}
etcd::Watcher::Watcher(Client const& client, std::string const& key, etcd::Watcher::Watcher(Client const &client, std::string const & key, int64_t fromIndex,
int64_t fromIndex, std::function<void(Response)> callback,
std::function<void(Response)> callback, bool recursive) bool recursive):
: Watcher(*client.sync_client(), key, fromIndex, callback, recursive) {} Watcher(*client.sync_client(), key, fromIndex, callback, recursive) {
}
etcd::Watcher::Watcher(Client const &client, std::string const & key, etcd::Watcher::Watcher(Client const &client, std::string const & key,
std::string const &range_end, int64_t fromIndex, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback) std::function<void(Response)> callback):
: Watcher(*client.sync_client(), key, range_end, fromIndex, callback) {} Watcher(*client.sync_client(), key, range_end, fromIndex, callback) {
}
// keepalive from then async client // keepalive from then async client
etcd::KeepAlive::KeepAlive(Client const& client, int ttl, int64_t lease_id) etcd::KeepAlive::KeepAlive(Client const &client, int ttl, int64_t lease_id):
: KeepAlive(*client.sync_client(), ttl, lease_id) {} KeepAlive(*client.sync_client(), ttl, lease_id) {
}
etcd::KeepAlive::KeepAlive( etcd::KeepAlive::KeepAlive(Client const &client,
Client const& client, std::function<void (std::exception_ptr)> const &handler,
std::function<void(std::exception_ptr)> const& handler, int ttl, int ttl, int64_t lease_id):
int64_t lease_id) KeepAlive(*client.sync_client(), handler, ttl, lease_id) {
: KeepAlive(*client.sync_client(), handler, ttl, lease_id) {} }

View File

@ -1,9 +1,8 @@
#include <chrono> #include <chrono>
#include <iostream>
#include <ratio> #include <ratio>
#include "etcd/KeepAlive.hpp" #include "etcd/KeepAlive.hpp"
#include "etcd/v3/AsyncGRPC.hpp" #include "etcd/v3/AsyncLeaseAction.hpp"
#include <grpc++/grpc++.h> #include <grpc++/grpc++.h>
#include "proto/rpc.grpc.pb.h" #include "proto/rpc.grpc.pb.h"
@ -17,22 +16,15 @@ struct etcd::KeepAlive::EtcdServerStubs {
std::unique_ptr<etcdv3::AsyncLeaseKeepAliveAction> call; std::unique_ptr<etcdv3::AsyncLeaseKeepAliveAction> call;
}; };
void etcd::KeepAlive::EtcdServerStubsDeleter::operator()( void etcd::KeepAlive::EtcdServerStubsDeleter::operator()(etcd::KeepAlive::EtcdServerStubs *stubs) {
etcd::KeepAlive::EtcdServerStubs* stubs) {
if (stubs) { if (stubs) {
delete stubs; delete stubs;
} }
} }
etcd::KeepAlive::KeepAlive(SyncClient const& client, int ttl, int64_t lease_id) etcd::KeepAlive::KeepAlive(SyncClient const &client, int ttl, int64_t lease_id):
: ttl(ttl), ttl(ttl), lease_id(lease_id), continue_next(true),
lease_id(lease_id),
continue_next(true),
grpc_timeout(client.get_grpc_timeout()) { grpc_timeout(client.get_grpc_timeout()) {
if (ttl > 0 && lease_id == 0) {
this->lease_id =
const_cast<SyncClient&>(client).leasegrant(ttl).value().lease();
}
stubs.reset(new EtcdServerStubs{}); stubs.reset(new EtcdServerStubs{});
stubs->leaseServiceStub = Lease::NewStub(client.grpc_channel()); stubs->leaseServiceStub = Lease::NewStub(client.grpc_channel());
@ -45,56 +37,43 @@ etcd::KeepAlive::KeepAlive(SyncClient const& client, int ttl, int64_t lease_id)
continue_next.store(true); continue_next.store(true);
stubs->call.reset(new etcdv3::AsyncLeaseKeepAliveAction(std::move(params))); stubs->call.reset(new etcdv3::AsyncLeaseKeepAliveAction(std::move(params)));
refresh_task_ = std::thread([this]() { task_ = std::thread([this]() {
#ifndef _ETCD_NO_EXCEPTIONS
try { try {
// start refresh // start refresh
this->refresh(); this->refresh();
} catch (const std::exception& e) { context.run();
// propagate the exception } catch (...) {
eptr_ = std::current_exception(); eptr_ = std::current_exception();
} }
#else context.stop(); // clean up
const std::string err = this->refresh();
if (!err.empty()) {
eptr_ = std::make_exception_ptr(std::runtime_error(err));
}
#endif
}); });
} }
etcd::KeepAlive::KeepAlive(std::string const& address, int ttl, etcd::KeepAlive::KeepAlive(std::string const & address, int ttl, int64_t lease_id):
int64_t lease_id) KeepAlive(SyncClient(address), ttl, lease_id) {
: KeepAlive(SyncClient(address), ttl, lease_id) {} }
etcd::KeepAlive::KeepAlive(std::string const & address, etcd::KeepAlive::KeepAlive(std::string const & address,
std::string const& username, std::string const & username, std::string const & password,
std::string const& password, int ttl, int ttl, int64_t lease_id, int const auth_token_ttl):
int64_t lease_id, int const auth_token_ttl) KeepAlive(SyncClient(address, username, password, auth_token_ttl), ttl, lease_id) {
: KeepAlive(SyncClient(address, username, password, auth_token_ttl), ttl,
lease_id) {}
etcd::KeepAlive::KeepAlive(
std::string const& address, std::string const& ca, std::string const& cert,
std::string const& privkey,
std::function<void(std::exception_ptr)> const& handler, int ttl,
int64_t lease_id, std::string const& target_name_override)
: KeepAlive(SyncClient(address, ca, cert, privkey, target_name_override),
handler, ttl, lease_id) {}
etcd::KeepAlive::KeepAlive(
SyncClient const& client,
std::function<void(std::exception_ptr)> const& handler, int ttl,
int64_t lease_id)
: handler_(handler),
ttl(ttl),
lease_id(lease_id),
continue_next(true),
grpc_timeout(client.get_grpc_timeout()) {
if (ttl > 0 && lease_id == 0) {
this->lease_id =
const_cast<SyncClient&>(client).leasegrant(ttl).value().lease();
} }
etcd::KeepAlive::KeepAlive(std::string const & address,
std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::function<void (std::exception_ptr)> const &handler,
int ttl, int64_t lease_id,
std::string const & target_name_override):
KeepAlive(SyncClient(address, ca, cert, privkey, target_name_override), ttl, lease_id) {
}
etcd::KeepAlive::KeepAlive(SyncClient const &client,
std::function<void (std::exception_ptr)> const &handler,
int ttl, int64_t lease_id):
handler_(handler), ttl(ttl), lease_id(lease_id), continue_next(true),
grpc_timeout(client.get_grpc_timeout()) {
stubs.reset(new EtcdServerStubs{}); stubs.reset(new EtcdServerStubs{});
stubs->leaseServiceStub = Lease::NewStub(client.grpc_channel()); stubs->leaseServiceStub = Lease::NewStub(client.grpc_channel());
@ -105,57 +84,56 @@ etcd::KeepAlive::KeepAlive(
params.lease_stub = stubs->leaseServiceStub.get(); params.lease_stub = stubs->leaseServiceStub.get();
stubs->call.reset(new etcdv3::AsyncLeaseKeepAliveAction(std::move(params))); stubs->call.reset(new etcdv3::AsyncLeaseKeepAliveAction(std::move(params)));
refresh_task_ = std::thread([this]() { task_ = std::thread([this]() {
#ifndef _ETCD_NO_EXCEPTIONS
try { try {
// start refresh // start refresh
this->refresh(); this->refresh();
context.run();
} catch (...) { } catch (...) {
// propagate the exception // run canceller first
this->Cancel();
// propogate the exception
eptr_ = std::current_exception(); eptr_ = std::current_exception();
} if (handler_) {
#else
const std::string err = this->refresh();
if (!err.empty()) {
eptr_ = std::make_exception_ptr(std::runtime_error(err));
}
#endif
if (eptr_ && handler_) {
handler_(eptr_); handler_(eptr_);
} }
}
}); });
} }
etcd::KeepAlive::KeepAlive( etcd::KeepAlive::KeepAlive(std::string const & address,
std::string const& address, std::function<void (std::exception_ptr)> const &handler,
std::function<void(std::exception_ptr)> const& handler, int ttl, int ttl, int64_t lease_id):
int64_t lease_id) KeepAlive(SyncClient(address), handler, ttl, lease_id) {
: KeepAlive(SyncClient(address), handler, ttl, lease_id) {} }
etcd::KeepAlive::KeepAlive( etcd::KeepAlive::KeepAlive(std::string const & address,
std::string const& address, std::string const& username, std::string const & username, std::string const & password,
std::string const& password, std::function<void (std::exception_ptr)> const &handler,
std::function<void(std::exception_ptr)> const& handler, int ttl, int ttl, int64_t lease_id, const int auth_token_ttl):
int64_t lease_id, const int auth_token_ttl) KeepAlive(SyncClient(address, username, password, auth_token_ttl), handler, ttl, lease_id) {
: KeepAlive(SyncClient(address, username, password, auth_token_ttl), }
handler, ttl, lease_id) {}
etcd::KeepAlive::~KeepAlive() { this->Cancel(); } etcd::KeepAlive::~KeepAlive()
{
this->Cancel();
// clean up
if (task_.joinable()) {
task_.join();
}
}
void etcd::KeepAlive::Cancel() { void etcd::KeepAlive::Cancel()
{
std::lock_guard<std::recursive_mutex> scope_lock(mutex_for_refresh_);
if (!continue_next.exchange(false)) { if (!continue_next.exchange(false)) {
return; return;
} }
// stop the thread
cv_for_refresh_.notify_all();
refresh_task_.join();
// send a cancel request
{
std::lock_guard<std::mutex> lock(mutex_for_refresh_);
stubs->call->CancelKeepAlive(); stubs->call->CancelKeepAlive();
if (keepalive_timer_) {
keepalive_timer_->cancel();
} }
context.stop();
} }
void etcd::KeepAlive::Check() { void etcd::KeepAlive::Check() {
@ -163,96 +141,61 @@ void etcd::KeepAlive::Check() {
std::rethrow_exception(eptr_); std::rethrow_exception(eptr_);
} }
// issue an refresh to make sure it still alive // issue an refresh to make sure it still alive
#ifndef _ETCD_NO_EXCEPTIONS
try { try {
this->refresh_once(); this->refresh_once();
} catch (...) { } catch (...) {
// run canceller first // run canceller first
this->Cancel(); this->Cancel();
// propagate the exception, as we throw in `Check()`, the `handler` won't be // propogate the exception, as we throw in `Check()`, the `handler` won't be touched
// touched
eptr_ = std::current_exception(); eptr_ = std::current_exception();
} if (handler_) {
#else
const std::string err = this->refresh_once();
if (!err.empty()) {
// run canceller first
this->Cancel();
// propagate the exception, as we throw in `Check()`, the `handler` won't be
// touched
eptr_ = std::make_exception_ptr(std::runtime_error(err));
}
#endif
if (eptr_ && handler_) {
handler_(eptr_); handler_(eptr_);
} }
#ifndef _ETCD_NO_EXCEPTIONS
if (eptr_) {
// rethrow in `Check()` to keep the consistent semantics // rethrow in `Check()` to keep the consistent semantics
std::rethrow_exception(eptr_); std::rethrow_exception(eptr_);
} }
#endif
return;
} }
std::string etcd::KeepAlive::refresh() { void etcd::KeepAlive::refresh()
while (true) { {
std::lock_guard<std::recursive_mutex> scope_lock(mutex_for_refresh_);
if (!continue_next.load()) { if (!continue_next.load()) {
return std::string{}; return;
} }
// minimal resolution: 1 second // minimal resolution: 1 second
int keepalive_ttl = std::max(ttl - 1, 1); int keepalive_ttl = std::max(ttl - 1, 1);
{ keepalive_timer_.reset(new boost::asio::steady_timer(context, std::chrono::seconds(keepalive_ttl)));
std::unique_lock<std::mutex> lock(mutex_for_refresh_); keepalive_timer_->async_wait([this](const boost::system::error_code& error) {
if (cv_for_refresh_.wait_for(lock, std::chrono::seconds(keepalive_ttl)) == if (error) {
std::cv_status::no_timeout) {
if (!continue_next.load()) {
return std::string{};
}
#ifndef NDEBUG #ifndef NDEBUG
std::cerr std::cerr << "keepalive timer cancelled: " << error << ", " << error.message() << std::endl;
<< "[warn] awaked from condition_variable but continue_next is "
"not set, maybe due to clock drift."
<< std::endl;
#endif #endif
} } else {
} if (this->continue_next.load()) {
// execute refresh // execute refresh
const std::string err = this->refresh_once(); this->refresh_once();
if (!err.empty()) { // trigger the next round;
return err; this->refresh();
} }
} }
return std::string{}; });
} }
std::string etcd::KeepAlive::refresh_once() { void etcd::KeepAlive::refresh_once()
std::lock_guard<std::mutex> scope_lock(mutex_for_refresh_); {
std::lock_guard<std::recursive_mutex> scope_lock(mutex_for_refresh_);
if (!continue_next.load()) { if (!continue_next.load()) {
return std::string{}; return;
} }
this->stubs->call->mutable_parameters().grpc_timeout = this->grpc_timeout; this->stubs->call->mutable_parameters().grpc_timeout = this->grpc_timeout;
auto resp = this->stubs->call->Refresh(); auto resp = this->stubs->call->Refresh();
if (!resp.is_ok()) { if (!resp.is_ok()) {
const std::string err = "Failed to refresh lease: error code: " + throw std::runtime_error("Failed to refresh lease: error code: " + std::to_string(resp.error_code()) +
std::to_string(resp.error_code()) + ", message: " + resp.error_message());
", message: " + resp.error_message();
#ifndef _ETCD_NO_EXCEPTIONS
throw std::runtime_error(err);
#endif
return err;
} }
if (resp.value().ttl() == 0) { if (resp.value().ttl() == 0) {
const std::string err = throw std::out_of_range("Failed to refresh lease due to expiration: the new TTL is 0.");
"Failed to refresh lease due to expiration: the new TTL is 0.";
#ifndef _ETCD_NO_EXCEPTIONS
throw std::out_of_range(err);
#endif
return err;
} }
return std::string{};
} }

View File

@ -3,7 +3,11 @@
#include <iostream> #include <iostream>
etcd::Response::Response() : _error_code(0), _index(0) {} etcd::Response::Response()
: _error_code(0),
_index(0)
{
}
etcd::Response::Response(const etcd::Response & response) { etcd::Response::Response(const etcd::Response & response) {
this->_error_code = response._error_code; this->_error_code = response._error_code;
@ -25,29 +29,30 @@ etcd::Response::Response(const etcd::Response& response) {
this->_raft_term = response._raft_term; this->_raft_term = response._raft_term;
this->_leases = response._leases; this->_leases = response._leases;
this->_members = response._members;
} }
etcd::Response::Response(const etcdv3::V3Response& reply, etcd::Response::Response(const etcdv3::V3Response& reply, std::chrono::microseconds const& duration)
std::chrono::microseconds const& duration) { {
_index = reply.get_index(); _index = reply.get_index();
_action = reply.get_action(); _action = reply.get_action();
_error_code = reply.get_error_code(); _error_code = reply.get_error_code();
_error_message = reply.get_error_message(); _error_message = reply.get_error_message();
if (reply.has_values()) { if(reply.has_values())
{
auto val = reply.get_values(); auto val = reply.get_values();
for (unsigned int index = 0; index < val.size(); index++) { for(unsigned int index = 0; index < val.size(); index++)
{
_values.push_back(Value(val[index])); _values.push_back(Value(val[index]));
_keys.push_back(val[index].kvs.key()); _keys.push_back(val[index].kvs.key());
} }
_value = Value(reply.get_values()[0]); }
} else { else
{
_value = Value(reply.get_value()); _value = Value(reply.get_value());
} }
_prev_value = Value(reply.get_prev_value()); _prev_value = Value(reply.get_prev_value());
_compact_revision = reply.get_compact_revision(); _compact_revision = reply.get_compact_revision();
_watch_id = reply.get_watch_id();
_lock_key = reply.get_lock_key(); _lock_key = reply.get_lock_key();
_name = reply.get_name(); _name = reply.get_name();
@ -65,57 +70,99 @@ etcd::Response::Response(const etcdv3::V3Response& reply,
// lease list // lease list
this->_leases = reply.get_leases(); this->_leases = reply.get_leases();
// member list
this->_members = reply.get_members();
} }
etcd::Response::Response(int error_code, std::string const& error_message) etcd::Response::Response(int error_code, std::string const& error_message)
: _error_code(error_code), _error_message(error_message), _index(0) {} : _error_code(error_code),
_error_message(error_message),
_index(0)
{
}
etcd::Response::Response(int error_code, char const * error_message) etcd::Response::Response(int error_code, char const * error_message)
: _error_code(error_code), _error_message(error_message), _index(0) {} : _error_code(error_code),
_error_message(error_message),
_index(0)
{
}
int etcd::Response::error_code() const { return _error_code; } int etcd::Response::error_code() const
{
return _error_code;
}
std::string const& etcd::Response::error_message() const { std::string const & etcd::Response::error_message() const
{
return _error_message; return _error_message;
} }
bool etcd::Response::is_ok() const { return error_code() == 0; } bool etcd::Response::is_ok() const
{
return error_code() == 0;
}
bool etcd::Response::is_network_unavailable() const { bool etcd::Response::is_network_unavailable() const
{
return error_code() == ::grpc::StatusCode::UNAVAILABLE; return error_code() == ::grpc::StatusCode::UNAVAILABLE;
} }
bool etcd::Response::is_grpc_timeout() const { bool etcd::Response::is_grpc_timeout() const
{
return _error_code == grpc::StatusCode::DEADLINE_EXCEEDED; return _error_code == grpc::StatusCode::DEADLINE_EXCEEDED;
} }
std::string const& etcd::Response::action() const { return _action; } std::string const & etcd::Response::action() const
{
return _action;
}
int64_t etcd::Response::index() const { return _index; } int64_t etcd::Response::index() const
{
return _index;
}
etcd::Value const& etcd::Response::value() const { return _value; } etcd::Value const & etcd::Response::value() const
{
return _value;
}
etcd::Value const& etcd::Response::prev_value() const { return _prev_value; } etcd::Value const & etcd::Response::prev_value() const
{
return _prev_value;
}
etcd::Values const& etcd::Response::values() const { return _values; } etcd::Values const & etcd::Response::values() const
{
return _values;
}
etcd::Value const& etcd::Response::value(int index) const { etcd::Value const & etcd::Response::value(int index) const
{
return _values[index]; return _values[index];
} }
etcd::Keys const& etcd::Response::keys() const { return _keys; } etcd::Keys const & etcd::Response::keys() const
{
return _keys;
}
std::string const& etcd::Response::key(int index) const { return _keys[index]; } std::string const & etcd::Response::key(int index) const
{
return _keys[index];
}
int64_t etcd::Response::compact_revision() const { return _compact_revision; } int64_t etcd::Response::compact_revision() const
{
return _compact_revision;
}
int64_t etcd::Response::watch_id() const { return _watch_id; } std::string const & etcd::Response::lock_key() const {
return _lock_key;
}
std::string const& etcd::Response::lock_key() const { return _lock_key; } std::string const & etcd::Response::name() const {
return _name;
std::string const& etcd::Response::name() const { return _name; } }
std::vector<etcd::Event> const & etcd::Response::events() const { std::vector<etcd::Event> const & etcd::Response::events() const {
return this->_events; return this->_events;
@ -125,16 +172,18 @@ std::chrono::microseconds const& etcd::Response::duration() const {
return this->_duration; return this->_duration;
} }
uint64_t etcd::Response::cluster_id() const { return this->_cluster_id; } uint64_t etcd::Response::cluster_id() const {
return this->_cluster_id;
}
uint64_t etcd::Response::member_id() const { return this->_member_id; } uint64_t etcd::Response::member_id() const {
return this->_member_id;
}
uint64_t etcd::Response::raft_term() const { return this->_raft_term; } uint64_t etcd::Response::raft_term() const {
return this->_raft_term;
}
std::vector<int64_t> const & etcd::Response::leases() const { std::vector<int64_t> const & etcd::Response::leases() const {
return this->_leases; return this->_leases;
} }
std::vector<etcdv3::Member> const& etcd::Response::members() const {
return this->_members;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,21 @@
#include <cstdint>
#include <iomanip> #include <iomanip>
#include "etcd/Value.hpp" #include "etcd/Value.hpp"
#include "etcd/v3/KeyValue.hpp" #include "etcd/v3/KeyValue.hpp"
etcd::Value::Value() etcd::Value::Value()
: dir(false), created(0), modified(0), _version(0), _ttl(0), leaseId(0) {} : dir(false),
created(0),
modified(0),
_version(0),
_ttl(0),
leaseId(0)
{
}
etcd::Value::Value(etcdv3::KeyValue const& kv) {
etcd::Value::Value(etcdv3::KeyValue const & kv)
{
dir = false; dir = false;
_key = kv.kvs.key(); _key = kv.kvs.key();
value = kv.kvs.value(); value = kv.kvs.value();
@ -18,7 +26,8 @@ etcd::Value::Value(etcdv3::KeyValue const& kv) {
_ttl = kv.get_ttl(); _ttl = kv.get_ttl();
} }
etcd::Value::Value(mvccpb::KeyValue const& kv) { etcd::Value::Value(mvccpb::KeyValue const & kv)
{
dir = false; dir = false;
_key = kv.key(); _key = kv.key();
value = kv.value(); value = kv.value();
@ -29,33 +38,44 @@ etcd::Value::Value(mvccpb::KeyValue const& kv) {
_ttl = -1; _ttl = -1;
} }
std::string const& etcd::Value::key() const { return _key; } std::string const & etcd::Value::key() const
{
return _key;
}
bool etcd::Value::is_dir() const { return dir; } bool etcd::Value::is_dir() const
{
return dir;
}
std::string const& etcd::Value::as_string() const { return value; } std::string const & etcd::Value::as_string() const
{
return value;
}
int64_t etcd::Value::created_index() const { return created; } int64_t etcd::Value::created_index() const
{
return created;
}
int64_t etcd::Value::modified_index() const { return modified; } int64_t etcd::Value::modified_index() const
{
return modified;
}
int64_t etcd::Value::version() const { return _version; } int64_t etcd::Value::version() const
{
return _version;
}
int etcd::Value::ttl() const { return _ttl; } int etcd::Value::ttl() const
{
return _ttl;
}
int64_t etcd::Value::lease() const { return leaseId; } int64_t etcd::Value::lease() const
{
std::ostream& etcd::operator<<(std::ostream& os, const etcd::Value& value) { return leaseId;
os << "Event: {";
os << "Key: " << value.key() << ", ";
os << "Value: " << value.as_string() << ", ";
os << "Created: " << value.created_index() << ", ";
os << "Modified: " << value.modified_index() << ", ";
os << "Version: " << value.version() << ", ";
os << "TTL: " << value.ttl() << ", ";
os << "Lease: " << value.lease() << ", ";
os << "}";
return os;
} }
etcd::Event::Event(mvccpb::Event const & event) { etcd::Event::Event(mvccpb::Event const & event) {
@ -80,37 +100,18 @@ enum etcd::Event::EventType etcd::Event::event_type() const {
return event_type_; return event_type_;
} }
bool etcd::Event::has_kv() const { return _has_kv; } bool etcd::Event::has_kv() const {
return _has_kv;
bool etcd::Event::has_prev_kv() const { return _has_prev_kv; }
const etcd::Value& etcd::Event::kv() const { return _kv; }
const etcd::Value& etcd::Event::prev_kv() const { return _prev_kv; }
std::ostream& etcd::operator<<(std::ostream& os,
const etcd::Event::EventType& value) {
switch (value) {
case etcd::Event::EventType::PUT:
os << "PUT";
break;
case etcd::Event::EventType::DELETE_:
os << "DELETE";
break;
case etcd::Event::EventType::INVALID:
os << "INVALID";
break;
}
return os;
} }
std::ostream& etcd::operator<<(std::ostream& os, const etcd::Event& event) { bool etcd::Event::has_prev_kv() const {
os << "Event type: " << event.event_type(); return _has_prev_kv;
if (event.has_kv()) {
os << ", KV: " << event.kv();
} }
if (event.has_prev_kv()) {
os << ", Prev KV: " << event.prev_kv(); const etcd::Value &etcd::Event::kv() const {
return _kv;
} }
return os;
const etcd::Value &etcd::Event::prev_kv() const {
return _prev_kv;
} }

View File

@ -2,15 +2,14 @@
#include "etcd/SyncClient.hpp" #include "etcd/SyncClient.hpp"
#include "etcd/v3/AsyncGRPC.hpp" #include "etcd/v3/AsyncWatchAction.hpp"
struct etcd::Watcher::EtcdServerStubs { struct etcd::Watcher::EtcdServerStubs {
std::unique_ptr<Watch::Stub> watchServiceStub; std::unique_ptr<Watch::Stub> watchServiceStub;
std::unique_ptr<etcdv3::AsyncWatchAction> call; std::unique_ptr<etcdv3::AsyncWatchAction> call;
}; };
void etcd::Watcher::EtcdServerStubsDeleter::operator()( void etcd::Watcher::EtcdServerStubsDeleter::operator()(etcd::Watcher::EtcdServerStubs *stubs) {
etcd::Watcher::EtcdServerStubs* stubs) {
if (stubs) { if (stubs) {
if (stubs->watchServiceStub) { if (stubs->watchServiceStub) {
stubs->watchServiceStub.reset(); stubs->watchServiceStub.reset();
@ -24,20 +23,23 @@ void etcd::Watcher::EtcdServerStubsDeleter::operator()(
etcd::Watcher::Watcher(SyncClient const &client, std::string const & key, etcd::Watcher::Watcher(SyncClient const &client, std::string const & key,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive) std::function<void(bool)> wait_callback,
: Watcher(client, key, -1, callback, wait_callback, recursive) {} bool recursive):
Watcher(client, key, -1, callback, wait_callback, recursive) {
}
etcd::Watcher::Watcher(SyncClient const &client, std::string const & key, etcd::Watcher::Watcher(SyncClient const &client, std::string const & key,
std::string const &range_end, std::string const &range_end,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback) std::function<void(bool)> wait_callback):
: Watcher(client, key, range_end, -1, callback, wait_callback) {} Watcher(client, key, range_end, -1, callback, wait_callback) {
}
etcd::Watcher::Watcher(SyncClient const& client, std::string const& key, etcd::Watcher::Watcher(SyncClient const &client, std::string const & key, int64_t fromIndex,
int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive) std::function<void(bool)> wait_callback,
: wait_callback(wait_callback), fromIndex(fromIndex), recursive(recursive) { bool recursive):
wait_callback(wait_callback), fromIndex(fromIndex), recursive(recursive) {
stubs.reset(new EtcdServerStubs{}); stubs.reset(new EtcdServerStubs{});
stubs->watchServiceStub = Watch::NewStub(client.channel); stubs->watchServiceStub = Watch::NewStub(client.channel);
doWatch(key, "", client.current_auth_token(), callback); doWatch(key, "", client.current_auth_token(), callback);
@ -46,8 +48,8 @@ etcd::Watcher::Watcher(SyncClient const& client, std::string const& key,
etcd::Watcher::Watcher(SyncClient const &client, std::string const & key, etcd::Watcher::Watcher(SyncClient const &client, std::string const & key,
std::string const &range_end, int64_t fromIndex, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback) std::function<void(bool)> wait_callback):
: wait_callback(wait_callback), fromIndex(fromIndex), recursive(false) { wait_callback(wait_callback), fromIndex(fromIndex), recursive(false) {
stubs.reset(new EtcdServerStubs{}); stubs.reset(new EtcdServerStubs{});
stubs->watchServiceStub = Watch::NewStub(client.channel); stubs->watchServiceStub = Watch::NewStub(client.channel);
doWatch(key, range_end, client.current_auth_token(), callback); doWatch(key, range_end, client.current_auth_token(), callback);
@ -55,172 +57,203 @@ etcd::Watcher::Watcher(SyncClient const& client, std::string const& key,
etcd::Watcher::Watcher(std::string const & address, std::string const & key, etcd::Watcher::Watcher(std::string const & address, std::string const & key,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive) std::function<void(bool)> wait_callback,
: Watcher(address, key, -1, callback, wait_callback, recursive) {} bool recursive):
Watcher(address, key, -1, callback, wait_callback, recursive) {
}
etcd::Watcher::Watcher(std::string const & address, std::string const & key, etcd::Watcher::Watcher(std::string const & address, std::string const & key,
std::string const & range_end, std::string const & range_end,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback) std::function<void(bool)> wait_callback):
: Watcher(address, key, range_end, -1, callback, wait_callback) {} Watcher(address, key, range_end, -1, callback, wait_callback) {
}
etcd::Watcher::Watcher(std::string const& address, std::string const& key, etcd::Watcher::Watcher(std::string const & address, std::string const & key, int64_t fromIndex,
int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive) std::function<void(bool)> wait_callback,
: Watcher(SyncClient(address), key, fromIndex, callback, wait_callback, bool recursive):
recursive) {} Watcher(SyncClient(address), key, fromIndex, callback, wait_callback, recursive) {
}
etcd::Watcher::Watcher(std::string const & address, std::string const & key, etcd::Watcher::Watcher(std::string const & address, std::string const & key,
std::string const & range_end, int64_t fromIndex, std::string const & range_end, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback) std::function<void(bool)> wait_callback):
: Watcher(SyncClient(address), key, range_end, fromIndex, callback, Watcher(SyncClient(address), key, range_end, fromIndex, callback, wait_callback) {
wait_callback) {} }
etcd::Watcher::Watcher(std::string const& address, std::string const& username, etcd::Watcher::Watcher(std::string const & address,
std::string const& password, std::string const& key, std::string const & username, std::string const & password,
std::function<void(Response)> callback, std::string const & key,
std::function<void(bool)> wait_callback, bool recursive,
int const auth_token_ttl)
: Watcher(address, username, password, key, -1, callback, wait_callback,
recursive, auth_token_ttl) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& username,
std::string const& password, std::string const& key,
std::string const& range_end,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, std::function<void(bool)> wait_callback,
int const auth_token_ttl) bool recursive,
: Watcher(address, username, password, key, range_end, -1, callback, int const auth_token_ttl):
wait_callback, auth_token_ttl) {} Watcher(address, username, password, key, -1, callback, wait_callback, recursive, auth_token_ttl) {
}
etcd::Watcher::Watcher(std::string const& address, std::string const& username, etcd::Watcher::Watcher(std::string const & address,
std::string const& password, std::string const& key, std::string const & username, std::string const & password,
int64_t fromIndex, std::string const & key, std::string const & range_end,
std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive,
int const auth_token_ttl)
: Watcher(SyncClient(address, username, password, auth_token_ttl), key,
fromIndex, callback, wait_callback, recursive) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& username,
std::string const& password, std::string const& key,
std::string const& range_end, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, std::function<void(bool)> wait_callback,
int const auth_token_ttl) int const auth_token_ttl):
: Watcher(SyncClient(address, username, password, auth_token_ttl), key, Watcher(address, username, password, key, range_end, -1, callback, wait_callback, auth_token_ttl) {
range_end, fromIndex, callback, wait_callback) {} }
etcd::Watcher::Watcher(std::string const& address, std::string const& ca, etcd::Watcher::Watcher(std::string const & address,
std::string const& cert, std::string const& privkey, std::string const & username, std::string const & password,
std::string const & key, int64_t fromIndex, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, bool recursive, std::function<void(bool)> wait_callback,
std::string const& target_name_override) bool recursive,
: Watcher(SyncClient(address, ca, cert, privkey, target_name_override), key, int const auth_token_ttl):
fromIndex, callback, wait_callback, recursive) {} Watcher(SyncClient(address, username, password, auth_token_ttl), key, fromIndex, callback, wait_callback, recursive) {
}
etcd::Watcher::Watcher(std::string const& address, std::string const& ca, etcd::Watcher::Watcher(std::string const & address,
std::string const& cert, std::string const& privkey, std::string const & username, std::string const & password,
std::string const& key, std::string const& range_end, std::string const & key, std::string const & range_end, int64_t fromIndex,
int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::function<void(bool)> wait_callback, std::function<void(bool)> wait_callback,
std::string const& target_name_override) int const auth_token_ttl):
: Watcher(SyncClient(address, ca, cert, privkey, target_name_override), key, Watcher(SyncClient(address, username, password, auth_token_ttl), key, range_end, fromIndex, callback, wait_callback) {
range_end, fromIndex, callback, wait_callback) {} }
etcd::Watcher::Watcher(SyncClient const& client, std::string const& key, etcd::Watcher::Watcher(std::string const & address,
std::function<void(Response)> callback, bool recursive) std::string const & ca,
: Watcher(client, key, callback, nullptr, recursive) {} std::string const & cert,
std::string const & privkey,
etcd::Watcher::Watcher(SyncClient const& client, std::string const& key,
std::string const& range_end,
std::function<void(Response)> callback)
: Watcher(client, key, range_end, callback, nullptr) {}
etcd::Watcher::Watcher(SyncClient const& client, std::string const& key,
int64_t fromIndex,
std::function<void(Response)> callback, bool recursive)
: Watcher(client, key, fromIndex, callback, nullptr, recursive) {}
etcd::Watcher::Watcher(SyncClient const& client, std::string const& key,
std::string const& range_end, int64_t fromIndex,
std::function<void(Response)> callback)
: Watcher(client, key, range_end, fromIndex, callback, nullptr) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& key,
std::function<void(Response)> callback, bool recursive)
: Watcher(address, key, callback, nullptr, recursive) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& key,
std::string const& range_end,
std::function<void(Response)> callback)
: Watcher(address, key, range_end, callback, nullptr) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& key,
int64_t fromIndex,
std::function<void(Response)> callback, bool recursive)
: Watcher(address, key, fromIndex, callback, nullptr, recursive) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& key,
std::string const& range_end, int64_t fromIndex,
std::function<void(Response)> callback)
: Watcher(address, key, range_end, fromIndex, callback, nullptr) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& username,
std::string const& password, std::string const& key,
std::function<void(Response)> callback, bool recursive,
int const auth_token_ttl)
: Watcher(address, username, password, key, callback, nullptr, recursive,
auth_token_ttl) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& username,
std::string const& password, std::string const& key,
std::string const& range_end,
std::function<void(Response)> callback,
int const auth_token_ttl)
: Watcher(address, username, password, key, range_end, callback, nullptr,
auth_token_ttl) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& username,
std::string const& password, std::string const& key,
int64_t fromIndex,
std::function<void(Response)> callback, bool recursive,
int const auth_token_ttl)
: Watcher(address, username, password, key, fromIndex, callback, nullptr,
recursive, auth_token_ttl) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& username,
std::string const& password, std::string const& key,
std::string const& range_end, int64_t fromIndex,
std::function<void(Response)> callback,
int const auth_token_ttl)
: Watcher(address, username, password, key, range_end, fromIndex, callback,
nullptr, auth_token_ttl) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& ca,
std::string const& cert, std::string const& privkey,
std::string const & key, int64_t fromIndex, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback, bool recursive,
std::string const& target_name_override)
: Watcher(address, ca, cert, privkey, key, fromIndex, callback, nullptr,
recursive, target_name_override) {}
etcd::Watcher::Watcher(std::string const& address, std::string const& ca,
std::string const& cert, std::string const& privkey,
std::string const& key, std::string const& range_end,
int64_t fromIndex,
std::function<void(Response)> callback, std::function<void(Response)> callback,
std::string const& target_name_override) std::function<void(bool)> wait_callback,
: Watcher(address, ca, cert, privkey, key, range_end, fromIndex, callback, bool recursive,
nullptr, target_name_override) {} std::string const & target_name_override):
Watcher(SyncClient(address, ca, cert, privkey, target_name_override), key, fromIndex, callback, wait_callback, recursive) {
}
etcd::Watcher::~Watcher() { this->Cancel(); } etcd::Watcher::Watcher(std::string const & address,
std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & key, std::string const & range_end, int64_t fromIndex,
std::function<void(Response)> callback,
std::function<void(bool)> wait_callback,
std::string const & target_name_override):
Watcher(SyncClient(address, ca, cert, privkey, target_name_override), key, range_end, fromIndex, callback, wait_callback) {
}
bool etcd::Watcher::Wait() { etcd::Watcher::Watcher(SyncClient const &client, std::string const & key,
std::function<void(Response)> callback,
bool recursive):
Watcher(client, key, callback, nullptr, recursive) {
}
etcd::Watcher::Watcher(SyncClient const &client, std::string const & key,
std::string const &range_end,
std::function<void(Response)> callback):
Watcher(client, key, range_end, callback, nullptr) {
}
etcd::Watcher::Watcher(SyncClient const &client, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback,
bool recursive):
Watcher(client, key, fromIndex, callback, nullptr, recursive) {
}
etcd::Watcher::Watcher(SyncClient const &client, std::string const & key,
std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback):
Watcher(client, key, range_end, fromIndex, callback, nullptr) {
}
etcd::Watcher::Watcher(std::string const & address, std::string const & key,
std::function<void(Response)> callback,
bool recursive):
Watcher(address, key, callback, nullptr, recursive) {
}
etcd::Watcher::Watcher(std::string const & address, std::string const & key,
std::string const &range_end,
std::function<void(Response)> callback):
Watcher(address, key, range_end, callback, nullptr) {
}
etcd::Watcher::Watcher(std::string const & address, std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback,
bool recursive):
Watcher(address, key, fromIndex, callback, nullptr, recursive) {
}
etcd::Watcher::Watcher(std::string const & address, std::string const & key,
std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback):
Watcher(address, key, range_end, fromIndex, callback, nullptr) {
}
etcd::Watcher::Watcher(std::string const & address,
std::string const & username, std::string const & password,
std::string const & key,
std::function<void(Response)> callback,
bool recursive,
int const auth_token_ttl):
Watcher(address, username, password, key, callback, nullptr, recursive, auth_token_ttl) {
}
etcd::Watcher::Watcher(std::string const & address,
std::string const & username, std::string const & password,
std::string const & key, std::string const &range_end,
std::function<void(Response)> callback,
int const auth_token_ttl):
Watcher(address, username, password, key, range_end, callback, nullptr, auth_token_ttl) {
}
etcd::Watcher::Watcher(std::string const & address,
std::string const & username, std::string const & password,
std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback,
bool recursive,
int const auth_token_ttl):
Watcher(address, username, password, key, fromIndex, callback, nullptr, recursive, auth_token_ttl) {
}
etcd::Watcher::Watcher(std::string const & address,
std::string const & username, std::string const & password,
std::string const & key, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback,
int const auth_token_ttl):
Watcher(address, username, password, key, range_end, fromIndex, callback, nullptr, auth_token_ttl) {
}
etcd::Watcher::Watcher(std::string const & address,
std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & key, int64_t fromIndex,
std::function<void(Response)> callback,
bool recursive,
std::string const & target_name_override):
Watcher(address, ca, cert, privkey, key, fromIndex, callback, nullptr, recursive, target_name_override) {
}
etcd::Watcher::Watcher(std::string const & address,
std::string const & ca,
std::string const & cert,
std::string const & privkey,
std::string const & key, std::string const &range_end, int64_t fromIndex,
std::function<void(Response)> callback,
std::string const & target_name_override):
Watcher(address, ca, cert, privkey, key, range_end, fromIndex, callback, nullptr, target_name_override) {
}
etcd::Watcher::~Watcher()
{
this->Cancel();
}
bool etcd::Watcher::Wait()
{
if (!cancelled.exchange(true)) { if (!cancelled.exchange(true)) {
if (task_.joinable()) { if (task_.joinable()) {
task_.join(); task_.join();
@ -229,34 +262,31 @@ bool etcd::Watcher::Wait() {
return stubs->call->Cancelled(); return stubs->call->Cancelled();
} }
bool etcd::Watcher::Wait(std::function<void(bool)> callback) { void etcd::Watcher::Wait(std::function<void(bool)> callback)
{
if (wait_callback == nullptr) { if (wait_callback == nullptr) {
wait_callback = callback; wait_callback = callback;
return true;
} else { } else {
#ifndef NDEBUG std::cerr << "Failed to set a asynchronous wait callback since it has already been set" << std::endl;
std::cerr
<< "[warn] failed to set a asynchronous wait callback since it has "
"already been set"
<< std::endl;
#endif
return false;
} }
} }
bool etcd::Watcher::Cancel() { bool etcd::Watcher::Cancel()
{
stubs->call->CancelWatch(); stubs->call->CancelWatch();
return this->Wait(); return this->Wait();
} }
bool etcd::Watcher::Cancelled() const { bool etcd::Watcher::Cancelled() const
{
return cancelled.load() || stubs->call->Cancelled(); return cancelled.load() || stubs->call->Cancelled();
} }
void etcd::Watcher::doWatch(std::string const & key, void etcd::Watcher::doWatch(std::string const & key,
std::string const & range_end, std::string const & range_end,
std::string const & auth_token, std::string const & auth_token,
std::function<void(Response)> callback) { std::function<void(Response)> callback)
{
etcdv3::ActionParameters params; etcdv3::ActionParameters params;
params.auth_token.assign(auth_token); params.auth_token.assign(auth_token);
// n.b.: watch: no need for timeout // n.b.: watch: no need for timeout
@ -274,12 +304,13 @@ void etcd::Watcher::doWatch(std::string const& key,
stubs->call->waitForResponse(callback); stubs->call->waitForResponse(callback);
if (wait_callback != nullptr) { if (wait_callback != nullptr) {
// issue the callback in another thread (detached) to avoid deadlock, // issue the callback in another thread (detached) to avoid deadlock,
// it is ok to detach a pplx::task, but we don't want to use the // it is ok to detach a pplx::task, but we don't want to use the pplx::task
// pplx::task in the core library // in the core library
bool cancelled = stubs->call->Cancelled(); bool cancelled = stubs->call->Cancelled();
std::function<void(bool)> wait_callback = this->wait_callback; std::function<void(bool)> wait_callback = this->wait_callback;
std::thread canceller( std::thread canceller([wait_callback, cancelled]() {
[wait_callback, cancelled]() { wait_callback(cancelled); }); wait_callback(cancelled);
});
canceller.detach(); canceller.detach();
} }
}); });

View File

@ -1,23 +1,16 @@
#include "etcd/v3/Action.hpp"
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpcpp/support/status.h> #include <grpcpp/support/status.h>
#include "etcd/v3/action_constants.hpp" #include "etcd/v3/action_constants.hpp"
#include <cstdlib> #include "etcd/v3/Action.hpp"
#ifndef GPR_ASSERT etcdv3::Action::Action(etcdv3::ActionParameters const &params)
#define GPR_ASSERT(x) \ {
if (!(x)) { \
fprintf(stderr, "%s:%d assert failed\n", __FILE__, __LINE__); \
abort(); \
}
#endif
etcdv3::Action::Action(etcdv3::ActionParameters const& params) {
parameters = params; parameters = params;
this->InitAction(); this->InitAction();
} }
etcdv3::Action::Action(etcdv3::ActionParameters&& params) { etcdv3::Action::Action(etcdv3::ActionParameters && params)
{
parameters = std::move(params); parameters = std::move(params);
this->InitAction(); this->InitAction();
} }
@ -39,7 +32,8 @@ void etcdv3::Action::InitAction() {
start_timepoint = std::chrono::high_resolution_clock::now(); start_timepoint = std::chrono::high_resolution_clock::now();
} }
etcdv3::ActionParameters::ActionParameters() { etcdv3::ActionParameters::ActionParameters()
{
withPrefix = false; withPrefix = false;
revision = 0; revision = 0;
old_revision = 0; old_revision = 0;
@ -56,8 +50,7 @@ bool etcdv3::ActionParameters::has_grpc_timeout() const {
return this->grpc_timeout != std::chrono::microseconds::zero(); return this->grpc_timeout != std::chrono::microseconds::zero();
} }
std::chrono::system_clock::time_point etcdv3::ActionParameters::grpc_deadline() std::chrono::system_clock::time_point etcdv3::ActionParameters::grpc_deadline() const {
const {
return std::chrono::system_clock::now() + this->grpc_timeout; return std::chrono::system_clock::now() + this->grpc_timeout;
} }
@ -80,28 +73,22 @@ void etcdv3::ActionParameters::dump(std::ostream& os) const {
os << " grpc_timeout: " << grpc_timeout.count() << "(ms)" << std::endl; os << " grpc_timeout: " << grpc_timeout.count() << "(ms)" << std::endl;
} }
void etcdv3::Action::waitForResponse() { void etcdv3::Action::waitForResponse()
{
void* got_tag; void* got_tag;
bool ok = false; bool ok = false;
if (parameters.has_grpc_timeout()) { if (parameters.has_grpc_timeout()) {
switch (cq_.AsyncNext(&got_tag, &ok, parameters.grpc_deadline())) { switch (cq_.AsyncNext(&got_tag, &ok, parameters.grpc_deadline())) {
case CompletionQueue::NextStatus::TIMEOUT: { case CompletionQueue::NextStatus::TIMEOUT: {
status = status = grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "gRPC timeout");
grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "gRPC timeout");
break; break;
} }
case CompletionQueue::NextStatus::SHUTDOWN: { case CompletionQueue::NextStatus::SHUTDOWN: {
status = status = grpc::Status(grpc::StatusCode::UNAVAILABLE, "gRPC already shutdown");
grpc::Status(grpc::StatusCode::UNAVAILABLE, "gRPC already shutdown");
break; break;
} }
case CompletionQueue::NextStatus::GOT_EVENT: { case CompletionQueue::NextStatus::GOT_EVENT: {
if (!ok) {
status =
grpc::Status(grpc::StatusCode::ABORTED,
"Failed to execute the action: not ok or invalid tag");
}
break; break;
} }
} }
@ -111,8 +98,7 @@ void etcdv3::Action::waitForResponse() {
} }
} }
const std::chrono::high_resolution_clock::time_point const std::chrono::high_resolution_clock::time_point etcdv3::Action::startTimepoint() {
etcdv3::Action::startTimepoint() {
return this->start_timepoint; return this->start_timepoint;
} }
@ -128,8 +114,7 @@ std::string etcdv3::detail::string_plus_one(std::string const& value) {
return {etcdv3::NUL}; return {etcdv3::NUL};
} }
std::string etcdv3::detail::resolve_etcd_endpoints( std::string etcdv3::detail::resolve_etcd_endpoints(std::string const&default_endpoints) {
std::string const& default_endpoints) {
const char *ep = std::getenv("ETCD_ENDPOINTS"); const char *ep = std::getenv("ETCD_ENDPOINTS");
return ep ? ep : default_endpoints; return ep ? ep : default_endpoints;
} }

View File

@ -0,0 +1,59 @@
#include "etcd/v3/AsyncCompareAndDeleteAction.hpp"
#include "etcd/v3/action_constants.hpp"
#include "etcd/v3/Transaction.hpp"
using etcdserverpb::RangeRequest;
using etcdserverpb::PutRequest;
using etcdserverpb::RequestOp;
using etcdserverpb::ResponseOp;
using etcdserverpb::TxnRequest;
etcdv3::AsyncCompareAndDeleteAction::AsyncCompareAndDeleteAction(
etcdv3::ActionParameters && params, etcdv3::AtomicityType type)
:etcdv3::Action(std::move(params))
{
etcdv3::Transaction transaction(parameters.key);
if(type == etcdv3::AtomicityType::PREV_VALUE)
{
transaction.init_compare(parameters.old_value,
CompareResult::EQUAL,
CompareTarget::VALUE);
}
else if (type == etcdv3::AtomicityType::PREV_INDEX)
{
transaction.init_compare(parameters.old_revision,
CompareResult::EQUAL,
CompareTarget::MOD);
}
transaction.setup_compare_and_delete_operation(parameters.key);
transaction.setup_basic_failure_operation(parameters.key);
response_reader = parameters.kv_stub->AsyncTxn(&context, *transaction.txn_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncTxnResponse etcdv3::AsyncCompareAndDeleteAction::ParseResponse()
{
AsyncTxnResponse txn_resp;
txn_resp.set_action(etcdv3::COMPAREDELETE_ACTION);
if(!status.ok())
{
txn_resp.set_error_code(status.error_code());
txn_resp.set_error_message(status.error_message());
}
else
{
txn_resp.ParseResponse(parameters.key, parameters.withPrefix, reply);
if(!reply.succeeded())
{
txn_resp.set_error_code(ERROR_COMPARE_FAILED);
txn_resp.set_error_message("etcd-cpp-apiv3: compare failed");
}
}
return txn_resp;
}

View File

@ -0,0 +1,61 @@
#include "etcd/v3/AsyncCompareAndSwapAction.hpp"
#include "etcd/v3/action_constants.hpp"
#include "etcd/v3/Transaction.hpp"
using etcdserverpb::RangeRequest;
using etcdserverpb::PutRequest;
using etcdserverpb::RequestOp;
using etcdserverpb::ResponseOp;
using etcdserverpb::TxnRequest;
etcdv3::AsyncCompareAndSwapAction::AsyncCompareAndSwapAction(
etcdv3::ActionParameters && params, etcdv3::AtomicityType type)
: etcdv3::Action(std::move(params))
{
etcdv3::Transaction transaction(parameters.key);
if(type == etcdv3::AtomicityType::PREV_VALUE)
{
transaction.init_compare(parameters.old_value,
CompareResult::EQUAL,
CompareTarget::VALUE);
}
else if (type == etcdv3::AtomicityType::PREV_INDEX)
{
transaction.init_compare(parameters.old_revision,
CompareResult::EQUAL,
CompareTarget::MOD);
}
transaction.setup_basic_failure_operation(parameters.key);
transaction.setup_compare_and_swap_sequence(parameters.value, parameters.lease_id);
response_reader = parameters.kv_stub->AsyncTxn(&context, *transaction.txn_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncTxnResponse etcdv3::AsyncCompareAndSwapAction::ParseResponse()
{
AsyncTxnResponse txn_resp;
txn_resp.set_action(etcdv3::COMPARESWAP_ACTION);
if(!status.ok())
{
txn_resp.set_error_code(status.error_code());
txn_resp.set_error_message(status.error_message());
}
else
{
txn_resp.ParseResponse(parameters.key, parameters.withPrefix, reply);
//if there is an error code returned by parseResponse, we must
//not overwrite it.
if(!reply.succeeded() && !txn_resp.get_error_code())
{
txn_resp.set_error_code(ERROR_COMPARE_FAILED);
txn_resp.set_error_message("etcd-cpp-apiv3: compare failed");
}
}
return txn_resp;
}

View File

@ -0,0 +1,49 @@
#include "etcd/v3/AsyncDeleteAction.hpp"
#include "etcd/v3/action_constants.hpp"
using etcdserverpb::DeleteRangeRequest;
etcdv3::AsyncDeleteAction::AsyncDeleteAction(
ActionParameters && params)
: etcdv3::Action(std::move(params))
{
DeleteRangeRequest del_request;
if (!parameters.withPrefix) {
del_request.set_key(parameters.key);
} else {
if (parameters.key.empty()) {
// see: WithFromKey in etcdv3/client
del_request.set_key(etcdv3::NUL);
del_request.set_range_end(etcdv3::NUL);
} else {
del_request.set_key(parameters.key);
del_request.set_range_end(detail::string_plus_one(parameters.key));
}
}
if(!parameters.range_end.empty()) {
del_request.set_range_end(parameters.range_end);
}
del_request.set_prev_kv(true);
response_reader = parameters.kv_stub->AsyncDeleteRange(&context, del_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncDeleteResponse etcdv3::AsyncDeleteAction::ParseResponse()
{
AsyncDeleteResponse del_resp;
del_resp.set_action(etcdv3::DELETE_ACTION);
if(!status.ok())
{
del_resp.set_error_code(status.error_code());
del_resp.set_error_message(status.error_message());
}
else
{
del_resp.ParseResponse(parameters.key, parameters.withPrefix || !parameters.range_end.empty(), reply);
}
return del_resp;
}

View File

@ -0,0 +1,32 @@
#include "etcd/v3/AsyncDeleteResponse.hpp"
#include "etcd/v3/action_constants.hpp"
void etcdv3::AsyncDeleteResponse::ParseResponse(std::string const& key, bool prefix, DeleteRangeResponse& resp)
{
index = resp.header().revision();
if(resp.prev_kvs_size() == 0)
{
error_code = etcdv3::ERROR_KEY_NOT_FOUND;
error_message = "etcd-cpp-apiv3: key not found";
}
else
{
//get all previous values
for(int cnt=0; cnt < resp.prev_kvs_size(); cnt++)
{
etcdv3::KeyValue kv;
kv.kvs.CopyFrom(resp.prev_kvs(cnt));
values.push_back(kv);
}
if(!prefix)
{
prev_value = values[0];
value = values[0];
value.kvs.clear_value();
values.clear();
}
}
}

View File

@ -0,0 +1,214 @@
#include "etcd/v3/AsyncElectionAction.hpp"
#include <grpcpp/support/status.h>
#include "etcd/v3/action_constants.hpp"
using v3electionpb::LeaderKey;
using v3electionpb::CampaignRequest;
using v3electionpb::CampaignResponse;
using v3electionpb::ProclaimRequest;
using v3electionpb::ProclaimResponse;
using v3electionpb::LeaderRequest;
using v3electionpb::LeaderResponse;
using v3electionpb::ResignRequest;
using v3electionpb::ResignResponse;
etcdv3::AsyncCampaignAction::AsyncCampaignAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
CampaignRequest campaign_request;
campaign_request.set_name(parameters.name);
campaign_request.set_lease(parameters.lease_id);
campaign_request.set_value(parameters.value);
response_reader = parameters.election_stub->AsyncCampaign(&context, campaign_request, &cq_);
response_reader->Finish(&reply, &status, (void *)this);
}
etcdv3::AsyncCampaignResponse etcdv3::AsyncCampaignAction::ParseResponse()
{
AsyncCampaignResponse campaign_resp;
campaign_resp.set_action(etcdv3::CAMPAIGN_ACTION);
if(!status.ok()) {
campaign_resp.set_error_code(status.error_code());
campaign_resp.set_error_message(status.error_message());
}
else {
campaign_resp.ParseResponse(reply);
}
return campaign_resp;
}
etcdv3::AsyncProclaimAction::AsyncProclaimAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
auto leader = new LeaderKey();
leader->set_name(parameters.name);
leader->set_key(parameters.key);
leader->set_rev(parameters.revision);
leader->set_lease(parameters.lease_id);
ProclaimRequest proclaim_request;
proclaim_request.set_allocated_leader(leader);
proclaim_request.set_value(parameters.value);
response_reader = parameters.election_stub->AsyncProclaim(&context, proclaim_request, &cq_);
response_reader->Finish(&reply, &status, (void *)this);
}
etcdv3::AsyncProclaimResponse etcdv3::AsyncProclaimAction::ParseResponse()
{
AsyncProclaimResponse proclaim_resp;
proclaim_resp.set_action(etcdv3::PROCLAIM_ACTION);
if(!status.ok()) {
proclaim_resp.set_error_code(status.error_code());
proclaim_resp.set_error_message(status.error_message());
}
else {
proclaim_resp.ParseResponse(reply);
}
return proclaim_resp;
}
etcdv3::AsyncLeaderAction::AsyncLeaderAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
LeaderRequest leader_request;
leader_request.set_name(parameters.name);
response_reader = parameters.election_stub->AsyncLeader(&context, leader_request, &cq_);
response_reader->Finish(&reply, &status, (void *)this);
}
etcdv3::AsyncLeaderResponse etcdv3::AsyncLeaderAction::ParseResponse()
{
AsyncLeaderResponse leader_resp;
leader_resp.set_action(etcdv3::LEADER_ACTION);
if(!status.ok()) {
leader_resp.set_error_code(status.error_code());
leader_resp.set_error_message(status.error_message());
}
else {
leader_resp.ParseResponse(reply);
}
return leader_resp;
}
etcdv3::AsyncObserveAction::AsyncObserveAction(etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
LeaderRequest leader_request;
leader_request.set_name(parameters.name);
response_reader = parameters.election_stub->AsyncObserve(&context, leader_request, &cq_, (void *)etcdv3::ELECTION_OBSERVE_CREATE);
void *got_tag;
bool ok = false;
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)etcdv3::ELECTION_OBSERVE_CREATE) {
// n.b.: leave the issue of `Read` to the `waitForResponse`
} else {
throw std::runtime_error("failed to create a observe connection");
}
}
void etcdv3::AsyncObserveAction::waitForResponse()
{
void* got_tag;
bool ok = false;
if (isCancelled.load()) {
status = grpc::Status::CANCELLED;
}
if (!status.ok()) {
return;
}
response_reader->Read(&reply, (void *)this);
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void*)this) {
auto response = ParseResponse();
if (response.get_error_code() == 0) {
// issue the next read
response_reader->Read(&reply, (void *)this);
} else {
this->CancelObserve();
}
} else {
this->CancelObserve();
status = grpc::Status::CANCELLED;
}
}
void etcdv3::AsyncObserveAction::CancelObserve()
{
std::lock_guard<std::mutex> scope_lock(this->protect_is_cancalled);
if (!isCancelled.exchange(true)) {
void* got_tag;
bool ok = false;
response_reader->Finish(&status, (void *)this);
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)this) {
// ok
} else {
std::cerr << "Failed to finish a election observing connection" << std::endl;
}
cq_.Shutdown();
}
}
bool etcdv3::AsyncObserveAction::Cancelled() const {
return isCancelled.load();
}
etcdv3::AsyncObserveResponse etcdv3::AsyncObserveAction::ParseResponse()
{
AsyncObserveResponse leader_resp;
leader_resp.set_action(etcdv3::OBSERVE_ACTION);
if(!status.ok()) {
leader_resp.set_error_code(status.error_code());
leader_resp.set_error_message(status.error_message());
}
else {
leader_resp.ParseResponse(reply);
}
return leader_resp;
}
etcdv3::AsyncResignAction::AsyncResignAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
auto leader = new LeaderKey();
leader->set_name(parameters.name);
leader->set_key(parameters.key);
leader->set_rev(parameters.revision);
leader->set_lease(parameters.lease_id);
ResignRequest resign_request;
resign_request.set_allocated_leader(leader);
response_reader = parameters.election_stub->AsyncResign(&context, resign_request, &cq_);
response_reader->Finish(&reply, &status, (void *)this);
}
etcdv3::AsyncResignResponse etcdv3::AsyncResignAction::ParseResponse()
{
AsyncResignResponse resign_resp;
resign_resp.set_action(etcdv3::RESIGN_ACTION);
if(!status.ok()) {
resign_resp.set_error_code(status.error_code());
resign_resp.set_error_message(status.error_message());
}
else {
resign_resp.ParseResponse(reply);
}
return resign_resp;
}

View File

@ -0,0 +1,35 @@
#include "etcd/v3/AsyncElectionResponse.hpp"
#include "etcd/v3/action_constants.hpp"
using etcdserverpb::ResponseOp;
void etcdv3::AsyncCampaignResponse::ParseResponse(CampaignResponse& reply) {
index = reply.header().revision();
auto const &leader = reply.leader();
name = leader.name();
value.kvs.set_key(leader.key());
value.kvs.set_create_revision(leader.rev());
value.kvs.set_lease(leader.lease());
}
void etcdv3::AsyncProclaimResponse::ParseResponse(ProclaimResponse& reply) {
index = reply.header().revision();
}
void etcdv3::AsyncLeaderResponse::ParseResponse(LeaderResponse& reply) {
index = reply.header().revision();
value.kvs = reply.kv();
}
void etcdv3::AsyncObserveResponse::ParseResponse(LeaderResponse& reply) {
index = reply.header().revision();
value.kvs = reply.kv();
}
void etcdv3::AsyncResignResponse::ParseResponse(ResignResponse& reply) {
index = reply.header().revision();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
#include "etcd/v3/AsyncHeadAction.hpp"
#include <cstdlib>
#include "etcd/v3/action_constants.hpp"
using etcdserverpb::RangeRequest;
etcdv3::AsyncHeadAction::AsyncHeadAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
RangeRequest get_request;
get_request.set_key(etcdv3::NUL);
get_request.set_limit(1);
response_reader = parameters.kv_stub->AsyncRange(&context,get_request,&cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncHeadResponse etcdv3::AsyncHeadAction::ParseResponse()
{
AsyncHeadResponse head_resp;
head_resp.set_action(etcdv3::GET_ACTION);
if(!status.ok())
{
head_resp.set_error_code(status.error_code());
head_resp.set_error_message(status.error_message());
}
else
{
head_resp.ParseResponse(reply);
}
return head_resp;
}

View File

@ -0,0 +1,11 @@
#include "etcd/v3/AsyncHeadResponse.hpp"
#include "etcd/v3/action_constants.hpp"
void etcdv3::AsyncHeadResponse::ParseResponse(RangeResponse& resp)
{
cluster_id = resp.header().cluster_id();
member_id = resp.header().member_id();
index = resp.header().revision();
raft_term = resp.header().raft_term();
}

264
src/v3/AsyncLeaseAction.cpp Normal file
View File

@ -0,0 +1,264 @@
#include "etcd/v3/AsyncLeaseAction.hpp"
#include "etcd/Response.hpp"
#include "etcd/v3/action_constants.hpp"
#include "etcd/v3/Transaction.hpp"
#include <grpcpp/support/status.h>
using etcdserverpb::LeaseGrantRequest;
using etcdserverpb::LeaseRevokeRequest;
using etcdserverpb::LeaseCheckpointRequest;
using etcdserverpb::LeaseKeepAliveRequest;
using etcdserverpb::LeaseTimeToLiveRequest;
using etcdserverpb::LeaseLeasesRequest;
etcdv3::AsyncLeaseGrantAction::AsyncLeaseGrantAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
LeaseGrantRequest leasegrant_request;
leasegrant_request.set_ttl(parameters.ttl);
// If ID is set to 0, etcd will choose an ID.
leasegrant_request.set_id(parameters.lease_id);
response_reader = parameters.lease_stub->AsyncLeaseGrant(&context, leasegrant_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncLeaseGrantResponse etcdv3::AsyncLeaseGrantAction::ParseResponse()
{
AsyncLeaseGrantResponse lease_resp;
lease_resp.set_action(etcdv3::LEASEGRANT);
if (!status.ok()) {
lease_resp.set_error_code(status.error_code());
lease_resp.set_error_message(status.error_message());
} else {
lease_resp.ParseResponse(reply);
}
return lease_resp;
}
etcdv3::AsyncLeaseRevokeAction::AsyncLeaseRevokeAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
LeaseRevokeRequest leaserevoke_request;
leaserevoke_request.set_id(parameters.lease_id);
response_reader = parameters.lease_stub->AsyncLeaseRevoke(&context, leaserevoke_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncLeaseRevokeResponse etcdv3::AsyncLeaseRevokeAction::ParseResponse()
{
AsyncLeaseRevokeResponse lease_resp;
lease_resp.set_action(etcdv3::LEASEREVOKE);
if (!status.ok()) {
lease_resp.set_error_code(status.error_code());
lease_resp.set_error_message(status.error_message());
} else {
lease_resp.ParseResponse(reply);
}
return lease_resp;
}
etcdv3::AsyncLeaseKeepAliveAction::AsyncLeaseKeepAliveAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
isCancelled = false;
stream = parameters.lease_stub->AsyncLeaseKeepAlive(&context, &cq_, (void*)etcdv3::KEEPALIVE_CREATE);
void *got_tag = nullptr;
bool ok = false;
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)etcdv3::KEEPALIVE_CREATE) {
// ok
} else {
throw std::runtime_error("Failed to create a lease keep-alive connection");
}
}
etcdv3::AsyncLeaseKeepAliveResponse etcdv3::AsyncLeaseKeepAliveAction::ParseResponse()
{
AsyncLeaseKeepAliveResponse lease_resp;
lease_resp.set_action(etcdv3::LEASEKEEPALIVE);
if (!status.ok()) {
lease_resp.set_error_code(status.error_code());
lease_resp.set_error_message(status.error_message());
} else {
lease_resp.ParseResponse(reply);
}
return lease_resp;
}
etcd::Response etcdv3::AsyncLeaseKeepAliveAction::Refresh()
{
std::lock_guard<std::recursive_mutex> scope_lock(this->protect_is_cancelled);
auto start_timepoint = std::chrono::high_resolution_clock::now();
if (isCancelled) {
status = grpc::Status::CANCELLED;
return etcd::Response(ParseResponse(), etcd::detail::duration_till_now(start_timepoint));
}
LeaseKeepAliveRequest leasekeepalive_request;
leasekeepalive_request.set_id(parameters.lease_id);
void *got_tag = nullptr;
bool ok = false;
if (parameters.has_grpc_timeout()) {
stream->Write(leasekeepalive_request, (void *)etcdv3::KEEPALIVE_WRITE);
// wait write finish
switch (cq_.AsyncNext(&got_tag, &ok, parameters.grpc_deadline())) {
case CompletionQueue::NextStatus::TIMEOUT: {
status = grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "gRPC timeout during keep alive write");
break;
}
case CompletionQueue::NextStatus::SHUTDOWN: {
status = grpc::Status(grpc::StatusCode::UNAVAILABLE, "gRPC already shutdown during keep alive write");
break;
}
case CompletionQueue::NextStatus::GOT_EVENT: {
if (!ok || got_tag != (void *)etcdv3::KEEPALIVE_WRITE) {
return etcd::Response(grpc::StatusCode::ABORTED, "Failed to create a lease keep-alive connection: write not ok or invalid tag");
}
}
}
if (!status.ok()) {
this->CancelKeepAlive();
return etcd::Response(ParseResponse(), etcd::detail::duration_till_now(start_timepoint));
}
stream->Read(&reply, (void*)etcdv3::KEEPALIVE_READ);
// wait read finish
switch (cq_.AsyncNext(&got_tag, &ok, parameters.grpc_deadline())) {
case CompletionQueue::NextStatus::TIMEOUT: {
status = grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "gRPC timeout during keep alive read");
break;
}
case CompletionQueue::NextStatus::SHUTDOWN: {
status = grpc::Status(grpc::StatusCode::UNAVAILABLE, "gRPC already shutdown during keep alive read");
break;
}
case CompletionQueue::NextStatus::GOT_EVENT: {
if (ok && got_tag == (void *)etcdv3::KEEPALIVE_READ) {
return etcd::Response(ParseResponse(), etcd::detail::duration_till_now(start_timepoint));
}
break;
}
}
this->CancelKeepAlive();
return etcd::Response(grpc::StatusCode::ABORTED, "Failed to create a lease keep-alive connection: read not ok or invalid tag");
} else {
stream->Write(leasekeepalive_request, (void *)etcdv3::KEEPALIVE_WRITE);
// wait write finish
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)etcdv3::KEEPALIVE_WRITE) {
stream->Read(&reply, (void*)etcdv3::KEEPALIVE_READ);
// wait read finish
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)etcdv3::KEEPALIVE_READ) {
return etcd::Response(ParseResponse(), etcd::detail::duration_till_now(start_timepoint));
}
}
this->CancelKeepAlive();
return etcd::Response(grpc::StatusCode::ABORTED, "Failed to create a lease keep-alive connection: read not ok or invalid tag");
}
}
void etcdv3::AsyncLeaseKeepAliveAction::CancelKeepAlive()
{
std::lock_guard<std::recursive_mutex> scope_lock(this->protect_is_cancelled);
if(isCancelled == false)
{
isCancelled = true;
void *got_tag = nullptr;
bool ok = false;
stream->WritesDone((void*)etcdv3::KEEPALIVE_DONE);
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)etcdv3::KEEPALIVE_DONE) {
// ok
} else {
std::cerr << "Failed to mark a lease keep-alive connection as DONE: "
<< context.debug_error_string() << std::endl;
}
stream->Finish(&status, (void *)KEEPALIVE_FINISH);
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)KEEPALIVE_FINISH) {
// ok
} else {
std::cerr << "Failed to finish a lease keep-alive connection: "
<< status.error_message()
<< ", " << context.debug_error_string() << std::endl;
}
// cancel on-the-fly calls
context.TryCancel();
cq_.Shutdown();
}
}
bool etcdv3::AsyncLeaseKeepAliveAction::Cancelled() const
{
return isCancelled;
}
etcdv3::ActionParameters& etcdv3::AsyncLeaseKeepAliveAction::mutable_parameters() {
return this->parameters;
}
etcdv3::AsyncLeaseTimeToLiveAction::AsyncLeaseTimeToLiveAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
LeaseTimeToLiveRequest leasetimetolive_request;
leasetimetolive_request.set_id(parameters.lease_id);
// FIXME: unsupported parameters: "keys"
// leasetimetolive_request.set_keys(parameters.keys);
response_reader = parameters.lease_stub->AsyncLeaseTimeToLive(&context, leasetimetolive_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncLeaseTimeToLiveResponse etcdv3::AsyncLeaseTimeToLiveAction::ParseResponse()
{
AsyncLeaseTimeToLiveResponse lease_resp;
lease_resp.set_action(etcdv3::LEASETIMETOLIVE);
if (!status.ok()) {
lease_resp.set_error_code(status.error_code());
lease_resp.set_error_message(status.error_message());
} else {
lease_resp.ParseResponse(reply);
}
return lease_resp;
}
etcdv3::AsyncLeaseLeasesAction::AsyncLeaseLeasesAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
LeaseLeasesRequest leaseleases_request;
response_reader = parameters.lease_stub->AsyncLeaseLeases(&context, leaseleases_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncLeaseLeasesResponse etcdv3::AsyncLeaseLeasesAction::ParseResponse()
{
AsyncLeaseLeasesResponse lease_resp;
lease_resp.set_action(etcdv3::LEASELEASES);
if (!status.ok()) {
lease_resp.set_error_code(status.error_code());
lease_resp.set_error_message(status.error_message());
} else {
lease_resp.ParseResponse(reply);
}
return lease_resp;
}

View File

@ -0,0 +1,34 @@
#include "etcd/v3/AsyncLeaseResponse.hpp"
#include "etcd/v3/action_constants.hpp"
void etcdv3::AsyncLeaseGrantResponse::ParseResponse(LeaseGrantResponse& resp) {
index = resp.header().revision();
value.kvs.set_lease(resp.id());
value.set_ttl(resp.ttl());
error_message = resp.error();
}
void etcdv3::AsyncLeaseRevokeResponse::ParseResponse(LeaseRevokeResponse& resp) {
index = resp.header().revision();
}
void etcdv3::AsyncLeaseKeepAliveResponse::ParseResponse(LeaseKeepAliveResponse& resp) {
index = resp.header().revision();
value.kvs.set_lease(resp.id());
value.set_ttl(resp.ttl());
}
void etcdv3::AsyncLeaseTimeToLiveResponse::ParseResponse(LeaseTimeToLiveResponse& resp) {
index = resp.header().revision();
value.kvs.set_lease(resp.id());
value.set_ttl(resp.ttl());
// FIXME: unsupported: fields "grantedTTL" and "keys"
}
void etcdv3::AsyncLeaseLeasesResponse::ParseResponse(LeaseLeasesResponse& resp) {
index = resp.header().revision();
for (auto lease : resp.leases()) {
leases.emplace_back(lease.id());
}
}

View File

@ -0,0 +1,64 @@
#include "etcd/v3/AsyncLockAction.hpp"
#include "etcd/v3/action_constants.hpp"
using v3lockpb::LockRequest;
using v3lockpb::UnlockRequest;
etcdv3::AsyncLockAction::AsyncLockAction(
ActionParameters && params)
: etcdv3::Action(std::move(params))
{
LockRequest lock_request;
lock_request.set_name(parameters.key);
lock_request.set_lease(parameters.lease_id);
response_reader = parameters.lock_stub->AsyncLock(&context, lock_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncLockResponse etcdv3::AsyncLockAction::ParseResponse()
{
AsyncLockResponse lock_resp;
lock_resp.set_action(etcdv3::LOCK_ACTION);
if(!status.ok())
{
lock_resp.set_error_code(status.error_code());
lock_resp.set_error_message(status.error_message());
}
else
{
lock_resp.ParseResponse(reply);
}
return lock_resp;
}
etcdv3::AsyncUnlockAction::AsyncUnlockAction(
ActionParameters && params)
: etcdv3::Action(std::move(params))
{
UnlockRequest unlock_request;
unlock_request.set_key(parameters.key);
response_reader = parameters.lock_stub->AsyncUnlock(&context, unlock_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncUnlockResponse etcdv3::AsyncUnlockAction::ParseResponse()
{
AsyncUnlockResponse unlock_resp;
unlock_resp.set_action(etcdv3::UNLOCK_ACTION);
if(!status.ok())
{
unlock_resp.set_error_code(status.error_code());
unlock_resp.set_error_message(status.error_message());
}
else
{
unlock_resp.ParseResponse(reply);
}
return unlock_resp;
}

View File

@ -0,0 +1,13 @@
#include "etcd/v3/AsyncLockResponse.hpp"
#include "etcd/v3/action_constants.hpp"
void etcdv3::AsyncLockResponse::ParseResponse(LockResponse& resp)
{
index = resp.header().revision();
lock_key = resp.key();
}
void etcdv3::AsyncUnlockResponse::ParseResponse(UnlockResponse& resp)
{
index = resp.header().revision();
}

36
src/v3/AsyncPutAction.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "etcd/v3/AsyncPutAction.hpp"
#include "etcd/v3/action_constants.hpp"
using etcdserverpb::PutRequest;
etcdv3::AsyncPutAction::AsyncPutAction(
ActionParameters && params)
: etcdv3::Action(std::move(params))
{
PutRequest put_request;
put_request.set_key(parameters.key);
put_request.set_value(parameters.value);
put_request.set_lease(parameters.lease_id);
put_request.set_prev_kv(true);
response_reader = parameters.kv_stub->AsyncPut(&context, put_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncPutResponse etcdv3::AsyncPutAction::ParseResponse()
{
AsyncPutResponse put_resp;
put_resp.set_action(etcdv3::PUT_ACTION);
if(!status.ok())
{
put_resp.set_error_code(status.error_code());
put_resp.set_error_message(status.error_message());
}
else
{
put_resp.ParseResponse(reply);
}
return put_resp;
}

View File

@ -0,0 +1,13 @@
#include "etcd/v3/AsyncPutResponse.hpp"
#include "etcd/v3/action_constants.hpp"
void etcdv3::AsyncPutResponse::ParseResponse(PutResponse& resp)
{
index = resp.header().revision();
//get all previous values
etcdv3::KeyValue kv;
kv.kvs.CopyFrom(resp.prev_kv());
prev_value = kv;
}

View File

@ -0,0 +1,56 @@
#include "etcd/v3/AsyncRangeAction.hpp"
#include <cstdlib>
#include "etcd/v3/action_constants.hpp"
using etcdserverpb::RangeRequest;
etcdv3::AsyncRangeAction::AsyncRangeAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
RangeRequest get_request;
if (!parameters.withPrefix) {
get_request.set_key(parameters.key);
} else {
if (parameters.key.empty()) {
// see: WithFromKey in etcdv3/client
get_request.set_key(etcdv3::NUL);
get_request.set_range_end(etcdv3::NUL);
} else {
get_request.set_key(parameters.key);
get_request.set_range_end(detail::string_plus_one(parameters.key));
}
}
if(!parameters.range_end.empty()) {
get_request.set_range_end(parameters.range_end);
}
get_request.set_limit(parameters.limit);
get_request.set_sort_order(RangeRequest::SortOrder::RangeRequest_SortOrder_NONE);
// set keys_only and count_only
get_request.set_keys_only(params.keys_only);
get_request.set_count_only(params.count_only);
response_reader = parameters.kv_stub->AsyncRange(&context,get_request,&cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncRangeResponse etcdv3::AsyncRangeAction::ParseResponse()
{
AsyncRangeResponse range_resp;
range_resp.set_action(etcdv3::GET_ACTION);
if(!status.ok())
{
range_resp.set_error_code(status.error_code());
range_resp.set_error_message(status.error_message());
}
else
{
range_resp.ParseResponse(reply, parameters.withPrefix || !parameters.range_end.empty());
}
return range_resp;
}

View File

@ -0,0 +1,29 @@
#include "etcd/v3/AsyncRangeResponse.hpp"
#include "etcd/v3/action_constants.hpp"
void etcdv3::AsyncRangeResponse::ParseResponse(RangeResponse& resp, bool prefix)
{
index = resp.header().revision();
if(resp.kvs_size() == 0 && !prefix)
{
error_code = etcdv3::ERROR_KEY_NOT_FOUND;
error_message = "etcd-cpp-apiv3: key not found";
return;
}
else
{
for(int index=0; index < resp.kvs_size(); index++)
{
etcdv3::KeyValue kv;
kv.kvs.CopyFrom(resp.kvs(index));
values.push_back(kv);
}
if(!prefix)
{
value = values[0];
values.clear();
}
}
}

51
src/v3/AsyncSetAction.cpp Normal file
View File

@ -0,0 +1,51 @@
#include "etcd/v3/AsyncSetAction.hpp"
#include "etcd/v3/action_constants.hpp"
#include "etcd/v3/Transaction.hpp"
etcdv3::AsyncSetAction::AsyncSetAction(
etcdv3::ActionParameters && params, bool create)
: etcdv3::Action(std::move(params))
{
etcdv3::Transaction transaction(parameters.key);
isCreate = create;
transaction.init_compare(CompareResult::EQUAL,
CompareTarget::VERSION);
transaction.setup_basic_create_sequence(parameters.key, parameters.value, parameters.lease_id);
if(isCreate)
{
transaction.setup_basic_failure_operation(parameters.key);
}
else
{
transaction.setup_set_failure_operation(parameters.key, parameters.value, parameters.lease_id);
}
response_reader = parameters.kv_stub->AsyncTxn(&context, *transaction.txn_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncTxnResponse etcdv3::AsyncSetAction::ParseResponse()
{
AsyncTxnResponse txn_resp;
txn_resp.set_action(isCreate? etcdv3::CREATE_ACTION : etcdv3::SET_ACTION);
if(!status.ok())
{
txn_resp.set_error_code(status.error_code());
txn_resp.set_error_message(status.error_message());
}
else
{
txn_resp.ParseResponse(parameters.key, parameters.withPrefix, reply);
if(!reply.succeeded() && isCreate)
{
txn_resp.set_error_code(etcdv3::ERROR_KEY_ALREADY_EXISTS);
txn_resp.set_error_message("etcd-cpp-apiv3: key already exists");
}
}
return txn_resp;
}

38
src/v3/AsyncTxnAction.cpp Normal file
View File

@ -0,0 +1,38 @@
#include "etcd/v3/action_constants.hpp"
#include "etcd/v3/AsyncTxnAction.hpp"
#include "etcd/v3/Transaction.hpp"
etcdv3::AsyncTxnAction::AsyncTxnAction(
etcdv3::ActionParameters && params, etcdv3::Transaction const &tx)
: etcdv3::Action(std::move(params))
{
response_reader = parameters.kv_stub->AsyncTxn(&context, *tx.txn_request, &cq_);
response_reader->Finish(&reply, &status, (void *)this);
}
etcdv3::AsyncTxnResponse etcdv3::AsyncTxnAction::ParseResponse()
{
AsyncTxnResponse txn_resp;
txn_resp.set_action(etcdv3::TXN_ACTION);
if(!status.ok())
{
txn_resp.set_error_code(status.error_code());
txn_resp.set_error_message(status.error_message());
}
else
{
txn_resp.ParseResponse(parameters.key, parameters.withPrefix, reply);
//if there is an error code returned by parseResponse, we must
//not overwrite it.
if(!reply.succeeded() && !txn_resp.get_error_code())
{
txn_resp.set_error_code(ERROR_COMPARE_FAILED);
txn_resp.set_error_message("etcd-cpp-apiv3: compare failed");
}
}
return txn_resp;
}

View File

@ -0,0 +1,48 @@
#include "etcd/v3/AsyncTxnResponse.hpp"
#include "etcd/v3/AsyncRangeResponse.hpp"
#include "etcd/v3/AsyncDeleteResponse.hpp"
#include "etcd/v3/action_constants.hpp"
using etcdserverpb::ResponseOp;
void etcdv3::AsyncTxnResponse::ParseResponse(TxnResponse& reply) {
index = reply.header().revision();
}
void etcdv3::AsyncTxnResponse::ParseResponse(std::string const& key, bool prefix, TxnResponse& reply)
{
index = reply.header().revision();
for(int index=0; index < reply.responses_size(); index++)
{
auto resp = reply.responses(index);
if(ResponseOp::ResponseCase::kResponseRange == resp.response_case())
{
AsyncRangeResponse response;
response.ParseResponse(*(resp.mutable_response_range()),prefix);
error_code = response.get_error_code();
error_message = response.get_error_message();
values = response.get_values();
value = response.get_value();
}
else if(ResponseOp::ResponseCase::kResponsePut == resp.response_case())
{
auto put_resp = resp.response_put();
if(put_resp.has_prev_kv())
{
prev_value.kvs.CopyFrom(put_resp.prev_kv());
}
}
else if(ResponseOp::ResponseCase::kResponseDeleteRange == resp.response_case())
{
AsyncDeleteResponse response;
response.ParseResponse(key,prefix,*(resp.mutable_response_delete_range()));
prev_value.kvs.CopyFrom(response.get_prev_value().kvs);
values = response.get_values();
value = response.get_value();
}
}
}

View File

@ -0,0 +1,50 @@
#include "etcd/v3/AsyncUpdateAction.hpp"
#include "etcd/v3/action_constants.hpp"
#include "etcd/v3/AsyncRangeResponse.hpp"
#include "etcd/v3/Transaction.hpp"
using etcdserverpb::RangeRequest;
using etcdserverpb::PutRequest;
using etcdserverpb::RequestOp;
using etcdserverpb::ResponseOp;
using etcdserverpb::TxnRequest;
etcdv3::AsyncUpdateAction::AsyncUpdateAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
etcdv3::Transaction transaction(parameters.key);
transaction.init_compare(CompareResult::GREATER,
CompareTarget::VERSION);
transaction.setup_compare_and_swap_sequence(parameters.value, parameters.lease_id);
response_reader = parameters.kv_stub->AsyncTxn(&context, *transaction.txn_request, &cq_);
response_reader->Finish(&reply, &status, (void*)this);
}
etcdv3::AsyncTxnResponse etcdv3::AsyncUpdateAction::ParseResponse()
{
AsyncTxnResponse txn_resp;
if(!status.ok())
{
txn_resp.set_error_code(status.error_code());
txn_resp.set_error_message(status.error_message());
}
else
{
if(reply.succeeded())
{
txn_resp.ParseResponse(parameters.key, parameters.withPrefix, reply);
txn_resp.set_action(etcdv3::UPDATE_ACTION);
}
else
{
txn_resp.set_error_code(etcdv3::ERROR_KEY_NOT_FOUND);
txn_resp.set_error_message("etcd-cpp-apiv3: key not found");
}
}
return txn_resp;
}

209
src/v3/AsyncWatchAction.cpp Normal file
View File

@ -0,0 +1,209 @@
#include "etcd/v3/AsyncWatchAction.hpp"
#include "etcd/v3/action_constants.hpp"
using etcdserverpb::WatchCreateRequest;
etcdv3::AsyncWatchAction::AsyncWatchAction(
etcdv3::ActionParameters && params)
: etcdv3::Action(std::move(params))
{
isCancelled.store(false);
stream = parameters.watch_stub->AsyncWatch(&context,&cq_,(void*)etcdv3::WATCH_CREATE);
WatchRequest watch_req;
WatchCreateRequest watch_create_req;
if(!parameters.withPrefix) {
watch_create_req.set_key(parameters.key);
} else {
if (parameters.key.empty()) {
watch_create_req.set_key(etcdv3::NUL);
watch_create_req.set_range_end(etcdv3::NUL);
} else {
watch_create_req.set_key(parameters.key);
watch_create_req.set_range_end(detail::string_plus_one(parameters.key));
}
}
if(!parameters.range_end.empty()) {
watch_create_req.set_range_end(parameters.range_end);
}
watch_create_req.set_prev_kv(true);
watch_create_req.set_start_revision(parameters.revision);
watch_req.mutable_create_request()->CopyFrom(watch_create_req);
// wait "create" success (the stream becomes ready)
void *got_tag;
bool ok = false;
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)etcdv3::WATCH_CREATE) {
stream->Write(watch_req, (void *)etcdv3::WATCH_WRITE);
} else {
throw std::runtime_error("failed to create a watch connection");
}
// wait "write" (WatchCreateRequest) success, and start to read the first reply
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)etcdv3::WATCH_WRITE) {
stream->Read(&reply, (void*)this);
this->watch_id = reply.watch_id();
} else {
throw std::runtime_error("failed to write WatchCreateRequest to server");
}
}
void etcdv3::AsyncWatchAction::waitForResponse()
{
void* got_tag;
bool ok = false;
while(cq_.Next(&got_tag, &ok))
{
if(ok == false)
{
break;
}
if(got_tag == (void *)etcdv3::WATCH_WRITE_CANCEL) {
stream->WritesDone((void*)etcdv3::WATCH_WRITES_DONE);
continue;
}
if(got_tag == (void*)etcdv3::WATCH_WRITES_DONE)
{
stream->Finish(&status, (void *)etcdv3::WATCH_FINISH);
continue;
}
if (got_tag == (void *)etcdv3::WATCH_FINISH) {
// shutdown
cq_.Shutdown();
break;
}
if(got_tag == (void*)this) // read tag
{
if (reply.canceled()) {
// cancel on-the-fly calls, but don't shutdown the completion queue as there
// are still a inflight call to finish
context.TryCancel();
continue;
}
// we stop watch under two conditions:
//
// 1. watch for a future revision, return immediately with empty events set
// 2. receive any effective events.
if ((reply.created() && reply.header().revision() < parameters.revision) ||
reply.events_size() > 0) {
// leave a warning if the response is too large and been fragmented
if (reply.fragment()) {
std::cerr << "WARN: The response hasn't been fully received and parsed" << std::endl;
}
std::cout << "issue a watch cancel" << std::endl;
// cancel the watcher after receiving the good response
this->CancelWatch();
// start the next round to read finish messages, read into "&dummy"
// (use nullptr, as it won't be touched).
stream->Read(nullptr, (void*)etcdv3::WATCH_FINISH);
} else {
// start the next round to read reply, read into "&reply"
stream->Read(&reply, (void*)this);
}
continue;
}
if(isCancelled.load()) {
// invalid tag, and is cancelled
break;
}
}
}
void etcdv3::AsyncWatchAction::CancelWatch()
{
if (!isCancelled.exchange(true)) {
WatchRequest cancel_req;
cancel_req.mutable_cancel_request()->set_watch_id(this->watch_id);
stream->Write(cancel_req, (void *)etcdv3::WATCH_WRITE_CANCEL);
isCancelled.store(true);
}
}
bool etcdv3::AsyncWatchAction::Cancelled() const {
return isCancelled.load();
}
void etcdv3::AsyncWatchAction::waitForResponse(std::function<void(etcd::Response)> callback)
{
void* got_tag;
bool ok = false;
while(cq_.Next(&got_tag, &ok))
{
if(ok == false)
{
break;
}
if(got_tag == (void *)etcdv3::WATCH_WRITE_CANCEL) {
stream->WritesDone((void*)etcdv3::WATCH_WRITES_DONE);
continue;
}
if(got_tag == (void*)etcdv3::WATCH_WRITES_DONE)
{
stream->Finish(&status, (void *)etcdv3::WATCH_FINISH);
continue;
}
if (got_tag == (void *)etcdv3::WATCH_FINISH) {
// shutdown
cq_.Shutdown();
break;
}
if(got_tag == (void*)this) // read tag
{
if (reply.canceled()) {
if (reply.compact_revision() != 0) {
auto resp = ParseResponse();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now() - start_timepoint);
callback(etcd::Response(resp, duration));
}
// cancel on-the-fly calls, but don't shutdown the completion queue as there
// are still a inflight call to finish
context.TryCancel();
continue;
}
// for the callback case, we don't invoke callback immediately if watching
// for a future revision, we wait until there are some effective events.
if(reply.events_size())
{
auto resp = ParseResponse();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now() - start_timepoint);
callback(etcd::Response(resp, duration));
start_timepoint = std::chrono::high_resolution_clock::now();
}
stream->Read(&reply, (void*)this);
continue;
}
if(isCancelled.load()) {
// invalid tag, and is cancelled
break;
}
}
}
etcdv3::AsyncWatchResponse etcdv3::AsyncWatchAction::ParseResponse()
{
AsyncWatchResponse watch_resp;
watch_resp.set_action(etcdv3::WATCH_ACTION);
if(!status.ok())
{
watch_resp.set_error_code(status.error_code());
watch_resp.set_error_message(status.error_message());
}
else
{
watch_resp.ParseResponse(reply);
}
return watch_resp;
}

View File

@ -0,0 +1,46 @@
#include "etcd/v3/AsyncWatchResponse.hpp"
#include "etcd/v3/action_constants.hpp"
void etcdv3::AsyncWatchResponse::ParseResponse(WatchResponse& reply)
{
if (reply.canceled() && reply.compact_revision() != 0) {
error_code = grpc::StatusCode::OUT_OF_RANGE;
error_message = "required revision has been compacted";
compact_revision = reply.compact_revision();
return;
}
index = reply.header().revision();
for (auto const &e: reply.events()) {
events.emplace_back(e);
}
for(int cnt =0; cnt < reply.events_size(); cnt++)
{
auto event = reply.events(cnt);
if(mvccpb::Event::EventType::Event_EventType_PUT == event.type())
{
if(event.kv().version() == 1)
{
action = etcdv3::CREATE_ACTION;
}
else
{
action = etcdv3::SET_ACTION;
}
value.kvs = event.kv();
}
else if(mvccpb::Event::EventType::Event_EventType_DELETE_ == event.type())
{
action = etcdv3::DELETE_ACTION;
value.kvs = event.kv();
}
if(event.has_prev_kv())
{
prev_value.kvs = event.prev_kv();
}
// just store the first occurence of the key in values.
// this is done so tas client will not need to change their behaviour.
// break immediately
break;
}
}

View File

@ -1,7 +1,16 @@
#include "etcd/v3/KeyValue.hpp" #include "etcd/v3/KeyValue.hpp"
etcdv3::KeyValue::KeyValue() { ttl = 0; } etcdv3::KeyValue::KeyValue()
{
ttl = 0;
}
void etcdv3::KeyValue::set_ttl(int ttl) { this->ttl = ttl; } void etcdv3::KeyValue::set_ttl(int ttl)
{
this->ttl = ttl;
}
int etcdv3::KeyValue::get_ttl() const { return ttl; } int etcdv3::KeyValue::get_ttl() const
{
return ttl;
}

View File

@ -1,40 +0,0 @@
#include "etcd/v3/Member.hpp"
etcdv3::Member::Member() {
id = 0;
name = "";
peerURLs = {};
clientURLs = {};
isLearner = false;
}
void etcdv3::Member::set_id(uint64_t const& id) { this->id = id; }
void etcdv3::Member::set_name(std::string const& name) { this->name = name; }
void etcdv3::Member::set_peerURLs(std::vector<std::string> const& peerURLs) {
this->peerURLs = peerURLs;
}
void etcdv3::Member::set_clientURLs(
std::vector<std::string> const& clientURLs) {
this->clientURLs = clientURLs;
}
void etcdv3::Member::set_learner(bool isLearner) {
this->isLearner = isLearner;
}
uint64_t const& etcdv3::Member::get_id() const { return id; }
std::string const& etcdv3::Member::get_name() const { return name; }
std::vector<std::string> const& etcdv3::Member::get_peerURLs() const {
return peerURLs;
}
std::vector<std::string> const& etcdv3::Member::get_clientURLs() const {
return clientURLs;
}
bool etcdv3::Member::get_learner() const { return isLearner; }

View File

@ -2,7 +2,11 @@
#include "proto/rpc.grpc.pb.h" #include "proto/rpc.grpc.pb.h"
#include "etcd/v3/Action.hpp" using etcdserverpb::Compare;
using etcdserverpb::RangeRequest;
using etcdserverpb::PutRequest;
using etcdserverpb::RequestOp;
using etcdserverpb::DeleteRangeRequest;
namespace etcdv3 { namespace etcdv3 {
@ -16,345 +20,182 @@ static etcdserverpb::Compare::CompareTarget to_compare_target(CompareTarget t) {
return static_cast<etcdserverpb::Compare::CompareTarget>(static_cast<int>(t)); return static_cast<etcdserverpb::Compare::CompareTarget>(static_cast<int>(t));
} }
} // namespace detail }
} // namespace etcdv3 }
etcdv3::Transaction::Transaction() { etcdv3::Transaction::Transaction() {
txn_request.reset(new etcdserverpb::TxnRequest{}); txn_request.reset(new etcdserverpb::TxnRequest{});
} }
etcdv3::Transaction::~Transaction() {} etcdv3::Transaction::Transaction(const std::string& key) : key(key) {
txn_request.reset(new etcdserverpb::TxnRequest{});
void etcdv3::Transaction::add_compare(std::string const& key,
CompareTarget const& target,
CompareResult const& result,
Value const& target_value,
std::string const& range_end) {
switch (target) {
case CompareTarget::VERSION:
add_compare_version(key, result, target_value.version, range_end);
break;
case CompareTarget::CREATE:
add_compare_create(key, result, target_value.create_revision, range_end);
break;
case CompareTarget::MOD:
add_compare_mod(key, result, target_value.mod_revision, range_end);
break;
case CompareTarget::VALUE:
add_compare_value(key, result, target_value.value, range_end);
break;
case CompareTarget::LEASE:
add_compare_lease(key, result, target_value.lease, range_end);
break;
default:
// ignore invalid compare target
break;
}
} }
void etcdv3::Transaction::add_compare_version(std::string const& key, void etcdv3::Transaction::reset_key(std::string const& newkey) {
int64_t const& version, key = newkey;
std::string const& range_end) {
this->add_compare_version(key, CompareResult::EQUAL, version, range_end);
} }
void etcdv3::Transaction::add_compare_version(std::string const& key, void etcdv3::Transaction::init_compare(CompareResult result, CompareTarget target){
CompareResult const& result, Compare* compare = txn_request->add_compare();
int64_t const& version,
std::string const& range_end) {
auto compare = txn_request->add_compare();
compare->set_result(detail::to_compare_result(result)); compare->set_result(detail::to_compare_result(result));
compare->set_target(detail::to_compare_target(CompareTarget::VERSION)); compare->set_target(detail::to_compare_target(target));
compare->set_key(key); compare->set_key(key);
compare->set_version(version);
compare->set_range_end(range_end); compare->set_version(0);
} }
void etcdv3::Transaction::add_compare_create(std::string const& key, void etcdv3::Transaction::init_compare(std::string const& old_value, CompareResult result, CompareTarget target){
int64_t const& create_revision, Compare* compare = txn_request->add_compare();
std::string const& range_end) {
this->add_compare_create(key, CompareResult::EQUAL, create_revision,
range_end);
}
void etcdv3::Transaction::add_compare_create(std::string const& key,
CompareResult const& result,
int64_t const& create_revision,
std::string const& range_end) {
auto compare = txn_request->add_compare();
compare->set_result(detail::to_compare_result(result)); compare->set_result(detail::to_compare_result(result));
compare->set_target(detail::to_compare_target(CompareTarget::CREATE)); compare->set_target(detail::to_compare_target(target));
compare->set_key(key); compare->set_key(key);
compare->set_create_revision(create_revision);
compare->set_range_end(range_end); compare->set_value(old_value);
} }
void etcdv3::Transaction::add_compare_mod(std::string const& key, void etcdv3::Transaction::init_compare(int64_t old_index, CompareResult result, CompareTarget target){
int64_t const& mod_revision, Compare* compare = txn_request->add_compare();
std::string const& range_end) {
this->add_compare_mod(key, CompareResult::EQUAL, mod_revision, range_end);
}
void etcdv3::Transaction::add_compare_mod(std::string const& key,
CompareResult const& result,
int64_t const& mod_revision,
std::string const& range_end) {
auto compare = txn_request->add_compare();
compare->set_result(detail::to_compare_result(result)); compare->set_result(detail::to_compare_result(result));
compare->set_target(detail::to_compare_target(CompareTarget::MOD)); compare->set_target(detail::to_compare_target(target));
compare->set_key(key); compare->set_key(key);
compare->set_mod_revision(mod_revision);
compare->set_range_end(range_end); compare->set_mod_revision(old_index);
} }
void etcdv3::Transaction::add_compare_value(std::string const& key, /**
std::string const& value, * get key on failure
std::string const& range_end) { */
this->add_compare_value(key, CompareResult::EQUAL, value, range_end); void etcdv3::Transaction::setup_basic_failure_operation(std::string const& key) {
std::unique_ptr<RangeRequest> get_request(new RangeRequest());
get_request->set_key(key);
RequestOp* req_failure = txn_request->add_failure();
req_failure->set_allocated_request_range(get_request.release());
} }
void etcdv3::Transaction::add_compare_value(std::string const& key, /**
CompareResult const& result, * get key on failure, get key before put, modify and then get updated key
std::string const& value, */
std::string const& range_end) { void etcdv3::Transaction::setup_set_failure_operation(std::string const &key, std::string const &value, int64_t leaseid) {
auto compare = txn_request->add_compare(); std::unique_ptr<PutRequest> put_request(new PutRequest());
compare->set_result(detail::to_compare_result(result));
compare->set_target(detail::to_compare_target(CompareTarget::VALUE));
compare->set_key(key);
compare->set_value(value);
compare->set_range_end(range_end);
}
void etcdv3::Transaction::add_compare_lease(std::string const& key,
int64_t const& lease,
std::string const& range_end) {
this->add_compare_lease(key, CompareResult::EQUAL, lease, range_end);
}
void etcdv3::Transaction::add_compare_lease(std::string const& key,
CompareResult const& result,
int64_t const& lease,
std::string const& range_end) {
auto compare = txn_request->add_compare();
compare->set_result(detail::to_compare_result(result));
compare->set_target(detail::to_compare_target(CompareTarget::LEASE));
compare->set_key(key);
compare->set_lease(lease);
compare->set_range_end(range_end);
}
void etcdv3::Transaction::add_success_range(std::string const& key,
std::string const& range_end,
bool const recursive,
const int64_t limit) {
auto succ = txn_request->add_success();
auto get_request = succ->mutable_request_range();
etcdv3::detail::make_request_with_ranges(*get_request, key, range_end,
recursive);
get_request->set_limit(limit);
}
void etcdv3::Transaction::add_success_put(std::string const& key,
std::string const& value,
int64_t const leaseid,
const bool prev_kv) {
auto succ = txn_request->add_success();
auto put_request = succ->mutable_request_put();
put_request->set_key(key); put_request->set_key(key);
put_request->set_value(value); put_request->set_value(value);
put_request->set_prev_kv(prev_kv); put_request->set_prev_kv(true);
put_request->set_lease(leaseid); put_request->set_lease(leaseid);
RequestOp* req_failure = txn_request->add_failure();
req_failure->set_allocated_request_put(put_request.release());
std::unique_ptr<RangeRequest> get_request(new RangeRequest());
get_request->set_key(key);
req_failure = txn_request->add_failure();
req_failure->set_allocated_request_range(get_request.release());
} }
void etcdv3::Transaction::add_success_delete(std::string const& key, /**
std::string const& range_end, * add key and then get new value of key
bool const recursive, */
const bool prev_kv) { void etcdv3::Transaction::setup_basic_create_sequence(std::string const& key, std::string const& value, int64_t leaseid) {
auto succ = txn_request->add_success(); std::unique_ptr<PutRequest> put_request(new PutRequest());
auto del_request = succ->mutable_request_delete_range();
etcdv3::detail::make_request_with_ranges(*del_request, key, range_end,
recursive);
del_request->set_prev_kv(prev_kv);
}
void etcdv3::Transaction::add_success_txn(
const std::shared_ptr<Transaction> txn) {
auto succ = txn_request->add_success();
auto txn_request = succ->mutable_request_txn();
txn_request->CopyFrom(*txn->txn_request);
}
void etcdv3::Transaction::add_failure_range(std::string const& key,
std::string const& range_end,
bool const recursive,
const int64_t limit) {
auto fail = txn_request->add_failure();
auto get_request = fail->mutable_request_range();
etcdv3::detail::make_request_with_ranges(*get_request, key, range_end,
recursive);
get_request->set_limit(limit);
}
void etcdv3::Transaction::add_failure_put(std::string const& key,
std::string const& value,
int64_t const leaseid,
const bool prev_kv) {
auto fail = txn_request->add_failure();
auto put_request = fail->mutable_request_put();
put_request->set_key(key); put_request->set_key(key);
put_request->set_value(value); put_request->set_value(value);
put_request->set_prev_kv(prev_kv); put_request->set_prev_kv(true);
put_request->set_lease(leaseid); put_request->set_lease(leaseid);
RequestOp* req_success = txn_request->add_success();
req_success->set_allocated_request_put(put_request.release());
std::unique_ptr<RangeRequest> get_request(new RangeRequest());
get_request->set_key(key);
req_success = txn_request->add_success();
req_success->set_allocated_request_range(get_request.release());
} }
void etcdv3::Transaction::add_failure_delete(std::string const& key, /**
std::string const& range_end, * get key value then modify and get new value
bool const recursive, */
const bool prev_kv) { void etcdv3::Transaction::setup_compare_and_swap_sequence(std::string const& value, int64_t leaseid) {
auto fail = txn_request->add_failure(); std::unique_ptr<PutRequest> put_request(new PutRequest());
auto del_request = fail->mutable_request_delete_range(); put_request->set_key(key);
etcdv3::detail::make_request_with_ranges(*del_request, key, range_end, put_request->set_value(value);
recursive); put_request->set_prev_kv(true);
del_request->set_prev_kv(prev_kv); put_request->set_lease(leaseid);
RequestOp* req_success = txn_request->add_success();
req_success->set_allocated_request_put(put_request.release());
std::unique_ptr<RangeRequest> get_request(new RangeRequest());
get_request->set_key(key);
req_success = txn_request->add_success();
req_success->set_allocated_request_range(get_request.release());
} }
void etcdv3::Transaction::add_failure_txn( /**
const std::shared_ptr<Transaction> txn) { * get key, delete
auto fail = txn_request->add_failure(); */
auto txn_request = fail->mutable_request_txn(); void etcdv3::Transaction::setup_delete_sequence(std::string const &key, std::string const &range_end, bool recursive) {
txn_request->CopyFrom(*txn->txn_request); std::unique_ptr<DeleteRangeRequest> del_request(new DeleteRangeRequest());
del_request->set_key(key);
del_request->set_prev_kv(true);
if(recursive)
{
del_request->set_range_end(range_end);
} }
void etcdv3::Transaction::setup_compare_and_create( RequestOp* req_success = txn_request->add_success();
std::string const& key, std::string const& prev_value, req_success->set_allocated_request_delete_range(del_request.release());
std::string const& create_key, std::string const& value,
int64_t const leaseid) {
this->add_compare_value(key, CompareResult::EQUAL, prev_value);
this->add_success_put(create_key, value, leaseid);
this->add_failure_range(key);
} }
void etcdv3::Transaction::setup_compare_or_create(std::string const& key, /**
std::string const& prev_value, * get key, delete
std::string const& create_key, */
std::string const& value, void etcdv3::Transaction::setup_delete_failure_operation(std::string const &key, std::string const &range_end, bool recursive) {
int64_t const leaseid) { std::unique_ptr<RangeRequest> get_request(new RangeRequest());
this->add_compare_value(key, CompareResult::NOT_EQUAL, prev_value); std::unique_ptr<DeleteRangeRequest> del_request(new DeleteRangeRequest());
this->add_success_put(create_key, value, leaseid); get_request.reset(new RangeRequest());
this->add_failure_range(key); get_request->set_key(key);
if(recursive)
{
get_request->set_range_end(range_end);
get_request->set_sort_target(RangeRequest::SortTarget::RangeRequest_SortTarget_KEY);
get_request->set_sort_order(RangeRequest::SortOrder::RangeRequest_SortOrder_ASCEND);
}
RequestOp* req_failure = txn_request->add_failure();
req_failure->set_allocated_request_range(get_request.release());
del_request.reset(new DeleteRangeRequest());
del_request->set_key(key);
if(recursive)
{
del_request->set_range_end(range_end);
} }
void etcdv3::Transaction::setup_compare_and_swap(std::string const& key, req_failure = txn_request->add_failure();
std::string const& prev_value, req_failure->set_allocated_request_delete_range(del_request.release());
std::string const& value,
int64_t const leaseid) {
this->add_compare_value(key, CompareResult::EQUAL, prev_value);
this->add_success_put(key, value, leaseid);
this->add_failure_range(key);
} }
void etcdv3::Transaction::setup_compare_or_swap(std::string const& key, void etcdv3::Transaction::setup_compare_and_delete_operation(std::string const& key) {
std::string const& prev_value, std::unique_ptr<DeleteRangeRequest> del_request(new DeleteRangeRequest());
std::string const& value, del_request->set_key(key);
int64_t const leaseid) { del_request->set_prev_kv(true);
this->add_compare_value(key, CompareResult::NOT_EQUAL, prev_value); RequestOp* req_success = txn_request->add_success();
this->add_success_put(key, value, leaseid); req_success->set_allocated_request_delete_range(del_request.release());
this->add_failure_range(key);
} }
void etcdv3::Transaction::setup_compare_and_delete( void etcdv3::Transaction::setup_put(std::string const &key, std::string const &value) {
std::string const& key, std::string const& prev_value, std::unique_ptr<PutRequest> put_request(new PutRequest());
std::string const& delete_key, std::string const& range_end, put_request->set_key(key);
const bool recursive) { put_request->set_value(value);
this->add_compare_value(key, CompareResult::EQUAL, prev_value); put_request->set_prev_kv(false);
this->add_success_delete(delete_key, range_end, recursive, RequestOp* req_success = txn_request->add_success();
true /* for backwards compatibility */); req_success->set_allocated_request_put(put_request.release());
this->add_failure_range(key);
}
void etcdv3::Transaction::setup_compare_or_delete(std::string const& key,
std::string const& prev_value,
std::string const& delete_key,
std::string const& range_end,
const bool recursive) {
this->add_compare_value(key, CompareResult::NOT_EQUAL, prev_value);
this->add_success_delete(delete_key, range_end, recursive,
true /* for backwards compatibility */);
this->add_failure_range(key);
}
void etcdv3::Transaction::setup_compare_and_create(
std::string const& key, const int64_t prev_revision,
std::string const& create_key, std::string const& value,
int64_t const leaseid) {
this->add_compare_mod(key, CompareResult::EQUAL, prev_revision);
this->add_success_put(create_key, value, leaseid);
this->add_failure_range(key);
}
void etcdv3::Transaction::setup_compare_or_create(std::string const& key,
const int64_t prev_revision,
std::string const& create_key,
std::string const& value,
int64_t const leaseid) {
this->add_compare_mod(key, CompareResult::NOT_EQUAL, prev_revision);
this->add_success_put(create_key, value, leaseid);
this->add_failure_range(key);
}
void etcdv3::Transaction::setup_compare_and_swap(std::string const& key,
const int64_t prev_revision,
std::string const& value,
int64_t const leaseid) {
this->add_compare_mod(key, CompareResult::EQUAL, prev_revision);
this->add_success_put(key, value, leaseid);
this->add_failure_range(key);
}
void etcdv3::Transaction::setup_compare_or_swap(std::string const& key,
const int64_t prev_revision,
std::string const& value,
int64_t const leaseid) {
this->add_compare_mod(key, CompareResult::NOT_EQUAL, prev_revision);
this->add_success_put(key, value, leaseid);
this->add_failure_range(key);
}
void etcdv3::Transaction::setup_compare_and_delete(
std::string const& key, const int64_t prev_revision,
std::string const& delete_key, std::string const& range_end,
const bool recursive) {
this->add_compare_mod(key, CompareResult::EQUAL, prev_revision);
this->add_success_delete(delete_key, range_end, recursive,
true /* for backwards compatibility */);
this->add_failure_range(key);
}
void etcdv3::Transaction::setup_compare_or_delete(std::string const& key,
const int64_t prev_revision,
std::string const& delete_key,
std::string const& range_end,
const bool recursive) {
this->add_compare_mod(key, CompareResult::NOT_EQUAL, prev_revision);
this->add_success_delete(delete_key, range_end, recursive,
true /* for backwards compatibility */);
this->add_failure_range(key);
}
void etcdv3::Transaction::setup_put(std::string const& key,
std::string const& value) {
this->add_success_put(key, value);
} }
void etcdv3::Transaction::setup_delete(std::string const &key) { void etcdv3::Transaction::setup_delete(std::string const &key) {
this->add_success_delete(key, "", false, std::unique_ptr<DeleteRangeRequest> del_request(new DeleteRangeRequest());
true /* for backwards compatibility */); del_request->set_key(key);
del_request->set_prev_kv(false);
RequestOp* req_success = txn_request->add_success();
req_success->set_allocated_request_delete_range(del_request.release());
} }
void etcdv3::Transaction::setup_delete(std::string const& key, etcdv3::Transaction::~Transaction() {
std::string const& range_end,
const bool recursive) {
this->add_success_delete(key, range_end, recursive,
true /* for backwards compatibility */);
} }

View File

@ -1,58 +1,71 @@
#include "etcd/v3/V3Response.hpp" #include "etcd/v3/V3Response.hpp"
#include "etcd/v3/Member.hpp"
#include "etcd/v3/action_constants.hpp" #include "etcd/v3/action_constants.hpp"
void etcdv3::V3Response::set_error_code(int code) { error_code = code; } void etcdv3::V3Response::set_error_code(int code)
{
error_code = code;
}
void etcdv3::V3Response::set_error_message(std::string msg) { void etcdv3::V3Response::set_error_message(std::string msg)
{
error_message = msg; error_message = msg;
} }
int64_t etcdv3::V3Response::get_index() const { return index; } int64_t etcdv3::V3Response::get_index() const
{
return index;
}
std::string const& etcdv3::V3Response::get_action() const { return action; } std::string const & etcdv3::V3Response::get_action() const
{
return action;
}
int etcdv3::V3Response::get_error_code() const { return error_code; } int etcdv3::V3Response::get_error_code() const
{
return error_code;
}
std::string const& etcdv3::V3Response::get_error_message() const { std::string const & etcdv3::V3Response::get_error_message() const
{
return error_message; return error_message;
} }
void etcdv3::V3Response::set_action(std::string action) { void etcdv3::V3Response::set_action(std::string action)
{
this->action = action; this->action = action;
} }
std::vector<etcdv3::KeyValue> const& etcdv3::V3Response::get_values() const { std::vector<etcdv3::KeyValue> const & etcdv3::V3Response::get_values() const
{
return values; return values;
} }
std::vector<etcdv3::KeyValue> const& etcdv3::V3Response::get_prev_values() std::vector<etcdv3::KeyValue> const & etcdv3::V3Response::get_prev_values() const
const { {
return prev_values; return prev_values;
} }
etcdv3::KeyValue const& etcdv3::V3Response::get_value() const { return value; } etcdv3::KeyValue const & etcdv3::V3Response::get_value() const
{
return value;
}
etcdv3::KeyValue const& etcdv3::V3Response::get_prev_value() const { etcdv3::KeyValue const & etcdv3::V3Response::get_prev_value() const
{
return prev_value; return prev_value;
} }
bool etcdv3::V3Response::has_values() const { return values.size() > 0; } bool etcdv3::V3Response::has_values() const
{
return values.size() > 0;
}
int64_t etcdv3::V3Response::get_compact_revision() const { int64_t etcdv3::V3Response::get_compact_revision() const
{
return compact_revision; return compact_revision;
} }
void etcdv3::V3Response::set_compact_revision(const int64_t compact_revision) {
this->compact_revision = compact_revision;
}
int64_t etcdv3::V3Response::get_watch_id() const { return watch_id; }
void etcdv3::V3Response::set_watch_id(const int64_t watch_id) {
this->watch_id = watch_id;
}
void etcdv3::V3Response::set_lock_key(std::string const &key) { void etcdv3::V3Response::set_lock_key(std::string const &key) {
this->lock_key = key; this->lock_key = key;
} }
@ -65,22 +78,26 @@ void etcdv3::V3Response::set_name(std::string const& name) {
this->name = name; this->name = name;
} }
std::string const& etcdv3::V3Response::get_name() const { return this->name; } std::string const & etcdv3::V3Response::get_name() const {
return this->name;
}
std::vector<mvccpb::Event> const & etcdv3::V3Response::get_events() const { std::vector<mvccpb::Event> const & etcdv3::V3Response::get_events() const {
return this->events; return this->events;
} }
uint64_t etcdv3::V3Response::get_cluster_id() const { return this->cluster_id; } uint64_t etcdv3::V3Response::get_cluster_id() const {
return this->cluster_id;
}
uint64_t etcdv3::V3Response::get_member_id() const { return this->member_id; } uint64_t etcdv3::V3Response::get_member_id() const {
return this->member_id;
}
uint64_t etcdv3::V3Response::get_raft_term() const { return this->raft_term; } uint64_t etcdv3::V3Response::get_raft_term() const {
return this->raft_term;
}
std::vector<int64_t> const & etcdv3::V3Response::get_leases() const { std::vector<int64_t> const & etcdv3::V3Response::get_leases() const {
return this->leases; return this->leases;
} }
std::vector<etcdv3::Member> const& etcdv3::V3Response::get_members() const {
return this->members;
}

View File

@ -5,7 +5,7 @@ char const* etcdv3::COMPARESWAP_ACTION = "compareAndSwap";
char const * etcdv3::UPDATE_ACTION = "update"; char const * etcdv3::UPDATE_ACTION = "update";
char const * etcdv3::SET_ACTION = "set"; char const * etcdv3::SET_ACTION = "set";
char const * etcdv3::GET_ACTION = "get"; char const * etcdv3::GET_ACTION = "get";
char const* etcdv3::PUT_ACTION = "set"; // alias char const * etcdv3::PUT_ACTION = "put";
char const * etcdv3::DELETE_ACTION = "delete"; char const * etcdv3::DELETE_ACTION = "delete";
char const * etcdv3::COMPAREDELETE_ACTION = "compareAndDelete"; char const * etcdv3::COMPAREDELETE_ACTION = "compareAndDelete";
char const * etcdv3::LOCK_ACTION = "lock"; char const * etcdv3::LOCK_ACTION = "lock";
@ -19,10 +19,6 @@ char const* etcdv3::LEASEKEEPALIVE = "leasekeepalive";
char const * etcdv3::LEASETIMETOLIVE = "leasetimetolive"; char const * etcdv3::LEASETIMETOLIVE = "leasetimetolive";
char const * etcdv3::LEASELEASES = "leaseleases"; char const * etcdv3::LEASELEASES = "leaseleases";
char const* etcdv3::ADDMEMBER = "addmember";
char const* etcdv3::LISTMEMBER = "listmember";
char const* etcdv3::REMOVEMEMBER = "removemember";
char const * etcdv3::CAMPAIGN_ACTION = "campaign"; char const * etcdv3::CAMPAIGN_ACTION = "campaign";
char const * etcdv3::PROCLAIM_ACTION = "preclaim"; char const * etcdv3::PROCLAIM_ACTION = "preclaim";
char const * etcdv3::LEADER_ACTION = "leader"; char const * etcdv3::LEADER_ACTION = "leader";
@ -46,7 +42,6 @@ char const* etcdv3::WATCH_WRITES_DONE = "watch writes done";
char const * etcdv3::WATCH_FINISH = "watch finish"; char const * etcdv3::WATCH_FINISH = "watch finish";
char const * etcdv3::ELECTION_OBSERVE_CREATE = "observe create"; char const * etcdv3::ELECTION_OBSERVE_CREATE = "observe create";
char const* etcdv3::ELECTION_OBSERVE_FINISH = "observe finish";
const int etcdv3::ERROR_GRPC_OK = 0; const int etcdv3::ERROR_GRPC_OK = 0;
const int etcdv3::ERROR_GRPC_CANCELLED = 1; const int etcdv3::ERROR_GRPC_CANCELLED = 1;

View File

@ -5,15 +5,16 @@
#include "etcd/Client.hpp" #include "etcd/Client.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
TEST_CASE("setup with auth") { TEST_CASE("setup with auth")
{
etcd::Client *etcd = etcd::Client::WithUser(etcd_url, "root", "root"); etcd::Client *etcd = etcd::Client::WithUser(etcd_url, "root", "root");
etcd->rmdir("/test", true).wait(); etcd->rmdir("/test", true).wait();
} }
TEST_CASE("add a new key after authenticate") { TEST_CASE("add a new key after authenticate")
{
etcd::Client *etcd = etcd::Client::WithUser(etcd_url, "root", "root"); etcd::Client *etcd = etcd::Client::WithUser(etcd_url, "root", "root");
etcd->rmdir("/test", true).wait(); etcd->rmdir("/test", true).wait();
etcd::Response resp = etcd->add("/test/key1", "42").get(); etcd::Response resp = etcd->add("/test/key1", "42").get();
@ -27,28 +28,24 @@ TEST_CASE("add a new key after authenticate") {
CHECK(0 < val.modified_index()); CHECK(0 < val.modified_index());
CHECK(1 == val.version()); CHECK(1 == val.version());
CHECK(0 < resp.index()); CHECK(0 < resp.index());
CHECK( CHECK(etcd::ERROR_KEY_ALREADY_EXISTS == etcd->add("/test/key1", "43").get().error_code()); // Key already exists
etcd::ERROR_KEY_ALREADY_EXISTS == CHECK(etcd::ERROR_KEY_ALREADY_EXISTS == etcd->add("/test/key1", "42").get().error_code()); // Key already exists
etcd->add("/test/key1", "43").get().error_code()); // Key already exists CHECK("etcd-cpp-apiv3: key already exists" == etcd->add("/test/key1", "42").get().error_message());
CHECK(
etcd::ERROR_KEY_ALREADY_EXISTS ==
etcd->add("/test/key1", "42").get().error_code()); // Key already exists
CHECK("etcd-cpp-apiv3: key already exists" ==
etcd->add("/test/key1", "42").get().error_message());
} }
TEST_CASE("read a value from etcd") { TEST_CASE("read a value from etcd")
{
etcd::Client *etcd = etcd::Client::WithUser(etcd_url, "root", "root"); etcd::Client *etcd = etcd::Client::WithUser(etcd_url, "root", "root");
etcd::Response resp = etcd->get("/test/key1").get(); etcd::Response resp = etcd->get("/test/key1").get();
CHECK("get" == resp.action()); CHECK("get" == resp.action());
REQUIRE(resp.is_ok()); REQUIRE(resp.is_ok());
REQUIRE(0 == resp.error_code()); REQUIRE(0 == resp.error_code());
CHECK("42" == resp.value().as_string()); CHECK("42" == resp.value().as_string());
CHECK("" == etcd->get("/test").get().value().as_string()); // key points to a CHECK("" == etcd->get("/test").get().value().as_string()); // key points to a directory
// directory
} }
TEST_CASE("cleanup") { TEST_CASE("cleanup")
{
etcd::Client *etcd = etcd::Client::WithUser(etcd_url, "root", "root"); etcd::Client *etcd = etcd::Client::WithUser(etcd_url, "root", "root");
REQUIRE(0 == etcd->rmdir("/test", true).get().error_code()); REQUIRE(0 == etcd->rmdir("/test", true).get().error_code());
} }

View File

@ -19,7 +19,6 @@ foreach(testfile ${TEST_FILES})
add_executable(${test_name} EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/${testfile}) add_executable(${test_name} EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/${testfile})
endif() endif()
use_cxx(${test_name}) use_cxx(${test_name})
set_exceptions(${test_name})
add_test(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}>) add_test(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}>)
target_include_directories(${test_name} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen) target_include_directories(${test_name} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen)

View File

@ -39,14 +39,13 @@ TEST_CASE("campaign and leadership using keepalive") {
std::cout << "finish leader" << std::endl; std::cout << "finish leader" << std::endl;
auto resp3 = etcd.resign("/leader", resp1.value().lease(), auto resp3 = etcd.resign("/leader", resp1.value().lease(), resp1.value().key(), resp1.value().created_index()).get();
resp1.value().key(), resp1.value().created_index())
.get();
CHECK(0 == resp3.error_code()); CHECK(0 == resp3.error_code());
std::cout << "finish resign" << std::endl; std::cout << "finish resign" << std::endl;
} }
TEST_CASE("concurrent campaign with grpc timeout") { TEST_CASE("concurrent campaign with grpc timeout") {
std::string value1 = std::string("192.168.1.6:1880"); std::string value1 = std::string("192.168.1.6:1880");
std::string value2 = std::string("192.168.1.6:1890"); std::string value2 = std::string("192.168.1.6:1890");
@ -61,9 +60,7 @@ TEST_CASE("concurrent campaign with grpc timeout") {
std::this_thread::sleep_for(std::chrono::seconds(10)); std::this_thread::sleep_for(std::chrono::seconds(10));
// resign // resign
auto resp2 = etcd.resign("/leader", resp1.value().lease(), auto resp2 = etcd.resign("/leader", resp1.value().lease(), resp1.value().key(), resp1.value().created_index()).get();
resp1.value().key(), resp1.value().created_index())
.get();
CHECK(0 == resp2.error_code()); CHECK(0 == resp2.error_code());
}; };
@ -95,9 +92,7 @@ TEST_CASE("concurrent campaign with grpc timeout") {
} }
} }
auto resp2 = etcd.resign("/leader", resp1.value().lease(), auto resp2 = etcd.resign("/leader", resp1.value().lease(), resp1.value().key(), resp1.value().created_index()).get();
resp1.value().key(), resp1.value().created_index())
.get();
CHECK(0 == resp2.error_code()); CHECK(0 == resp2.error_code());
}; };

View File

@ -8,15 +8,16 @@
#include "etcd/Client.hpp" #include "etcd/Client.hpp"
#include "etcd/KeepAlive.hpp" #include "etcd/KeepAlive.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
TEST_CASE("setup") { TEST_CASE("setup")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.rmdir("/test", true).wait(); etcd.rmdir("/test", true).wait();
} }
TEST_CASE("campaign and resign") { TEST_CASE("campaign and resign")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
auto keepalive = etcd.leasekeepalive(60).get(); auto keepalive = etcd.leasekeepalive(60).get();
@ -35,9 +36,9 @@ TEST_CASE("campaign and resign") {
} }
// proclaim // proclaim
auto resp3 = etcd.proclaim("test", lease_id, resp1.value().key(), auto resp3 = etcd.proclaim("test", lease_id,
resp1.value().created_index(), "tttt") resp1.value().key(), resp1.value().created_index(),
.get(); "tttt").get();
REQUIRE(0 == resp3.error_code()); REQUIRE(0 == resp3.error_code());
// leader // leader
@ -49,67 +50,13 @@ TEST_CASE("campaign and resign") {
} }
// resign // resign
auto resp5 = etcd.resign("test", lease_id, resp1.value().key(), auto resp5 = etcd.resign("test", lease_id,
resp1.value().created_index()) resp1.value().key(), resp1.value().created_index()).get();
.get();
REQUIRE(0 == resp5.error_code()); REQUIRE(0 == resp5.error_code());
} }
TEST_CASE("campaign and observe") { TEST_CASE("cleanup")
etcd::Client etcd(etcd_url);
auto keepalive = etcd.leasekeepalive(60).get();
auto lease_id = keepalive->Lease();
auto observer_thread = std::thread([&etcd]() {
std::unique_ptr<etcd::SyncClient::Observer> observer = etcd.observe("test");
// wait many change events, blocked execution
for (size_t i = 0; i < 10; ++i) {
etcd::Response resp = observer->WaitOnce();
std::cout << "observe " << resp.value().key()
<< " as the leader: " << resp.value().as_string() << std::endl;
}
std::cout << "finish the observe" << std::endl;
// cancel the observers
observer.reset(nullptr);
});
std::this_thread::sleep_for(std::chrono::seconds(1));
for (int i = 0; i < 5; ++i) {
// campaign
auto resp1 = etcd.campaign("test", lease_id, "xxxx").get();
REQUIRE(0 == resp1.error_code());
std::cout << "key " << resp1.value().key() << " becomes the leader"
<< std::endl;
// proclaim
auto resp3 = etcd.proclaim("test", lease_id, resp1.value().key(),
resp1.value().created_index(),
"tttt - " + std::to_string(i))
.get();
REQUIRE(0 == resp3.error_code());
// leader
{ {
auto resp4 = etcd.leader("test").get();
REQUIRE(0 == resp4.error_code());
REQUIRE(resp1.value().key() == resp4.value().key());
REQUIRE("tttt - " + std::to_string(i) == resp4.value().as_string());
}
// resign
auto resp5 = etcd.resign("test", lease_id, resp1.value().key(),
resp1.value().created_index())
.get();
REQUIRE(0 == resp5.error_code());
std::this_thread::sleep_for(std::chrono::seconds(1));
}
observer_thread.join();
}
TEST_CASE("cleanup") {
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.rmdir("/test", true).get(); etcd.rmdir("/test", true).get();
} }

View File

@ -1,136 +0,0 @@
#include <cmath>
#define CATCH_CONFIG_MAIN
#include <catch.hpp>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <chrono>
#include <cstring>
#include <iostream>
#include <thread>
#include <vector>
#include "etcd/Client.hpp"
static const std::string etcd_url =
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
pid_t new_etcd_pid = 0;
pid_t start_etcd_server(const std::string& etcd_path,
const std::vector<std::string>& args) {
pid_t pid = fork();
if (pid == -1) {
std::cout << "Failed to fork process" << std::endl;
exit(EXIT_FAILURE);
} else if (pid == 0) {
std::vector<char*> c_args;
c_args.push_back(const_cast<char*>(etcd_path.c_str()));
for (const auto& arg : args) {
c_args.push_back(const_cast<char*>(arg.c_str()));
}
c_args.push_back(nullptr);
if (execvp(etcd_path.c_str(), c_args.data()) == -1) {
std::cout << "Failed to exec etcd process: " << std::strerror(errno)
<< std::endl;
exit(EXIT_FAILURE);
}
}
return pid;
}
TEST_CASE("add member") {
etcd::Client etcd(etcd_url);
etcd::Response res = etcd.list_member().get();
REQUIRE(res.is_ok());
CHECK(1 == res.members().size());
std::string member_name = res.members()[0].get_name();
std::string prev_peer_urls = res.members()[0].get_peerURLs()[0];
// Add a new member
std::string peer_urls = "http://127.0.0.1:33691";
std::string client_urls = "http://127.0.0.1:33690";
bool is_learner = false;
res = etcd.add_member(peer_urls, is_learner).get();
REQUIRE(res.is_ok());
// Create the directory for the new etcd server
std::string cmd = "mkdir -p /tmp/new_etcd_member";
system(cmd.c_str());
// Start a new etcd server
std::vector<std::string> args = {
"--name",
"new_etcd_member",
"--initial-advertise-peer-urls",
peer_urls,
"--listen-peer-urls",
peer_urls,
"--initial-cluster",
member_name + "=" + prev_peer_urls + ",new_etcd_member=" + peer_urls,
"--initial-cluster-state",
"existing",
"--listen-client-urls",
client_urls,
"--advertise-client-urls",
client_urls,
"--data-dir",
"/tmp/new_etcd_member",
};
new_etcd_pid = start_etcd_server("/usr/local/bin/etcd", args);
std::this_thread::sleep_for(std::chrono::seconds(30));
// Check the member's number
{
etcd::Response res = etcd.list_member().get();
REQUIRE(res.is_ok());
CHECK(2 == res.members().size());
}
}
TEST_CASE("member remove") {
etcd::Client etcd(etcd_url);
etcd::Response res = etcd.list_member().get();
REQUIRE(res.is_ok());
CHECK(2 == res.members().size());
uint64_t member_id = 0;
for (const auto& member : res.members()) {
if (member.get_name() == "new_etcd_member") {
member_id = member.get_id();
break;
}
}
REQUIRE(member_id != 0);
// Remove the new member
res = etcd.remove_member(member_id).get();
std::this_thread::sleep_for(std::chrono::seconds(30));
// Check whether the new etcd server is quited
{
int status;
pid_t wpid = waitpid(new_etcd_pid, &status, WNOHANG);
REQUIRE(wpid == new_etcd_pid);
REQUIRE(WIFEXITED(status));
REQUIRE(WEXITSTATUS(status) == 0);
}
// Remove the directory for the new etcd server
std::string cmd = "rm -rf /tmp/new_etcd_member";
system(cmd.c_str());
// Check the member's number
{
etcd::Response res = etcd.list_member().get();
REQUIRE(res.is_ok());
CHECK(1 == res.members().size());
}
}

View File

@ -1,30 +0,0 @@
#define CATCH_CONFIG_MAIN
#include <catch.hpp>
#include <iostream>
#include "etcd/Client.hpp"
static const std::string etcd_v4_url =
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
static const std::string etcd_v6_url =
etcdv3::detail::resolve_etcd_endpoints("http://::1:2379");
// http://[ipv6]:port url
static const std::string etcd_ipv6_url =
etcdv3::detail::resolve_etcd_endpoints("http://[::1]:2379");
TEST_CASE("test ipv4 connection") {
std::cout << "ipv4 endpoints: " << etcd_v4_url << std::endl;
etcd::Client etcd(etcd_v4_url);
REQUIRE(etcd.head().get().is_ok());
}
TEST_CASE("test ipv6 connection") {
std::cout << "ipv6 endpoints: " << etcd_v6_url << std::endl;
etcd::Client etcd(etcd_v6_url);
REQUIRE(etcd.head().get().is_ok());
std::cout << "ipv6 endpoints: " << etcd_ipv6_url << std::endl;
etcd::Client etcd1(etcd_ipv6_url);
REQUIRE(etcd1.head().get().is_ok());
}

View File

@ -5,29 +5,22 @@
#include "etcd/SyncClient.hpp" #include "etcd/SyncClient.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
TEST_CASE("sync operations") { TEST_CASE("sync operations")
{
etcd::SyncClient etcd(etcd_url); etcd::SyncClient etcd(etcd_url);
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
etcd::Response res;
int64_t index;
// add // add
CHECK(0 == etcd.add("/test/key1", "42").error_code()); CHECK(0 == etcd.add("/test/key1", "42").error_code());
CHECK(etcd::ERROR_KEY_ALREADY_EXISTS == CHECK(etcd::ERROR_KEY_ALREADY_EXISTS == etcd.add("/test/key1", "42").error_code()); // Key already exists
etcd.add("/test/key1", "41").error_code()); // Key already exists
CHECK("42" == etcd.get("/test/key1").value().as_string()); CHECK("42" == etcd.get("/test/key1").value().as_string());
// modify // modify
CHECK(0 == etcd.modify("/test/key1", "43").error_code()); CHECK(0 == etcd.modify("/test/key1", "43").error_code());
CHECK(etcd::ERROR_KEY_NOT_FOUND == CHECK(etcd::ERROR_KEY_NOT_FOUND == etcd.modify("/test/key2", "43").error_code()); // Key not found
etcd.modify("/test/key2", "43").error_code()); // Key not found CHECK("43" == etcd.modify("/test/key1", "42").prev_value().as_string());
res = etcd.modify("/test/key1", "42");
CHECK(0 == res.error_code());
CHECK("43" == res.prev_value().as_string());
// set // set
CHECK(0 == etcd.set("/test/key1", "43").error_code()); // overwrite CHECK(0 == etcd.set("/test/key1", "43").error_code()); // overwrite
@ -39,8 +32,7 @@ TEST_CASE("sync operations") {
CHECK("43" == etcd.get("/test/key1").value().as_string()); CHECK("43" == etcd.get("/test/key1").value().as_string());
CHECK("44" == etcd.get("/test/key2").value().as_string()); CHECK("44" == etcd.get("/test/key2").value().as_string());
CHECK("44" == etcd.get("/test/key3").value().as_string()); CHECK("44" == etcd.get("/test/key3").value().as_string());
CHECK(etcd::ERROR_KEY_NOT_FOUND == CHECK(etcd::ERROR_KEY_NOT_FOUND == etcd.get("/test/key4").error_code()); // key not found
etcd.get("/test/key4").error_code()); // key not found
// rm // rm
CHECK(3 == etcd.ls("/test").keys().size()); CHECK(3 == etcd.ls("/test").keys().size());
@ -59,37 +51,28 @@ TEST_CASE("sync operations") {
CHECK(2 == etcd.keys("/test/new_dir").keys().size()); CHECK(2 == etcd.keys("/test/new_dir").keys().size());
// rmdir // rmdir
CHECK(etcd::ERROR_KEY_NOT_FOUND == CHECK(etcd::ERROR_KEY_NOT_FOUND == etcd.rmdir("/test/new_dir").error_code()); // key not found
etcd.rmdir("/test/new_dir").error_code()); // key not found
CHECK(0 == etcd.rmdir("/test/new_dir", true).error_code()); CHECK(0 == etcd.rmdir("/test/new_dir", true).error_code());
// compare and swap // compare and swap
etcd.set("/test/key1", "42"); etcd.set("/test/key1", "42");
index = etcd.modify_if("/test/key1", "43", "42").index(); int64_t index = etcd.modify_if("/test/key1", "43", "42").index();
CHECK(etcd::ERROR_COMPARE_FAILED == CHECK(etcd::ERROR_COMPARE_FAILED == etcd.modify_if("/test/key1", "44", "42").error_code());
etcd.modify_if("/test/key1", "44", "42").error_code());
REQUIRE(etcd.modify_if("/test/key1", "44", index).is_ok()); REQUIRE(etcd.modify_if("/test/key1", "44", index).is_ok());
CHECK(etcd::ERROR_COMPARE_FAILED == CHECK(etcd::ERROR_COMPARE_FAILED == etcd.modify_if("/test/key1", "45", index).error_code());
etcd.modify_if("/test/key1", "45", index).error_code());
// atomic compare-and-delete based on prevValue // atomic compare-and-delete based on prevValue
etcd.set("/test/key1", "42"); etcd.set("/test/key1", "42");
CHECK(etcd::ERROR_COMPARE_FAILED == CHECK(etcd::ERROR_COMPARE_FAILED == etcd.rm_if("/test/key1", "43").error_code());
etcd.rm_if("/test/key1", "43").error_code()); CHECK(0 == etcd.rm_if("/test/key1", "42").error_code());
res = etcd.rm_if("/test/key1", "42");
CHECK(
(0 == res.error_code() || etcd::ERROR_KEY_NOT_FOUND == res.error_code()));
// atomic compare-and-delete based on prevIndex // atomic compare-and-delete based on prevIndex
index = etcd.set("/test/key1", "42").index(); index = etcd.set("/test/key1", "42").index();
CHECK(etcd::ERROR_COMPARE_FAILED == CHECK(etcd::ERROR_COMPARE_FAILED == etcd.rm_if("/test/key1", index - 1).error_code());
etcd.rm_if("/test/key1", index - 1).error_code()); CHECK(0 == etcd.rm_if("/test/key1", index).error_code());
res = etcd.rm_if("/test/key1", index);
CHECK(
(0 == res.error_code() || etcd::ERROR_KEY_NOT_FOUND == res.error_code()));
//leasegrant //leasegrant
res = etcd.leasegrant(60); etcd::Response res = etcd.leasegrant(60);
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
CHECK(60 == res.value().ttl()); CHECK(60 == res.value().ttl());
CHECK(0 < res.value().lease()); CHECK(0 < res.value().lease());
@ -105,7 +88,6 @@ TEST_CASE("sync operations") {
res = etcd.set("/test/key1", "43", leaseid); res = etcd.set("/test/key1", "43", leaseid);
REQUIRE(0 == res.error_code()); REQUIRE(0 == res.error_code());
CHECK("set" == res.action()); CHECK("set" == res.action());
res = etcd.get("/test/key1");
CHECK(leaseid == res.value().lease()); CHECK(leaseid == res.value().lease());
//modify with lease //modify with lease
@ -132,7 +114,8 @@ TEST_CASE("sync operations") {
REQUIRE(0 == etcd.rmdir("/test", true).error_code()); REQUIRE(0 == etcd.rmdir("/test", true).error_code());
} }
TEST_CASE("wait for a value change") { TEST_CASE("wait for a value change")
{
etcd::SyncClient etcd(etcd_url); etcd::SyncClient etcd(etcd_url);
etcd.set("/test/key1", "42"); etcd.set("/test/key1", "42");
@ -149,7 +132,8 @@ TEST_CASE("wait for a value change") {
REQUIRE(0 == etcd.rmdir("/test", true).error_code()); REQUIRE(0 == etcd.rmdir("/test", true).error_code());
} }
TEST_CASE("wait for a directory change") { TEST_CASE("wait for a directory change")
{
etcd::SyncClient etcd(etcd_url); etcd::SyncClient etcd(etcd_url);
std::thread watch_thrd1([&]() { std::thread watch_thrd1([&]() {
@ -175,7 +159,8 @@ TEST_CASE("wait for a directory change") {
REQUIRE(0 == etcd.rmdir("/test", true).error_code()); REQUIRE(0 == etcd.rmdir("/test", true).error_code());
} }
TEST_CASE("watch changes in the past") { TEST_CASE("watch changes in the past")
{
etcd::SyncClient etcd(etcd_url); etcd::SyncClient etcd(etcd_url);
int64_t index = etcd.set("/test/key1", "42").index(); int64_t index = etcd.set("/test/key1", "42").index();

View File

@ -7,15 +7,16 @@
#include "etcd/Client.hpp" #include "etcd/Client.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
TEST_CASE("setup") { TEST_CASE("setup")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.rmdir("/test", true).wait(); etcd.rmdir("/test", true).wait();
} }
TEST_CASE("add a new key") { TEST_CASE("add a new key")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.rmdir("/test", true).wait(); etcd.rmdir("/test", true).wait();
etcd::Response resp = etcd.add("/test/key1", "42").get(); etcd::Response resp = etcd.add("/test/key1", "42").get();
@ -29,58 +30,42 @@ TEST_CASE("add a new key") {
CHECK(0 < val.modified_index()); CHECK(0 < val.modified_index());
CHECK(1 == val.version()); CHECK(1 == val.version());
CHECK(0 < resp.index()); CHECK(0 < resp.index());
CHECK(etcd::ERROR_KEY_ALREADY_EXISTS == CHECK(etcd::ERROR_KEY_ALREADY_EXISTS == etcd.add("/test/key1", "43").get().error_code()); // Key already exists
etcd.add("/test/key1", "43").get().error_code()); // Key already exists CHECK(etcd::ERROR_KEY_ALREADY_EXISTS == etcd.add("/test/key1", "42").get().error_code()); // Key already exists
CHECK(etcd::ERROR_KEY_ALREADY_EXISTS == CHECK("etcd-cpp-apiv3: key already exists" == etcd.add("/test/key1", "42").get().error_message());
etcd.add("/test/key1", "42").get().error_code()); // Key already exists
CHECK("etcd-cpp-apiv3: key already exists" ==
etcd.add("/test/key1", "42").get().error_message());
} }
TEST_CASE("read a value from etcd") { TEST_CASE("read a value from etcd")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd::Response resp = etcd.get("/test/key1").get(); etcd::Response resp = etcd.get("/test/key1").get();
CHECK("get" == resp.action()); CHECK("get" == resp.action());
REQUIRE(resp.is_ok()); REQUIRE(resp.is_ok());
REQUIRE(0 == resp.error_code()); REQUIRE(0 == resp.error_code());
CHECK("42" == resp.value().as_string()); CHECK("42" == resp.value().as_string());
CHECK("" == etcd.get("/test").get().value().as_string()); // key points to a CHECK("" == etcd.get("/test").get().value().as_string()); // key points to a directory
// directory
} }
TEST_CASE("simplified read") { TEST_CASE("simplified read")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
CHECK("42" == etcd.get("/test/key1").get().value().as_string()); CHECK("42" == etcd.get("/test/key1").get().value().as_string());
CHECK(etcd::ERROR_KEY_NOT_FOUND == CHECK(etcd::ERROR_KEY_NOT_FOUND == etcd.get("/test/key2").get().error_code()); // Key not found
etcd.get("/test/key2").get().error_code()); // Key not found CHECK("" == etcd.get("/test/key2").get().value().as_string()); // Key not found
CHECK("" ==
etcd.get("/test/key2").get().value().as_string()); // Key not found
} }
TEST_CASE("modify a key") { TEST_CASE("modify a key")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd::Response resp = etcd.modify("/test/key1", "43").get();
// get
etcd::Response resp = etcd.get("/test/key1").get();
REQUIRE(resp.is_ok());
int64_t revision = resp.value().modified_index();
CHECK("42" == resp.value().as_string());
// modify
resp = etcd.modify("/test/key1", "43").get();
REQUIRE(0 == resp.error_code()); // overwrite REQUIRE(0 == resp.error_code()); // overwrite
CHECK("update" == resp.action()); CHECK("update" == resp.action());
CHECK(etcd::ERROR_KEY_NOT_FOUND == CHECK(etcd::ERROR_KEY_NOT_FOUND == etcd.modify("/test/key2", "43").get().error_code()); // Key not found
etcd.modify("/test/key2", "43").get().error_code()); // Key not found
CHECK("43" == etcd.modify("/test/key1", "42").get().prev_value().as_string()); CHECK("43" == etcd.modify("/test/key1", "42").get().prev_value().as_string());
// check previous
resp = etcd.get("/test/key1", revision).get();
REQUIRE(resp.is_ok());
CHECK("42" == resp.value().as_string());
} }
TEST_CASE("set a key") { TEST_CASE("set a key")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd::Response resp = etcd.set("/test/key1", "43").get(); etcd::Response resp = etcd.set("/test/key1", "43").get();
REQUIRE(0 == resp.error_code()); // overwrite REQUIRE(0 == resp.error_code()); // overwrite
@ -91,16 +76,16 @@ TEST_CASE("set a key") {
CHECK(0 == etcd.set("/test", "42").get().error_code()); // Not a file CHECK(0 == etcd.set("/test", "42").get().error_code()); // Not a file
//set with ttl //set with ttl
resp = etcd.set("/test/key1", "50").get(); resp = etcd.set("/test/key1", "50", 10).get();
REQUIRE(0 == resp.error_code()); // overwrite REQUIRE(0 == resp.error_code()); // overwrite
CHECK("set" == resp.action()); CHECK("set" == resp.action());
CHECK("43" == resp.prev_value().as_string()); CHECK("43" == resp.prev_value().as_string());
resp = etcd.get("/test/key1").get();
CHECK("50" == resp.value().as_string()); CHECK("50" == resp.value().as_string());
CHECK(0 == resp.value().lease()); CHECK( 0 < resp.value().lease());
} }
TEST_CASE("atomic compare-and-swap") { TEST_CASE("atomic compare-and-swap")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.set("/test/key1", "42").wait(); etcd.set("/test/key1", "42").wait();
@ -116,14 +101,15 @@ TEST_CASE("atomic compare-and-swap") {
CHECK(etcd::ERROR_COMPARE_FAILED == res.error_code()); CHECK(etcd::ERROR_COMPARE_FAILED == res.error_code());
CHECK("etcd-cpp-apiv3: compare failed" == res.error_message()); CHECK("etcd-cpp-apiv3: compare failed" == res.error_message());
// modify fails on non-existing keys // modify fails the second time
res = etcd.modify_if("/test/key222", "44", "42").get(); res = etcd.modify_if("/test/key222", "44", "42").get();
CHECK(!res.is_ok()); CHECK(!res.is_ok());
CHECK(etcd::ERROR_COMPARE_FAILED == res.error_code()); CHECK(etcd::ERROR_KEY_NOT_FOUND == res.error_code());
CHECK("etcd-cpp-apiv3: compare failed" == res.error_message()); CHECK("etcd-cpp-apiv3: key not found" == res.error_message());
} }
TEST_CASE("delete a value") { TEST_CASE("delete a value")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd::Response resp = etcd.rm("/test/key11111").get(); etcd::Response resp = etcd.rm("/test/key11111").get();
CHECK(!resp.is_ok()); CHECK(!resp.is_ok());
@ -145,14 +131,15 @@ TEST_CASE("delete a value") {
CHECK( modify_index == resp.prev_value().modified_index()); CHECK( modify_index == resp.prev_value().modified_index());
CHECK( version == resp.prev_value().version()); CHECK( version == resp.prev_value().version());
CHECK("delete" == resp.action()); CHECK("delete" == resp.action());
CHECK(create_index == resp.value().created_index());
CHECK( modify_index == resp.value().modified_index()); CHECK( modify_index == resp.value().modified_index());
CHECK( create_index == resp.value().created_index());
CHECK( version == resp.value().version()); CHECK( version == resp.value().version());
CHECK("43" == resp.value().as_string()); CHECK("" == resp.value().as_string());
CHECK( "/test/key1" == resp.value().key()); CHECK( "/test/key1" == resp.value().key());
} }
TEST_CASE("atomic compare-and-delete based on prevValue") { TEST_CASE("atomic compare-and-delete based on prevValue")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.set("/test/key1", "42").wait(); etcd.set("/test/key1", "42").wait();
@ -167,7 +154,8 @@ TEST_CASE("atomic compare-and-delete based on prevValue") {
CHECK("42" == res.prev_value().as_string()); CHECK("42" == res.prev_value().as_string());
} }
TEST_CASE("atomic compare-and-delete based on prevIndex") { TEST_CASE("atomic compare-and-delete based on prevIndex")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
int64_t index = etcd.set("/test/key1", "42").get().index(); int64_t index = etcd.set("/test/key1", "42").get().index();
@ -182,7 +170,8 @@ TEST_CASE("atomic compare-and-delete based on prevIndex") {
CHECK("42" == res.prev_value().as_string()); CHECK("42" == res.prev_value().as_string());
} }
TEST_CASE("deep atomic compare-and-swap") { TEST_CASE("deep atomic compare-and-swap")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.set("/test/key1", "42").wait(); etcd.set("/test/key1", "42").wait();
@ -212,7 +201,8 @@ TEST_CASE("deep atomic compare-and-swap") {
CHECK("etcd-cpp-apiv3: compare failed" == res.error_message()); CHECK("etcd-cpp-apiv3: compare failed" == res.error_message());
} }
TEST_CASE("using binary keys and values, raw char pointer doesn't work") { TEST_CASE("using binary keys and values, raw char pointer doesn't work")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.rmdir("/test", true).wait(); etcd.rmdir("/test", true).wait();
{ {
@ -241,13 +231,12 @@ TEST_CASE("using binary keys and values, raw char pointer doesn't work") {
} }
} }
TEST_CASE("using binary keys and values, std::string is ok for \\0") { TEST_CASE("using binary keys and values, std::string is ok for \\0")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.rmdir("/test", true).wait(); etcd.rmdir("/test", true).wait();
{ {
etcd::Response resp = etcd::Response resp = etcd.put(std::string("/test/key1\0xyz", 14), std::string("42\0foo", 6)).get();
etcd.put(std::string("/test/key1\0xyz", 14), std::string("42\0foo", 6))
.get();
REQUIRE(resp.is_ok()); REQUIRE(resp.is_ok());
} }
{ {
@ -265,7 +254,8 @@ TEST_CASE("using binary keys and values, std::string is ok for \\0") {
} }
} }
TEST_CASE("list a directory") { TEST_CASE("list a directory")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
CHECK(0 == etcd.ls("/test/new_dir").get().keys().size()); CHECK(0 == etcd.ls("/test/new_dir").get().keys().size());
@ -301,7 +291,8 @@ TEST_CASE("list a directory") {
CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok()); CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok());
} }
TEST_CASE("list by range") { TEST_CASE("list by range")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
CHECK(0 == etcd.ls("/test/new_dir").get().keys().size()); CHECK(0 == etcd.ls("/test/new_dir").get().keys().size());
@ -311,15 +302,13 @@ TEST_CASE("list by range") {
etcd.set("/test/new_dir/key3", "value3").wait(); etcd.set("/test/new_dir/key3", "value3").wait();
etcd.set("/test/new_dir/key4", "value4").wait(); etcd.set("/test/new_dir/key4", "value4").wait();
etcd::Response resp1 = etcd::Response resp1 = etcd.ls("/test/new_dir/key1", "/test/new_dir/key3").get();
etcd.ls("/test/new_dir/key1", "/test/new_dir/key3").get();
REQUIRE(resp1.is_ok()); REQUIRE(resp1.is_ok());
CHECK("get" == resp1.action()); CHECK("get" == resp1.action());
REQUIRE(2 == resp1.keys().size()); REQUIRE(2 == resp1.keys().size());
REQUIRE(2 == resp1.values().size()); REQUIRE(2 == resp1.values().size());
etcd::Response resp2 = etcd::Response resp2 = etcd.ls("/test/new_dir/key1", "/test/new_dir/key4").get();
etcd.ls("/test/new_dir/key1", "/test/new_dir/key4").get();
REQUIRE(resp2.is_ok()); REQUIRE(resp2.is_ok());
CHECK("get" == resp2.action()); CHECK("get" == resp2.action());
REQUIRE(3 == resp2.keys().size()); REQUIRE(3 == resp2.keys().size());
@ -331,10 +320,7 @@ TEST_CASE("list by range") {
REQUIRE(4 == resp3.keys().size()); REQUIRE(4 == resp3.keys().size());
REQUIRE(4 == resp3.values().size()); REQUIRE(4 == resp3.values().size());
etcd::Response resp4 = etcd::Response resp4 = etcd.ls("/test/new_dir/key1", etcdv3::detail::string_plus_one("/test/new_dir/key")).get();
etcd.ls("/test/new_dir/key1",
etcdv3::detail::string_plus_one("/test/new_dir/key"))
.get();
REQUIRE(resp4.is_ok()); REQUIRE(resp4.is_ok());
CHECK("get" == resp4.action()); CHECK("get" == resp4.action());
REQUIRE(4 == resp4.keys().size()); REQUIRE(4 == resp4.keys().size());
@ -345,7 +331,8 @@ TEST_CASE("list by range") {
CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok()); CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok());
} }
TEST_CASE("list by range, w/o values") { TEST_CASE("list by range, w/o values")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
CHECK(0 == etcd.ls("/test/new_dir").get().keys().size()); CHECK(0 == etcd.ls("/test/new_dir").get().keys().size());
@ -355,16 +342,14 @@ TEST_CASE("list by range, w/o values") {
etcd.set("/test/new_dir/key3", "value3").wait(); etcd.set("/test/new_dir/key3", "value3").wait();
etcd.set("/test/new_dir/key4", "value4").wait(); etcd.set("/test/new_dir/key4", "value4").wait();
etcd::Response resp1 = etcd::Response resp1 = etcd.ls("/test/new_dir/key1", "/test/new_dir/key2").get();
etcd.ls("/test/new_dir/key1", "/test/new_dir/key2").get();
REQUIRE(resp1.is_ok()); REQUIRE(resp1.is_ok());
CHECK("get" == resp1.action()); CHECK("get" == resp1.action());
REQUIRE(1 == resp1.keys().size()); REQUIRE(1 == resp1.keys().size());
REQUIRE(1 == resp1.values().size()); REQUIRE(1 == resp1.values().size());
REQUIRE(resp1.values()[0].as_string() == "value1"); REQUIRE(resp1.values()[0].as_string() == "value1");
etcd::Response resp2 = etcd::Response resp2 = etcd.keys("/test/new_dir/key1", "/test/new_dir/key2").get();
etcd.keys("/test/new_dir/key1", "/test/new_dir/key2").get();
REQUIRE(resp1.is_ok()); REQUIRE(resp1.is_ok());
CHECK("get" == resp2.action()); CHECK("get" == resp2.action());
REQUIRE(1 == resp2.keys().size()); REQUIRE(1 == resp2.keys().size());
@ -374,15 +359,15 @@ TEST_CASE("list by range, w/o values") {
CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok()); CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok());
} }
TEST_CASE("delete a directory") { TEST_CASE("delete a directory")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.set("/test/new_dir/key1", "value1").wait(); etcd.set("/test/new_dir/key1", "value1").wait();
etcd.set("/test/new_dir/key2", "value2").wait(); etcd.set("/test/new_dir/key2", "value2").wait();
etcd.set("/test/new_dir/key3", "value3").wait(); etcd.set("/test/new_dir/key3", "value3").wait();
CHECK(etcd::ERROR_KEY_NOT_FOUND == CHECK(etcd::ERROR_KEY_NOT_FOUND == etcd.rmdir("/test/new_dir").get().error_code()); // key not found
etcd.rmdir("/test/new_dir").get().error_code()); // key not found
etcd::Response resp = etcd.ls("/test/new_dir").get(); etcd::Response resp = etcd.ls("/test/new_dir").get();
resp = etcd.rmdir("/test/new_dir", true).get(); resp = etcd.rmdir("/test/new_dir", true).get();
@ -393,6 +378,7 @@ TEST_CASE("delete a directory") {
CHECK("value1" == resp.value(0).as_string()); CHECK("value1" == resp.value(0).as_string());
CHECK("value2" == resp.value(1).as_string()); CHECK("value2" == resp.value(1).as_string());
resp = etcd.rmdir("/test/dirnotfound", true).get(); resp = etcd.rmdir("/test/dirnotfound", true).get();
CHECK(!resp.is_ok()); CHECK(!resp.is_ok());
CHECK(etcd::ERROR_KEY_NOT_FOUND == resp.error_code()); CHECK(etcd::ERROR_KEY_NOT_FOUND == resp.error_code());
@ -404,7 +390,8 @@ TEST_CASE("delete a directory") {
CHECK("etcd-cpp-apiv3: key not found" == resp.error_message()); CHECK("etcd-cpp-apiv3: key not found" == resp.error_message());
} }
TEST_CASE("delete all keys with rmdir(\"\", true)") { TEST_CASE("delete all keys with rmdir(\"\", true)")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.rmdir("", true).wait(); etcd.rmdir("", true).wait();
@ -417,11 +404,11 @@ TEST_CASE("delete all keys with rmdir(\"\", true)") {
CHECK(resp.values().size() == 3); CHECK(resp.values().size() == 3);
} }
TEST_CASE("delete by range") { TEST_CASE("delete by range")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
CHECK(etcd::ERROR_KEY_NOT_FOUND == CHECK(etcd::ERROR_KEY_NOT_FOUND == etcd.rmdir("/test/new_dir").get().error_code()); // key not found
etcd.rmdir("/test/new_dir").get().error_code()); // key not found
etcd::Response resp = etcd.ls("/test/new_dir").get(); etcd::Response resp = etcd.ls("/test/new_dir").get();
etcd.set("/test/new_dir/key1", "value1").wait(); etcd.set("/test/new_dir/key1", "value1").wait();
@ -438,7 +425,8 @@ TEST_CASE("delete by range") {
CHECK("value2" == resp.value(1).as_string()); CHECK("value2" == resp.value(1).as_string());
} }
TEST_CASE("wait for a value change") { TEST_CASE("wait for a value change")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.set("/test/key1", "42").wait(); etcd.set("/test/key1", "42").wait();
@ -455,7 +443,8 @@ TEST_CASE("wait for a value change") {
CHECK("42" == res.get().prev_value().as_string()); CHECK("42" == res.get().prev_value().as_string());
} }
TEST_CASE("wait for a directory change") { TEST_CASE("wait for a directory change")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
pplx::task<etcd::Response> res = etcd.watch("/test", true); pplx::task<etcd::Response> res = etcd.watch("/test", true);
@ -481,7 +470,8 @@ TEST_CASE("wait for a directory change") {
CHECK("45" == res2.get().value().as_string()); CHECK("45" == res2.get().value().as_string());
} }
TEST_CASE("watch changes in the past") { TEST_CASE("watch changes in the past")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code()); REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
int64_t index = etcd.set("/test/key1", "42").get().index(); int64_t index = etcd.set("/test/key1", "42").get().index();
@ -507,7 +497,8 @@ TEST_CASE("watch changes in the past") {
CHECK("45" == res.value().as_string()); CHECK("45" == res.value().as_string());
} }
TEST_CASE("watch range changes in the past") { TEST_CASE("watch range changes in the past")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code()); REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
int64_t index = etcd.set("/test/key1", "42").get().index(); int64_t index = etcd.set("/test/key1", "42").get().index();
@ -538,8 +529,7 @@ TEST_CASE("watch multiple keys and use promise") {
int64_t start_index = etcd.set("/test/key1", "value1").get().index(); int64_t start_index = etcd.set("/test/key1", "value1").get().index();
etcd.set("/test/key2", "value2").get(); etcd.set("/test/key2", "value2").get();
pplx::task<size_t> res = pplx::task<size_t> res = etcd.watch("/test", start_index, true)
etcd.watch("/test", start_index, true)
.then([](pplx::task<etcd::Response> const &resp_task) -> size_t { .then([](pplx::task<etcd::Response> const &resp_task) -> size_t {
auto const &resp = resp_task.get(); auto const &resp = resp_task.get();
return resp.events().size(); return resp.events().size();
@ -548,7 +538,8 @@ TEST_CASE("watch multiple keys and use promise") {
CHECK(2 == event_size); CHECK(2 == event_size);
} }
TEST_CASE("lease grant") { TEST_CASE("lease grant")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd::Response res = etcd.leasegrant(60).get(); etcd::Response res = etcd.leasegrant(60).get();
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
@ -559,8 +550,6 @@ TEST_CASE("lease grant") {
res = etcd.set("/test/key1", "43", leaseid).get(); res = etcd.set("/test/key1", "43", leaseid).get();
REQUIRE(0 == res.error_code()); // overwrite REQUIRE(0 == res.error_code()); // overwrite
CHECK("set" == res.action()); CHECK("set" == res.action());
res = etcd.get("/test/key1").get();
REQUIRE(0 == res.error_code()); // overwrite
CHECK(leaseid == res.value().lease()); CHECK(leaseid == res.value().lease());
// change with lease id // change with lease id
@ -569,8 +558,6 @@ TEST_CASE("lease grant") {
res = etcd.set("/test/key1", "43", leaseid).get(); res = etcd.set("/test/key1", "43", leaseid).get();
REQUIRE(0 == res.error_code()); // overwrite REQUIRE(0 == res.error_code()); // overwrite
CHECK("set" == res.action()); CHECK("set" == res.action());
res = etcd.get("/test/key1").get();
REQUIRE(0 == res.error_code()); // overwrite
CHECK(leaseid == res.value().lease()); CHECK(leaseid == res.value().lease());
// failure to attach lease id // failure to attach lease id
@ -627,7 +614,8 @@ TEST_CASE("lease grant") {
CHECK("etcdserver: requested lease not found" == res.error_message()); CHECK("etcdserver: requested lease not found" == res.error_message());
} }
TEST_CASE("lease list") { TEST_CASE("lease list")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd::Response res = etcd.leasegrant(60).get(); etcd::Response res = etcd.leasegrant(60).get();
REQUIRE(res.is_ok()); REQUIRE(res.is_ok());
@ -646,7 +634,8 @@ TEST_CASE("lease list") {
} }
} }
TEST_CASE("cleanup") { TEST_CASE("cleanup")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code()); REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
} }

View File

@ -12,10 +12,10 @@
#include "etcd/Client.hpp" #include "etcd/Client.hpp"
#include "etcd/KeepAlive.hpp" #include "etcd/KeepAlive.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
TEST_CASE("fork: set in child and get from self") { TEST_CASE("fork: set in child and get from self")
{
pid_t pid = fork(); pid_t pid = fork();
REQUIRE(pid >= 0); REQUIRE(pid >= 0);

View File

@ -10,8 +10,8 @@
#include "etcd/SyncClient.hpp" #include "etcd/SyncClient.hpp"
#include "etcd/Value.hpp" #include "etcd/Value.hpp"
static std::string etcd_uri = static std::string etcd_uri = etcdv3::detail::resolve_etcd_endpoints(
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379"); "http://127.0.0.1:2379,http://127.0.0.1:2479,http://127.0.0.1:2579");
TEST_CASE("keepalive revoke and check if alive") { TEST_CASE("keepalive revoke and check if alive") {
etcd::Client etcd(etcd_uri); etcd::Client etcd(etcd_uri);
@ -31,51 +31,5 @@ TEST_CASE("keepalive revoke and check if alive") {
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
// expect keep_alive->Check() to throw exception // expect keep_alive->Check() to throw exception
#ifndef _ETCD_NO_EXCEPTIONS
REQUIRE_THROWS(keepalive->Check()); REQUIRE_THROWS(keepalive->Check());
#endif
}
TEST_CASE("keepalive won't expire") {
etcd::Client etcd(etcd_uri);
const int64_t ttl = 3;
const std::string key = "key";
const std::string meta_str = "meta ....";
etcd::Response resp = etcd.leasegrant(ttl).get();
auto lease_id = resp.value().lease();
etcd.add(key, meta_str, lease_id);
#ifndef _ETCD_NO_EXCEPTIONS
std::function<void(std::exception_ptr)> handler =
[](std::exception_ptr eptr) {
try {
if (eptr) {
std::rethrow_exception(eptr);
}
} catch (const std::runtime_error& e) {
std::cerr << "Connection failure \"" << e.what() << "\"\n";
} catch (const std::out_of_range& e) {
std::cerr << "Lease expiry \"" << e.what() << "\"\n";
}
};
#else
std::function<void(std::exception_ptr)> handler;
#endif
etcd::KeepAlive keepalive(etcd, handler, ttl, lease_id);
std::this_thread::sleep_for(std::chrono::seconds(5));
}
TEST_CASE("keepalive auto-grant") {
etcd::Client etcd(etcd_uri);
// create a lease without pre-granted lease id
auto keepalive = std::make_shared<etcd::KeepAlive>(etcd, 10 /* ttl */);
auto lease_id = keepalive->Lease();
REQUIRE(lease_id != 0);
// sleep for a while, and cancel
std::this_thread::sleep_for(std::chrono::seconds(5));
keepalive->Cancel();
} }

View File

@ -10,10 +10,10 @@
#include "etcd/Client.hpp" #include "etcd/Client.hpp"
#include "etcd/KeepAlive.hpp" #include "etcd/KeepAlive.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
TEST_CASE("lock and unlock") { TEST_CASE("lock and unlock")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
// lock // lock
@ -29,7 +29,8 @@ TEST_CASE("lock and unlock") {
REQUIRE(0 == resp2.error_code()); REQUIRE(0 == resp2.error_code());
} }
TEST_CASE("double lock will fail") { TEST_CASE("double lock will fail")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
// lock // lock
@ -61,8 +62,7 @@ TEST_CASE("double lock will fail") {
REQUIRE(0 == resp3.error_code()); REQUIRE(0 == resp3.error_code());
// create a duration // create a duration
// using a duration longer than default lease TTL for lock (see: // using a duration longer than default lease TTL for lock (see: DEFAULT_LEASE_TTL_FOR_LOCK)
// DEFAULT_LEASE_TTL_FOR_LOCK)
std::this_thread::sleep_for(std::chrono::seconds(15)); std::this_thread::sleep_for(std::chrono::seconds(15));
// unlock the first lock // unlock the first lock
@ -84,7 +84,8 @@ TEST_CASE("double lock will fail") {
REQUIRE(0 == resp5.error_code()); REQUIRE(0 == resp5.error_code());
} }
TEST_CASE("lock could be timeout") { TEST_CASE("lock could be timeout")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
// setup the timeout // setup the timeout
@ -112,14 +113,13 @@ TEST_CASE("lock could be timeout") {
REQUIRE(0 == resp5.error_code()); REQUIRE(0 == resp5.error_code());
} }
TEST_CASE("lock using lease") { TEST_CASE("lock using lease")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
bool failed = false; bool failed = false;
#ifndef _ETCD_NO_EXCEPTIONS std::function<void (std::exception_ptr)> handler = [&failed](std::exception_ptr eptr) {
std::function<void(std::exception_ptr)> handler =
[&failed](std::exception_ptr eptr) {
try { try {
if (eptr) { if (eptr) {
std::rethrow_exception(eptr); std::rethrow_exception(eptr);
@ -129,9 +129,6 @@ TEST_CASE("lock using lease") {
failed = true; failed = true;
} }
}; };
#else
std::function<void(std::exception_ptr)> handler;
#endif
// with handler // with handler
{ {
@ -200,30 +197,23 @@ TEST_CASE("lock using lease") {
} }
} }
TEST_CASE("concurrent lock & unlock") { TEST_CASE("concurrent lock & unlock")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
std::string const lock_key = "/test/test_key"; std::string const lock_key = "/test/test_key";
constexpr size_t trials = 192; constexpr size_t trials = 192;
std::function<void(std::string const&, const size_t)> locker = std::function<void(std::string const &, const size_t)> locker = [&etcd](std::string const &key, const size_t index) {
[&etcd](std::string const& key, const size_t index) {
std::cout << "start lock for " << key << ", index is " << index
<< std::endl;
auto resp = etcd.lock(key).get(); auto resp = etcd.lock(key).get();
std::cout << "lock for " << index << " is ok, starts sleeping: ..." std::cout << "lock for " << index << " is ok, starts sleeping: ..." << resp.error_message() << std::endl << std::flush;
<< resp.error_message() << std::endl
<< std::flush;
REQUIRE(resp.is_ok()); REQUIRE(resp.is_ok());
std::srand(index); std::srand(index);
size_t time_to_sleep = 1; size_t time_to_sleep = 1;
std::this_thread::sleep_for(std::chrono::seconds(time_to_sleep)); std::this_thread::sleep_for(std::chrono::seconds(time_to_sleep));
std::cout << "lock for " << index << " resumes from sleep: ..." std::cout << "lock for " << index << " resumes from sleep: ..." << resp.error_message() << std::endl << std::flush;
<< resp.error_message() << std::endl
<< std::flush;
REQUIRE(etcd.unlock(resp.lock_key()).get().is_ok()); REQUIRE(etcd.unlock(resp.lock_key()).get().is_ok());
std::cout << "thread " << index << " been unlocked" << std::endl std::cout << "thread " << index << " been unlocked" << std::endl << std::flush;
<< std::flush;
}; };
std::vector<std::thread> locks(trials); std::vector<std::thread> locks(trials);
@ -242,15 +232,12 @@ TEST_CASE("concurrent lock & unlock with a put in between") {
constexpr size_t trials = 128; constexpr size_t trials = 128;
std::function<void(std::string const&, const size_t)> locker = std::function<void(std::string const &, const size_t)> locker = [&etcd](std::string const &key, const size_t index) {
[&etcd](std::string const& key, const size_t index) {
std::cout << "start lock for " << index << std::endl; std::cout << "start lock for " << index << std::endl;
auto resp = etcd.lock(key, true).get(); auto resp = etcd.lock(key, true).get();
std::cout << "lock for " << index << " is ok, start put and unlock: ..." std::cout << "lock for " << index << " is ok, start put and unlock: ..." << resp.error_message() << std::endl;
<< resp.error_message() << std::endl;
REQUIRE(resp.is_ok()); REQUIRE(resp.is_ok());
auto put_resp = auto put_resp = etcd.put("/test/test_put", "hello" + std::to_string(index)).get();
etcd.put("/test/test_put", "hello" + std::to_string(index)).get();
REQUIRE(put_resp.is_ok()); REQUIRE(put_resp.is_ok());
REQUIRE(etcd.unlock(resp.lock_key()).get().is_ok()); REQUIRE(etcd.unlock(resp.lock_key()).get().is_ok());
std::cout << "thread " << index << " been unlocked" << std::endl; std::cout << "thread " << index << " been unlocked" << std::endl;

View File

@ -6,14 +6,16 @@
#include "etcd/Client.hpp" #include "etcd/Client.hpp"
#include "etcd/KeepAlive.hpp" #include "etcd/KeepAlive.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
class DistributedLock { class DistributedLock {
public: public:
DistributedLock(const std::string& lock_name, uint timeout = 0); DistributedLock(const std::string &lock_name,
uint timeout = 0);
~DistributedLock() noexcept; ~DistributedLock() noexcept;
inline bool lock_acquired() { return _acquired; } inline bool lock_acquired() {
return _acquired;
}
private: private:
bool _acquired = false; bool _acquired = false;
@ -21,9 +23,11 @@ class DistributedLock {
std::unique_ptr<::etcd::Client> _etcd_client; std::unique_ptr<::etcd::Client> _etcd_client;
}; };
DistributedLock::DistributedLock(const std::string& lock_name, uint timeout) { DistributedLock::DistributedLock(const std::string &lock_name,
uint timeout) {
_etcd_client = std::unique_ptr<etcd::Client>(new etcd::Client(etcd_url)); _etcd_client = std::unique_ptr<etcd::Client>(new etcd::Client(etcd_url));
try {
if (timeout == 0) { if (timeout == 0) {
etcd::Response resp = _etcd_client->lock(lock_name).get(); etcd::Response resp = _etcd_client->lock(lock_name).get();
if (resp.is_ok()) { if (resp.is_ok()) {
@ -44,12 +48,14 @@ DistributedLock::DistributedLock(const std::string& lock_name, uint timeout) {
_acquired = true; _acquired = true;
} }
} else if (status == std::future_status::timeout) { } else if (status == std::future_status::timeout) {
std::cerr << "failed to acquire distributed because of lock timeout" std::cerr << "failed to acquire distributed because of lock timeout" << std::endl;
<< std::endl;
} else { } else {
std::cerr << "failed to acquire distributed lock" << std::endl; std::cerr << "failed to acquire distributed lock" << std::endl;
} }
} }
} catch (std::exception &e) {
std::cerr << "failed to construct: " << e.what() << std::endl;
}
} }
DistributedLock::~DistributedLock() noexcept { DistributedLock::~DistributedLock() noexcept {
@ -57,10 +63,14 @@ DistributedLock::~DistributedLock() noexcept {
return; return;
} }
try {
auto resp = _etcd_client->unlock(_lock_key).get(); auto resp = _etcd_client->unlock(_lock_key).get();
if (!resp.is_ok()) { if (!resp.is_ok()) {
std::cout << resp.error_code() << std::endl; std::cout << resp.error_code() << std::endl;
} }
} catch (std::exception &e) {
std::cerr << "failed to destruct: " << e.what() << std::endl;
}
} }
int main() { int main() {

View File

@ -8,12 +8,14 @@
#include "etcd/KeepAlive.hpp" #include "etcd/KeepAlive.hpp"
#include "etcd/Watcher.hpp" #include "etcd/Watcher.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
static std::atomic_int watcher_called; static std::atomic_int watcher_called;
void print_response(etcd::Response const& resp) { watcher_called.fetch_add(1); } void print_response(etcd::Response const & resp)
{
watcher_called.fetch_add(1);
}
/** /**
* @brief emulate the behavior of creating watcher many times: * @brief emulate the behavior of creating watcher many times:
@ -22,8 +24,7 @@ void print_response(etcd::Response const& resp) { watcher_called.fetch_add(1); }
* 2. change a value * 2. change a value
* 3. cancel the watcher * 3. cancel the watcher
*/ */
void watch_once(etcd::Client& client, std::unique_ptr<etcd::Watcher>& watcher, void watch_once(etcd::Client & client, std::unique_ptr<etcd::Watcher> &watcher, const size_t round) {
const size_t round) {
const std::string my_prefix = "/test"; const std::string my_prefix = "/test";
const std::string my_key = my_prefix + "/foo"; const std::string my_key = my_prefix + "/foo";
watcher.reset(new etcd::Watcher(client, my_prefix, print_response, true)); watcher.reset(new etcd::Watcher(client, my_prefix, print_response, true));
@ -41,20 +42,19 @@ void watch_once(etcd::Client& client, std::unique_ptr<etcd::Watcher>& watcher,
watcher->Cancel(); watcher->Cancel();
} }
TEST_CASE("watch shouldn't leak memory") { TEST_CASE("watch shouldn't leak memory")
{
watcher_called.store(0); watcher_called.store(0);
// issue some changes to see if the watcher works // issue some changes to see if the watcher works
etcd::Client client(etcd_url); etcd::Client client(etcd_url);
std::unique_ptr<etcd::Watcher> watcher; std::unique_ptr<etcd::Watcher> watcher;
for (int round = 0; for (int round = 0; round < 10 /* update this value to make it run for longer */; ++round) {
round < 10 /* update this value to make it run for longer */; ++round) {
if (round % 50 == 0) { if (round % 50 == 0) {
std::cout << "starting round " << round << std::endl; std::cout << "starting round " << round << std::endl;
} }
watch_once(client, watcher, round); watch_once(client, watcher, round);
} }
std::cout << "watcher been called for " << watcher_called.load() << " times" std::cout << "watcher been called for " << watcher_called.load() << " times" << std::endl;
<< std::endl;
} }

View File

@ -8,28 +8,27 @@
#include "etcd/SyncClient.hpp" #include "etcd/SyncClient.hpp"
#include "etcd/Watcher.hpp" #include "etcd/Watcher.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
static int watcher_called = 0; static int watcher_called = 0;
void print_response(etcd::Response const& resp) { void print_response(etcd::Response const & resp)
{
++watcher_called; ++watcher_called;
std::cout << "print response called" << std::endl; std::cout << "print response called" << std::endl;
if (resp.error_code()) { if (resp.error_code()) {
std::cout << resp.error_code() << ": " << resp.error_message() << std::endl; std::cout << resp.error_code() << ": " << resp.error_message() << std::endl;
} else { }
else {
std::cout << resp.action() << " " << resp.value().as_string() << std::endl; std::cout << resp.action() << " " << resp.value().as_string() << std::endl;
std::cout << "Previous value: " << resp.prev_value().as_string() std::cout << "Previous value: " << resp.prev_value().as_string() << std::endl;
<< std::endl;
std::cout << "Events size: " << resp.events().size() << std::endl; std::cout << "Events size: " << resp.events().size() << std::endl;
for (auto const &ev: resp.events()) { for (auto const &ev: resp.events()) {
std::cout << "Value change in events: " std::cout << "Value change in events: " << static_cast<int>(ev.event_type())
<< static_cast<int>(ev.event_type()) << ", prev kv = " << ev.prev_kv().key() << " -> " << ev.prev_kv().as_string()
<< ", prev kv = " << ev.prev_kv().key() << " -> " << ", kv = " << ev.kv().key() << " -> " << ev.kv().as_string()
<< ev.prev_kv().as_string() << ", kv = " << ev.kv().key() << std::endl;
<< " -> " << ev.kv().as_string() << std::endl;
} }
} }
} }
@ -37,7 +36,6 @@ void print_response(etcd::Response const& resp) {
void wait_for_connection(std::string endpoints) { void wait_for_connection(std::string endpoints) {
// wait until the client connects to etcd server // wait until the client connects to etcd server
while (true) { while (true) {
#ifndef _ETCD_NO_EXCEPTIONS
try { try {
etcd::Client client(endpoints); etcd::Client client(endpoints);
if (client.head().get().is_ok()) { if (client.head().get().is_ok()) {
@ -46,17 +44,12 @@ void wait_for_connection(std::string endpoints) {
} catch (...) { } catch (...) {
// pass // pass
} }
#else
etcd::Client client(endpoints);
if (client.head().get().is_ok()) {
break;
}
#endif
sleep(1); sleep(1);
} }
} }
void initialize_watcher(const std::string& endpoints, const std::string& prefix, void initialize_watcher(const std::string& endpoints,
const std::string& prefix,
std::function<void(etcd::Response)> callback, std::function<void(etcd::Response)> callback,
std::shared_ptr<etcd::Watcher>& watcher) { std::shared_ptr<etcd::Watcher>& watcher) {
// wait until the endpoints turn to be available // wait until the endpoints turn to be available
@ -71,19 +64,18 @@ void initialize_watcher(const std::string& endpoints, const std::string& prefix,
watcher.reset(new etcd::Watcher(client, prefix, callback, true)); watcher.reset(new etcd::Watcher(client, prefix, callback, true));
// Note that lambda requires `mutable`qualifier. // Note that lambda requires `mutable`qualifier.
watcher->Wait( watcher->Wait([endpoints, prefix, callback,
[endpoints, prefix, callback,
/* By reference for renewing */ &watcher](bool cancelled) mutable { /* By reference for renewing */ &watcher](bool cancelled) mutable {
if (cancelled) { if (cancelled) {
std::cout << "watcher's reconnect loop stopped as been cancelled" std::cout << "watcher's reconnect loop stopped as been cancelled" << std::endl;
<< std::endl;
return; return;
} }
initialize_watcher(endpoints, prefix, callback, watcher); initialize_watcher(endpoints, prefix, callback, watcher);
}); });
} }
TEST_CASE("watch should can be re-established") { TEST_CASE("watch should can be re-established")
{
const std::string my_prefix = "/test"; const std::string my_prefix = "/test";
// the watcher initialized in this way will auto re-connect to etcd // the watcher initialized in this way will auto re-connect to etcd
@ -92,19 +84,13 @@ TEST_CASE("watch should can be re-established") {
// issue some changes to see if the watcher works // issue some changes to see if the watcher works
for (int round = 0; round < 100000; ++round) { for (int round = 0; round < 100000; ++round) {
#ifndef _ETCD_NO_EXCEPTIONS
try { try {
etcd::Client client(etcd_url); etcd::Client client(etcd_url);
auto response = auto response = client.set(
client.set(my_prefix + "/foo", "bar-" + std::to_string(round)).get(); my_prefix + "/foo", "bar-" + std::to_string(round)).get();
} catch (...) { } catch (...) {
// pass // pass
} }
#else
etcd::Client client(etcd_url);
auto response =
client.set(my_prefix + "/foo", "bar-" + std::to_string(round)).get();
#endif
std::this_thread::sleep_for(std::chrono::seconds(2)); std::this_thread::sleep_for(std::chrono::seconds(2));
} }
@ -114,19 +100,13 @@ TEST_CASE("watch should can be re-established") {
// the watcher has been cancelled and shouldn't work anymore // the watcher has been cancelled and shouldn't work anymore
for (int round = 10; round < 20; ++round) { for (int round = 10; round < 20; ++round) {
#ifndef _ETCD_NO_EXCEPTIONS
try { try {
etcd::Client client(etcd_url); etcd::Client client(etcd_url);
auto response = auto response = client.set(
client.set(my_prefix + "/foo", "bar-" + std::to_string(round)).get(); my_prefix + "/foo", "bar-" + std::to_string(round)).get();
} catch (...) { } catch (...) {
// pass // pass
} }
#else
etcd::Client client(etcd_url);
auto response =
client.set(my_prefix + "/foo", "bar-" + std::to_string(round)).get();
#endif
std::this_thread::sleep_for(std::chrono::seconds(2)); std::this_thread::sleep_for(std::chrono::seconds(2));
} }

View File

@ -9,15 +9,16 @@ static std::string ca = "security-config/certs/ca.crt";
static std::string cert = "security-config/certs/etcd0.example.com.crt"; static std::string cert = "security-config/certs/etcd0.example.com.crt";
static std::string key = "security-config/private/etcd0.example.com.key"; static std::string key = "security-config/private/etcd0.example.com.key";
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("https://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("https://127.0.0.1:2379");
TEST_CASE("setup with auth") { TEST_CASE("setup with auth")
{
etcd::Client *etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key); etcd::Client *etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key);
etcd->rmdir("/test", true).wait(); etcd->rmdir("/test", true).wait();
} }
TEST_CASE("add a new key after authenticate") { TEST_CASE("add a new key after authenticate")
{
etcd::Client *etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key); etcd::Client *etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key);
etcd->rmdir("/test", true).wait(); etcd->rmdir("/test", true).wait();
etcd::Response resp = etcd->add("/test/key1", "42").get(); etcd::Response resp = etcd->add("/test/key1", "42").get();
@ -31,28 +32,24 @@ TEST_CASE("add a new key after authenticate") {
CHECK(0 < val.modified_index()); CHECK(0 < val.modified_index());
CHECK(1 == val.version()); CHECK(1 == val.version());
CHECK(0 < resp.index()); CHECK(0 < resp.index());
CHECK( CHECK(etcd::ERROR_KEY_ALREADY_EXISTS == etcd->add("/test/key1", "43").get().error_code()); // Key already exists
etcd::ERROR_KEY_ALREADY_EXISTS == CHECK(etcd::ERROR_KEY_ALREADY_EXISTS == etcd->add("/test/key1", "42").get().error_code()); // Key already exists
etcd->add("/test/key1", "43").get().error_code()); // Key already exists CHECK("etcd-cpp-apiv3: key already exists" == etcd->add("/test/key1", "42").get().error_message());
CHECK(
etcd::ERROR_KEY_ALREADY_EXISTS ==
etcd->add("/test/key1", "42").get().error_code()); // Key already exists
CHECK("etcd-cpp-apiv3: key already exists" ==
etcd->add("/test/key1", "42").get().error_message());
} }
TEST_CASE("read a value from etcd") { TEST_CASE("read a value from etcd")
{
etcd::Client *etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key); etcd::Client *etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key);
etcd::Response resp = etcd->get("/test/key1").get(); etcd::Response resp = etcd->get("/test/key1").get();
CHECK("get" == resp.action()); CHECK("get" == resp.action());
REQUIRE(resp.is_ok()); REQUIRE(resp.is_ok());
REQUIRE(0 == resp.error_code()); REQUIRE(0 == resp.error_code());
CHECK("42" == resp.value().as_string()); CHECK("42" == resp.value().as_string());
CHECK("" == etcd->get("/test").get().value().as_string()); // key points to a CHECK("" == etcd->get("/test").get().value().as_string()); // key points to a directory
// directory
} }
TEST_CASE("cleanup") { TEST_CASE("cleanup")
{
etcd::Client *etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key); etcd::Client *etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key);
REQUIRE(0 == etcd->rmdir("/test", true).get().error_code()); REQUIRE(0 == etcd->rmdir("/test", true).get().error_code());
} }

View File

@ -8,15 +8,16 @@
#include "etcd/Client.hpp" #include "etcd/Client.hpp"
#include "etcd/v3/Transaction.hpp" #include "etcd/v3/Transaction.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
TEST_CASE("setup") { TEST_CASE("setup")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.rmdir("/test", true).wait(); etcd.rmdir("/test", true).wait();
} }
TEST_CASE("add a new key") { TEST_CASE("add a new key")
{
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
etcd.rmdir("/test", true).wait(); etcd.rmdir("/test", true).wait();
@ -50,8 +51,11 @@ TEST_CASE("add a new key") {
etcdv3::Transaction txn; etcdv3::Transaction txn;
// setup the conditions // setup the conditions
txn.add_compare_value("/test/x1", "1"); txn.reset_key("/test/x1");
txn.add_compare_value("/test/x2", "2"); txn.init_compare("1", etcdv3::CompareResult::EQUAL, etcdv3::CompareTarget::VALUE);
txn.reset_key("/test/x2");
txn.init_compare("2", etcdv3::CompareResult::EQUAL, etcdv3::CompareTarget::VALUE);
txn.setup_put("/test/x1", "111"); txn.setup_put("/test/x1", "111");
txn.setup_delete("/test/x2"); txn.setup_delete("/test/x2");
@ -79,52 +83,8 @@ TEST_CASE("add a new key") {
} }
} }
TEST_CASE("fetch & add") { TEST_CASE("cleanup")
etcd::Client etcd(etcd_url);
etcd.rmdir("/test", true).wait();
etcd.set("/test/key", "0").wait();
auto fetch_and_add = [](etcd::Client& client,
std::string const& key) -> void {
auto value = stoi(client.get(key).get().value().as_string());
while (true) {
auto txn = etcdv3::Transaction();
txn.setup_compare_and_swap(key, std::to_string(value),
std::to_string(value + 1));
etcd::Response resp = client.txn(txn).get();
if (resp.is_ok()) {
break;
}
value = stoi(resp.value().as_string());
}
};
// run 1000 times
const size_t rounds = 100;
std::atomic_size_t counter(0);
std::vector<std::thread> threads;
for (size_t i = 0; i < 10; ++i) {
threads.emplace_back([&]() {
while (counter.fetch_add(1) < rounds) {
fetch_and_add(etcd, "/test/key");
}
});
}
for (auto& thr : threads) {
thr.join();
}
// check the value
{ {
etcd::Response resp = etcd.get("/test/key").get();
REQUIRE(0 == resp.error_code());
CHECK(resp.value().as_string() == std::to_string(rounds));
}
}
TEST_CASE("cleanup") {
etcd::Client etcd(etcd_url); etcd::Client etcd(etcd_url);
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code()); REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
} }

View File

@ -4,42 +4,39 @@
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include "etcd/SyncClient.hpp"
#include "etcd/Watcher.hpp" #include "etcd/Watcher.hpp"
#include "etcd/SyncClient.hpp"
static const std::string etcd_url = static const std::string etcd_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
static int watcher_called = 0; static int watcher_called = 0;
void printResponse(etcd::Response const& resp) { void printResponse(etcd::Response const & resp)
{
if (resp.error_code()) { if (resp.error_code()) {
std::cout << "Watcher " << resp.watch_id() << " fails with " std::cout << resp.error_code() << ": " << resp.error_message() << std::endl;
<< resp.error_code() << ": " << resp.error_message() << std::endl; }
} else { else {
std::cout << "Watcher " << resp.watch_id() << " responses with " std::cout << resp.action() << " " << resp.value().as_string() << std::endl;
<< resp.action() << " " << resp.value().as_string() << std::endl; std::cout << "Previous value: " << resp.prev_value().as_string() << std::endl;
std::cout << "Previous value: " << resp.prev_value().as_string()
<< std::endl;
std::cout << "Events size: " << resp.events().size() << std::endl; std::cout << "Events size: " << resp.events().size() << std::endl;
for (auto const &ev: resp.events()) { for (auto const &ev: resp.events()) {
if (ev.prev_kv().key().find("/leader") == 0 || if (ev.prev_kv().key().find("/leader") == 0 || ev.kv().key().find("/leader") == 0) {
ev.kv().key().find("/leader") == 0) {
return; return;
} }
std::cout << "Value change in events: " std::cout << "Value change in events: " << static_cast<int>(ev.event_type())
<< static_cast<int>(ev.event_type()) << ", prev kv = " << ev.prev_kv().key() << " -> " << ev.prev_kv().as_string()
<< ", prev kv = " << ev.prev_kv().key() << " -> " << ", kv = " << ev.kv().key() << " -> " << ev.kv().as_string()
<< ev.prev_kv().as_string() << ", kv = " << ev.kv().key() << std::endl;
<< " -> " << ev.kv().as_string() << std::endl;
} }
} }
std::cout << "print response called" << std::endl; std::cout << "print response called" << std::endl;
++watcher_called; ++watcher_called;
} }
TEST_CASE("create watcher") { TEST_CASE("create watcher")
{
etcd::SyncClient etcd(etcd_url); etcd::SyncClient etcd(etcd_url);
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
@ -56,7 +53,8 @@ TEST_CASE("create watcher") {
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
} }
TEST_CASE("watch with correct prefix") { TEST_CASE("watch with correct prefix")
{
etcd::SyncClient etcd(etcd_url); etcd::SyncClient etcd(etcd_url);
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
@ -92,7 +90,8 @@ TEST_CASE("watch with correct prefix") {
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
} }
TEST_CASE("create watcher with cancel") { TEST_CASE("create watcher with cancel")
{
etcd::SyncClient etcd(etcd_url); etcd::SyncClient etcd(etcd_url);
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
@ -114,7 +113,8 @@ TEST_CASE("create watcher with cancel") {
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
} }
TEST_CASE("create watcher on ranges with cancel") { TEST_CASE("create watcher on ranges with cancel")
{
etcd::SyncClient etcd(etcd_url); etcd::SyncClient etcd(etcd_url);
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
@ -136,96 +136,28 @@ TEST_CASE("create watcher on ranges with cancel") {
etcd.rmdir("/test", true); etcd.rmdir("/test", true);
} }
TEST_CASE("watch should exit normally") { TEST_CASE("watch should exit normally")
{
// cancel immediately after start watch. // cancel immediately after start watch.
etcd::Watcher watcher(etcd_url, "/test", printResponse, true); etcd::Watcher watcher(etcd_url, "/test", printResponse, true);
watcher.Cancel(); watcher.Cancel();
} }
TEST_CASE("watch should can be cancelled repeatedly") { TEST_CASE("watch should can be cancelled repeatedly")
{
// cancel immediately after start watch.
etcd::Watcher watcher(etcd_url, "/test", printResponse, true); etcd::Watcher watcher(etcd_url, "/test", printResponse, true);
std::vector<std::thread> threads(10); std::vector<std::thread> threads(10);
for (size_t i = 0; i < 10; ++i) { for (size_t i = 0; i < 10; ++i) {
threads[i] = std::thread([&]() { watcher.Cancel(); }); threads[i] = std::thread([&]() {
watcher.Cancel();
});
} }
for (size_t i = 0; i < 10; ++i) { for (size_t i = 0; i < 10; ++i) {
threads[i].join(); threads[i].join();
} }
} }
TEST_CASE("watch changes on the same key (#212)") {
std::string key_watch = "key watch";
etcd::SyncClient client(etcd_url);
client.put(key_watch, "inittt");
auto current_index = client.head().index();
std::cout << "Current index " << current_index << std::endl;
auto internal_cb = [&](etcd::Response resp) -> void {
if (!resp.is_ok()) {
std::cout << "Error: " << resp.error_message() << std::endl;
return;
}
for (auto const& event : resp.events()) {
std::cout << "Watch '" << event.kv().key()
<< "'. ModifedRevision : " << event.kv().modified_index()
<< "', Vision : " << event.kv().version()
<< ", value = " << event.kv().as_string() << std::endl;
}
};
auto wait_cb = [&](bool) {};
etcd::Watcher w(client, key_watch, current_index, std::move(internal_cb),
std::move(wait_cb), false);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
for (int i = 0; i < 10; ++i) {
std::string value = "watch_" + std::to_string(i);
client.put(key_watch, value);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
TEST_CASE("create two watcher") {
etcd::Watcher w1(etcd_url, "/test", printResponse, true);
etcd::Watcher w2(etcd_url, "/test", printResponse, true);
std::this_thread::sleep_for(std::chrono::seconds(5));
}
TEST_CASE("using two watcher") {
etcd::SyncClient etcd(etcd_url);
int watched1 = 0;
int watched2 = 0;
etcd::Watcher w1(
etcd, "/test/def",
[&](etcd::Response const& resp) {
std::cout << "w1 called: " << resp.events().at(0).event_type() << " on "
<< resp.events().at(0).kv().key() << std::endl;
++watched1;
},
true);
etcd::Watcher w2(
etcd, "/test",
[&](etcd::Response const& resp) {
std::cout << "w2 called: " << resp.events().at(0).event_type() << " on "
<< resp.events().at(0).kv().key() << std::endl;
++watched2;
},
true);
std::this_thread::sleep_for(std::chrono::seconds(5));
etcd.put("/test/def/xxx", "42");
etcd.put("/test/abc", "42");
etcd.rm("/test/def/xxx");
etcd.rm("/test/abc");
std::this_thread::sleep_for(std::chrono::seconds(5));
CHECK(2 == watched1);
CHECK(4 == watched2);
}
// TEST_CASE("request cancellation") // TEST_CASE("request cancellation")
// { // {
// etcd::Client etcd(etcd_url); // etcd::Client etcd(etcd_url);