Compare commits
144 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
5994b54c68 | |
|
|
ba6216385f | |
|
|
5ccaccec43 | |
|
|
b82efea7a9 | |
|
|
5aff57cce5 | |
|
|
84343ca9f0 | |
|
|
59635008c0 | |
|
|
47f0d9e032 | |
|
|
6fc1f164c0 | |
|
|
e31ac4d4ca | |
|
|
e5dc903a5d | |
|
|
0eee75b52e | |
|
|
15c022e36c | |
|
|
3d344190d7 | |
|
|
2c0d824ebe | |
|
|
068f37ba5c | |
|
|
153546f965 | |
|
|
204038c4bc | |
|
|
d57dff2f86 | |
|
|
1d5128a7e8 | |
|
|
fe9f17e61e | |
|
|
32fae70113 | |
|
|
09f665fe3e | |
|
|
fcc5807748 | |
|
|
dd2c0276aa | |
|
|
a8d5980c76 | |
|
|
0ed7bee2c8 | |
|
|
e771d2f6da | |
|
|
5e2884f362 | |
|
|
ebf9c493f1 | |
|
|
a288eb5db4 | |
|
|
24b1414118 | |
|
|
91c64e18d3 | |
|
|
af0c96f6ba | |
|
|
6e4b45f986 | |
|
|
c72e072f77 | |
|
|
16a9638e3e | |
|
|
66cf911654 | |
|
|
9fc0f37ef7 | |
|
|
5e27ac33c6 | |
|
|
f0f9c4e8c2 | |
|
|
d27f0b9e81 | |
|
|
cad42fdf07 | |
|
|
639c7e9f24 | |
|
|
c23845ee90 | |
|
|
9d3f8cec3d | |
|
|
b12fc293b9 | |
|
|
80b4d2178f | |
|
|
f774f832de | |
|
|
817153bcc9 | |
|
|
3133fbec21 | |
|
|
ceb1af1110 | |
|
|
9b5c5bd3c6 | |
|
|
81d446e55c | |
|
|
9e1e60af2e | |
|
|
5c7e155c9e | |
|
|
74ca58fdcf | |
|
|
04d5659f5f | |
|
|
fb41073a90 | |
|
|
fe80439c5b | |
|
|
10f3435c28 | |
|
|
47a5f5238f | |
|
|
30c880dd05 | |
|
|
2f15c45d4e | |
|
|
c7f17cdf0d | |
|
|
1e72df7ca3 | |
|
|
9f09066b47 | |
|
|
62884c7e38 | |
|
|
f6af474c5a | |
|
|
1fb5abac28 | |
|
|
71b5b3a1a3 | |
|
|
bbcab86eab | |
|
|
ab255467d0 | |
|
|
2e38d3c11e | |
|
|
c81f3fb211 | |
|
|
1108d986a7 | |
|
|
92efa7c9de | |
|
|
39be27e021 | |
|
|
ed7ab08ef7 | |
|
|
a789dc2f13 | |
|
|
69a1bf3b30 | |
|
|
543a901ee7 | |
|
|
89ae8a5ee4 | |
|
|
bd42e7129e | |
|
|
ca2553e30d | |
|
|
efa502831d | |
|
|
2ed6cacce9 | |
|
|
7c9b9e5699 | |
|
|
2437a08e72 | |
|
|
e5f1167c69 | |
|
|
8da8946409 | |
|
|
f21c45b362 | |
|
|
9a5267286b | |
|
|
ffb489ba4b | |
|
|
bd4ec37ff4 | |
|
|
9a2b753e9e | |
|
|
680e8ad5c6 | |
|
|
56c7189f92 | |
|
|
f70255c8b0 | |
|
|
9a2afd4e81 | |
|
|
d4975f84b3 | |
|
|
767f0b1c65 | |
|
|
23394ab9bb | |
|
|
0467c0eef3 | |
|
|
82f632de11 | |
|
|
1575c5b43a | |
|
|
bad5f6ea72 | |
|
|
5ad96e21f9 | |
|
|
99ea53cb79 | |
|
|
345380a83a | |
|
|
405383c0ba | |
|
|
6a0b6696e5 | |
|
|
8678cec1f7 | |
|
|
0d672ffa1c | |
|
|
bfb56be151 | |
|
|
1118222b3d | |
|
|
d29e05545d | |
|
|
e9db91b335 | |
|
|
b99dc2024e | |
|
|
a949dec288 | |
|
|
6eed82a766 | |
|
|
d386bb96b0 | |
|
|
cda80854eb | |
|
|
116b49b784 | |
|
|
42c2fdf58c | |
|
|
1b68f9c79d | |
|
|
0e4934523e | |
|
|
a69638409c | |
|
|
891dc7bedf | |
|
|
3d4fa143e0 | |
|
|
2338a83fd5 | |
|
|
7559258b87 | |
|
|
0dc89e4c1e | |
|
|
6704b9373f | |
|
|
efcecb7731 | |
|
|
68c5626d2c | |
|
|
b3a193c75e | |
|
|
230ebfd08b | |
|
|
46f36dac6d | |
|
|
9d794504aa | |
|
|
4dda2db5a1 | |
|
|
e80709418b | |
|
|
0b9a4f36ce | |
|
|
eab29edaf0 |
|
|
@ -0,0 +1,15 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
name: Build Deb Package
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Get time
|
||||
run: |
|
||||
date +'%Y-%m' > snapshot.txt
|
||||
|
||||
- name: Cache for cccahe
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ${{ runner.os }}-deb-ccache-${{ hashFiles('**/snapshot.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-deb-ccache-
|
||||
|
||||
- name: Install dependencies for Linux
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt update -y
|
||||
sudo apt install -y ca-certificates \
|
||||
ccache \
|
||||
cmake \
|
||||
debhelper \
|
||||
devscripts \
|
||||
libboost-all-dev \
|
||||
libcpprest-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libssl-dev \
|
||||
libz-dev \
|
||||
lsb-release \
|
||||
openssl \
|
||||
screenfetch \
|
||||
wget
|
||||
|
||||
- name: Install grpc for Ubuntu 20.04
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
run: |
|
||||
sudo apt install -y libcurl4-openssl-dev \
|
||||
libprotobuf-dev \
|
||||
libprotoc-dev \
|
||||
libgrpc-dev \
|
||||
libgrpc++-dev \
|
||||
protobuf-compiler-grpc
|
||||
|
||||
- name: Screen fetch
|
||||
run: |
|
||||
screenfetch
|
||||
|
||||
- name: Setup tmate session
|
||||
if: false
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
|
||||
- name: Prepare gpg environment
|
||||
run: |
|
||||
cat > gpg-script <<EOF
|
||||
%echo Generating a OpenPGP key
|
||||
Key-Type: DSA
|
||||
Key-Length: 1024
|
||||
Subkey-Type: ELG-E
|
||||
Subkey-Length: 1024
|
||||
Name-Real: Tao He
|
||||
Name-Email: sighingnow@gmail.com
|
||||
Expire-Date: 0
|
||||
Passphrase: etcd-passphrase
|
||||
# Do a commit here, so that we can later print "done" :-)
|
||||
%commit
|
||||
%echo done
|
||||
EOF
|
||||
|
||||
gpg --batch --gen-key gpg-script
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. -DCMAKE_CXX_STANDARD=17 \
|
||||
-DCMAKE_CXX_STANDARD_REQUIRED=TRUE \
|
||||
-DBUILD_ETCD_TESTS=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Generate PPA source tarball
|
||||
if: false
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
- name: Generate Deb package
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
cpack -G DEB
|
||||
|
||||
- name: Upload deb package
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: deb package
|
||||
path: build/*.deb
|
||||
|
||||
- name: Check ccache
|
||||
run: |
|
||||
ccache --show-stats
|
||||
|
|
@ -2,17 +2,52 @@ name: Build and Test
|
|||
|
||||
on: [push, pull_request]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
SEGFAULT_SIGNALS: all
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# disabled: macos-11.0
|
||||
# why: https://github.com/actions/virtual-environments/issues/2486
|
||||
os: [ubuntu-18.04, ubuntu-20.04, macos-10.15]
|
||||
etcd: [v3.2.26, v3.3.11, v3.4.13]
|
||||
os: [ubuntu-20.04, ubuntu-22.04, macos-11, macos-12]
|
||||
etcd: [v3.2.32, v3.3.27, v3.4.27, v3.5.9]
|
||||
exclude:
|
||||
- os: ubuntu-20.04
|
||||
etcd: v3.2.26
|
||||
- os: ubuntu-20.04
|
||||
etcd: v3.4.13
|
||||
- os: ubuntu-20.04
|
||||
etcd: v3.5.7
|
||||
|
||||
- os: ubuntu-22.04
|
||||
etcd: v3.2.26
|
||||
- os: ubuntu-22.04
|
||||
etcd: v3.3.11
|
||||
- os: ubuntu-22.04
|
||||
etcd: v3.5.7
|
||||
|
||||
- os: macos-11
|
||||
etcd: v3.2.26
|
||||
- os: macos-11
|
||||
etcd: v3.3.11
|
||||
- os: macos-11
|
||||
etcd: v3.4.13
|
||||
|
||||
- os: macos-12
|
||||
etcd: v3.2.26
|
||||
- os: macos-12
|
||||
etcd: v3.3.11
|
||||
- os: macos-12
|
||||
etcd: v3.4.13
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
|
|
@ -21,7 +56,7 @@ jobs:
|
|||
date +'%Y-%m' > snapshot.txt
|
||||
|
||||
- name: Cache for cccahe
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ${{ runner.os }}-ccache-${{ hashFiles('**/snapshot.txt') }}
|
||||
|
|
@ -47,6 +82,10 @@ jobs:
|
|||
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
|
||||
|
||||
# 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
|
||||
if: matrix.os == 'ubuntu-18.04'
|
||||
run: |
|
||||
|
|
@ -74,8 +113,8 @@ jobs:
|
|||
make -j`nproc`
|
||||
sudo make install
|
||||
|
||||
- name: Install grpc for Ubuntu 20.04
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
- name: Install grpc for Ubuntu 20.04 or later
|
||||
if: ${{ runner.os != 'macOS' && matrix.os != 'ubuntu-18.04' }}
|
||||
run: |
|
||||
sudo apt install -y libcurl4-openssl-dev \
|
||||
libprotobuf-dev \
|
||||
|
|
@ -84,6 +123,10 @@ jobs:
|
|||
libgrpc++-dev \
|
||||
protobuf-compiler-grpc
|
||||
|
||||
# install libsegfault.so
|
||||
sudo apt-get install libc6 || true
|
||||
sudo apt-get install glibc-tools || true
|
||||
|
||||
- name: Install dependencies for Mac
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
|
|
@ -127,26 +170,68 @@ jobs:
|
|||
make -j`nproc`
|
||||
sudo make install
|
||||
|
||||
- name: Build
|
||||
- name: Setup tmate session
|
||||
if: false
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib/x86_64-linux-gnu
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Debug \
|
||||
cmake .. -DCMAKE_CXX_STANDARD=17 \
|
||||
-DCMAKE_CXX_STANDARD_REQUIRED=TRUE \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DBUILD_ETCD_TESTS=ON \
|
||||
-DCMAKE_C_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`
|
||||
sudo make install
|
||||
|
||||
- name: Setup tmate session
|
||||
if: false
|
||||
uses: mxschmitt/action-tmate@v2
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||
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"
|
||||
|
|
@ -157,17 +242,99 @@ jobs:
|
|||
sleep 5
|
||||
|
||||
# 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 ........................."
|
||||
./build/bin/EtcdSyncTest
|
||||
|
||||
echo "Run the etcd test ........................."
|
||||
./build/bin/EtcdTest
|
||||
./build/bin/LockTest
|
||||
|
||||
echo "Run the etcd campaign test ........................."
|
||||
./build/bin/CampaignTest
|
||||
|
||||
echo "Run the etcd memory leak test ........................."
|
||||
./build/bin/MemLeakTest
|
||||
|
||||
echo "Run the etcd watcher test ........................."
|
||||
./build/bin/WatcherTest
|
||||
|
||||
killall -TERM etcd
|
||||
echo "Run the etcd memory leak in watcher test ........................."
|
||||
./build/bin/MemLeakWatcherTest
|
||||
|
||||
echo "Run the etcd keepalive test ........................."
|
||||
./build/bin/KeepAliveTest
|
||||
|
||||
echo "Run the etcd transaction test ........................."
|
||||
./build/bin/TransactionTest
|
||||
|
||||
echo "Run the etcd election test ........................."
|
||||
./build/bin/ElectionTest
|
||||
|
||||
echo "Run the etcd with fork test ........................."
|
||||
./build/bin/ForkTest
|
||||
|
||||
killall -TERM etcd || true
|
||||
sleep 5
|
||||
|
||||
- name: Lock Test
|
||||
if: false
|
||||
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
|
||||
|
||||
echo "Run the etcd lock test ........................."
|
||||
./build/bin/LockTest
|
||||
|
||||
killall -TERM etcd || true
|
||||
sleep 5
|
||||
|
||||
- name: Lock Tests with Debug
|
||||
if: true
|
||||
uses: sighingnow/action-tmate@master
|
||||
with:
|
||||
script-to-run: |
|
||||
killall -TERM etcd || true
|
||||
sleep 5
|
||||
|
||||
# enable coredump for debugging
|
||||
ulimit -c unlimited
|
||||
|
||||
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
|
||||
|
||||
echo "Run the etcd lock test ........................."
|
||||
./build/bin/LockTest
|
||||
|
||||
killall -TERM etcd || true
|
||||
sleep 5
|
||||
|
||||
- name: Authentication Test
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||
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"
|
||||
|
|
@ -183,7 +350,7 @@ jobs:
|
|||
printf 'root\nroot\n' | /usr/local/bin/etcdctl user add root || true
|
||||
fi
|
||||
# for etcd v3.4
|
||||
if [[ "${{ matrix.etcd }}" == v3.4* ]];
|
||||
if [[ "${{ matrix.etcd }}" == v3.4* ]] || [[ "${{ matrix.etcd }}" == v3.5* ]];
|
||||
then
|
||||
/usr/local/bin/etcdctl user add root --new-user-password="root" || true
|
||||
fi
|
||||
|
|
@ -198,17 +365,20 @@ jobs:
|
|||
/usr/local/bin/etcdctl --user="root:root" auth disable || true
|
||||
fi
|
||||
# for etcd v3.4
|
||||
if [[ "${{ matrix.etcd }}" == v3.4* ]];
|
||||
if [[ "${{ matrix.etcd }}" == v3.4* ]] || [[ "${{ matrix.etcd }}" == v3.5* ]];
|
||||
then
|
||||
/usr/local/bin/etcdctl auth disable --user="root" --password="root" || true
|
||||
fi
|
||||
|
||||
killall -TERM etcd
|
||||
killall -TERM etcd || true
|
||||
sleep 5
|
||||
|
||||
- name: Transport Security and Authentication Test
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||
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"
|
||||
|
|
@ -233,6 +403,26 @@ jobs:
|
|||
killall -TERM etcd
|
||||
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
|
||||
run: |
|
||||
ccache --show-stats
|
||||
|
|
|
|||
|
|
@ -0,0 +1,142 @@
|
|||
name: Build & Test on CentOS Latest
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.repository }}-${{ github.event.number || github.head_ref || github.sha }}-${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
container:
|
||||
image: centos:latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
etcd: [v3.4.13]
|
||||
steps:
|
||||
- name: Install dependencies for Linux
|
||||
run: |
|
||||
# switch to centos stream
|
||||
dnf -y --disablerepo '*' --enablerepo=extras swap centos-linux-repos centos-stream-repos
|
||||
dnf -y update
|
||||
|
||||
# install required dependencies
|
||||
yum -y group install "Development Tools"
|
||||
yum -y install boost-devel \
|
||||
cmake \
|
||||
git \
|
||||
openssl-devel \
|
||||
wget
|
||||
|
||||
# install screen fetch
|
||||
wget -O screenfetch-dev https://git.io/vaHfR
|
||||
chmod +x screenfetch-dev
|
||||
mv ./screenfetch-dev /usr/bin/screenfetch
|
||||
|
||||
# the checkout action require new version of git
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Cache for cccahe
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /home/runner/.ccache
|
||||
key: ${{ runner.os }}-centos-ccache-${{ hashFiles('/CMakeLists.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-centos-ccache-
|
||||
|
||||
- name: Install grpc v1.27.x for CentOS latest
|
||||
run: |
|
||||
# We simply keep the same version with Ubuntu 18.04
|
||||
#
|
||||
git clone https://github.com/grpc/grpc.git --depth 1 --branch v1.27.x
|
||||
cd grpc/
|
||||
git submodule update --init
|
||||
mkdir cmake-build
|
||||
cd cmake-build/
|
||||
cmake .. -DBUILD_SHARED_LIBS=ON \
|
||||
-DgRPC_INSTALL=ON \
|
||||
-DgRPC_BUILD_TESTS=OFF \
|
||||
-DgRPC_BUILD_CSHARP_EXT=OFF \
|
||||
-DgRPC_BUILD_GRPC_CSHARP_PLUGIN=OFF \
|
||||
-DgRPC_BUILD_GRPC_NODE_PLUGIN=OFF \
|
||||
-DgRPC_BUILD_GRPC_OBJECTIVE_C_PLUGIN=OFF \
|
||||
-DgRPC_BUILD_GRPC_PHP_PLUGIN=OFF \
|
||||
-DgRPC_BUILD_GRPC_PYTHON_PLUGIN=OFF \
|
||||
-DgRPC_BUILD_GRPC_RUBY_PLUGIN=OFF \
|
||||
-DgRPC_BACKWARDS_COMPATIBILITY_MODE=ON \
|
||||
-DgRPC_ZLIB_PROVIDER=package \
|
||||
-DgRPC_SSL_PROVIDER=package
|
||||
make -j`nproc`
|
||||
make install
|
||||
|
||||
- name: Screen fetch
|
||||
run: |
|
||||
screenfetch
|
||||
|
||||
- name: Install etcd for Linux
|
||||
run: |
|
||||
# install etcd
|
||||
wget https://github.com/etcd-io/etcd/releases/download/${{ matrix.etcd }}/etcd-${{ matrix.etcd }}-linux-amd64.tar.gz
|
||||
tar zxvf etcd-${{ matrix.etcd }}-linux-amd64.tar.gz
|
||||
mv etcd-${{ matrix.etcd }}-linux-amd64/etcd /usr/local/bin/
|
||||
mv etcd-${{ matrix.etcd }}-linux-amd64/etcdctl /usr/local/bin/
|
||||
|
||||
- name: Install cpprestsdk
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib64
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
git clone https://github.com/microsoft/cpprestsdk.git --depth=1
|
||||
mkdir -p cpprestsdk/build
|
||||
cd cpprestsdk/build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Debug \
|
||||
-DBUILD_TESTS=OFF \
|
||||
-DBUILD_SAMPLES=OFF \
|
||||
-DCPPREST_EXCLUDE_WEBSOCKETS=ON
|
||||
make -j`nproc`
|
||||
make install
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib64
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. -DCMAKE_CXX_STANDARD=17 \
|
||||
-DCMAKE_CXX_STANDARD_REQUIRED=TRUE \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DBUILD_ETCD_TESTS=ON
|
||||
make -j`nproc`
|
||||
make install
|
||||
|
||||
- name: Setup tmate session
|
||||
if: false
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib64
|
||||
|
||||
# use etcd v3 api
|
||||
export ETCDCTL_API="3"
|
||||
|
||||
rm -rf default.etcd
|
||||
/usr/local/bin/etcd &
|
||||
|
||||
sleep 5
|
||||
|
||||
# tests without auth
|
||||
./build/bin/EtcdSyncTest
|
||||
./build/bin/EtcdTest
|
||||
./build/bin/LockTest
|
||||
./build/bin/MemLeakTest
|
||||
./build/bin/WatcherTest
|
||||
./build/bin/ElectionTest
|
||||
|
||||
killall -TERM etcd
|
||||
sleep 5
|
||||
|
|
@ -3,3 +3,6 @@ compile_commands.json
|
|||
proto/**/*.pb.cc
|
||||
proto/**/*.pb.h
|
||||
default.etcd/
|
||||
|
||||
# vscode-clangd
|
||||
.cache/
|
||||
|
|
|
|||
261
CMakeLists.txt
261
CMakeLists.txt
|
|
@ -1,15 +1,16 @@
|
|||
cmake_minimum_required (VERSION 3.1.3 FATAL_ERROR)
|
||||
cmake_minimum_required (VERSION 3.3 FATAL_ERROR)
|
||||
project (etcd-cpp-api)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
set(etcd-cpp-api_VERSION_MAJOR 0)
|
||||
set(etcd-cpp-api_VERSION_MINOR 2)
|
||||
set(etcd-cpp-api_VERSION_PATCH 1)
|
||||
set(etcd-cpp-api_VERSION_MINOR 15)
|
||||
set(etcd-cpp-api_VERSION_PATCH 5)
|
||||
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)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CheckLibraryExists)
|
||||
include(GNUInstallDirs)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
|
@ -25,22 +26,91 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
|||
)
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
|
||||
option(BUILD_ETCD_TESTS "Build test cases" OFF)
|
||||
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_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(CMAKE_POSITION_INDEPENDENT_CODE "Build etcd-cpp-apiv3 with -fPIC" ON)
|
||||
option(ETCD_W_STRICT "Build etcd-cpp-apiv3 with -Werror" ON)
|
||||
option(ETCD_CMAKE_CXX_STANDARD "Build etcd-cpp-apiv3 with specified C++ standard, e.g., 11, 14, 17, 20" "")
|
||||
|
||||
if(NOT "${ETCD_CMAKE_CXX_STANDARD}")
|
||||
if(NOT "${CMAKE_CXX_STANDARD}")
|
||||
set(ETCD_CMAKE_CXX_STANDARD 11)
|
||||
else()
|
||||
set(ETCD_CMAKE_CXX_STANDARD ${CMAKE_CXX_STANDARD})
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "Building etcd-cpp-apiv3 with C++${ETCD_CMAKE_CXX_STANDARD}")
|
||||
|
||||
# reference: https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling#always-full-rpath
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib:${CMAKE_INSTALL_PREFIX}/lib64")
|
||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib:${CMAKE_INSTALL_PREFIX}/lib64:${CMAKE_INSTALL_PREFIX}/lib/x86_64-linux-gnu")
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
|
||||
find_package(Boost REQUIRED COMPONENTS system thread random)
|
||||
if (APPLE)
|
||||
if(MSVC)
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
set(CMAKE_GNUtoMS ON)
|
||||
endif()
|
||||
|
||||
if(NOT (CMAKE_CXX_COMPILER_LAUNCHER MATCHES "ccache") AND NOT (CMAKE_C_COMPILER_LAUNCHER MATCHES "ccache"))
|
||||
find_program(ccache_EXECUTABLE ccache)
|
||||
if(ccache_EXECUTABLE)
|
||||
set(CMAKE_C_COMPILER_LAUNCHER ${ccache_EXECUTABLE})
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER ${ccache_EXECUTABLE})
|
||||
if (NOT TARGET ccache-stats)
|
||||
add_custom_target(ccache-stats
|
||||
COMMAND ${ccache_EXECUTABLE} --show-stats
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
if (NOT TARGET ccache-stats)
|
||||
add_custom_target(ccache-stats
|
||||
COMMAND echo "ccache not found."
|
||||
)
|
||||
endif()
|
||||
endif(ccache_EXECUTABLE)
|
||||
endif()
|
||||
|
||||
macro(use_cxx target)
|
||||
if(CMAKE_VERSION VERSION_LESS "3.1")
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_compile_options(${target} PRIVATE "-std=gnu++${ETCD_CMAKE_CXX_STANDARD}")
|
||||
else()
|
||||
target_compile_options(${target} PRIVATE "-std=c++${ETCD_CMAKE_CXX_STANDARD}")
|
||||
endif()
|
||||
elseif(CMAKE_VERSION VERSION_LESS "3.8")
|
||||
set_target_properties(${target} PROPERTIES
|
||||
CXX_STANDARD ${ETCD_CMAKE_CXX_STANDARD}
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
)
|
||||
else()
|
||||
target_compile_features(${target} PUBLIC cxx_std_${ETCD_CMAKE_CXX_STANDARD})
|
||||
endif()
|
||||
endmacro(use_cxx)
|
||||
|
||||
macro(set_exceptions target)
|
||||
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 we're on OS X check for Homebrew's copy of OpenSSL instead of Apple's
|
||||
if (NOT OpenSSL_DIR)
|
||||
if(NOT OpenSSL_DIR)
|
||||
find_program(HOMEBREW brew)
|
||||
if (HOMEBREW STREQUAL "HOMEBREW-NOTFOUND")
|
||||
if(HOMEBREW STREQUAL "HOMEBREW-NOTFOUND")
|
||||
message(WARNING "Homebrew not found: not using Homebrew's OpenSSL")
|
||||
if (NOT OPENSSL_ROOT_DIR)
|
||||
if(NOT OPENSSL_ROOT_DIR)
|
||||
message(WARNING "Use -DOPENSSL_ROOT_DIR for non-Apple OpenSSL")
|
||||
endif()
|
||||
else()
|
||||
|
|
@ -52,60 +122,138 @@ if (APPLE)
|
|||
endif()
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(Protobuf REQUIRED)
|
||||
find_package(cpprestsdk QUIET)
|
||||
if(cpprestsdk_FOUND)
|
||||
set(CPPREST_INCLUDE_DIR)
|
||||
set(CPPREST_LIB cpprestsdk::cpprest)
|
||||
else()
|
||||
find_library(CPPREST_LIB NAMES cpprest)
|
||||
find_path(CPPREST_INCLUDE_DIR NAMES cpprest/http_client.h)
|
||||
find_package(Protobuf CONFIG QUIET)
|
||||
if (NOT Protobuf_FOUND)
|
||||
find_package(Protobuf REQUIRED)
|
||||
endif()
|
||||
if(Protobuf_PROTOC_EXECUTABLE)
|
||||
if(NOT TARGET protobuf::protoc)
|
||||
add_executable(protobuf::protoc IMPORTED)
|
||||
if(EXISTS "${Protobuf_PROTOC_EXECUTABLE}")
|
||||
set_target_properties(protobuf::protoc PROPERTIES
|
||||
IMPORTED_LOCATION "${Protobuf_PROTOC_EXECUTABLE}")
|
||||
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)
|
||||
if(gRPC_FOUND)
|
||||
if(gRPC_FOUND AND TARGET gRPC::grpc)
|
||||
# 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++)
|
||||
get_target_property(GRPC_CPP_PLUGIN gRPC::grpc_cpp_plugin LOCATION)
|
||||
get_target_property(GRPC_INCLUDE_DIR gRPC::grpc INTERFACE_INCLUDE_DIRECTORIES)
|
||||
else()
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindGRPC.cmake)
|
||||
set(GRPC_LIBRARIES ${GPR_LIBRARY} ${GRPC_LIBRARY} ${GRPC_GRPC++_LIBRARY})
|
||||
endif()
|
||||
|
||||
message(STATUS "Found GRPC: ${GRPC_LIBRARIES}, ${GRPC_CPP_PLUGIN} (found version: \"${gRPC_VERSION}\")")
|
||||
# avoid use the apt-get installed libgrpc-dev (version v1.13) on Ubuntu 18.04
|
||||
if(gRPC_FOUND AND gRPC_VERSION VERSION_LESS "1.14")
|
||||
message(FATAL_ERROR "gRPC '${gRPC_VERSION}' is not supported, please install a newer gRPC library "
|
||||
"by following the example below"
|
||||
"\n"
|
||||
" https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/blob/master/.github/workflows/build-test.yml#L50-L75")
|
||||
endif()
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateProtobufGRPC.cmake)
|
||||
|
||||
if(gRPC_VERSION VERSION_LESS "1.21" OR gRPC_VERSION VERSION_GREATER "1.31")
|
||||
add_definitions(-DWITH_GRPC_CHANNEL_CLASS)
|
||||
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`.
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/GenerateProtobuf.cmake)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/proto)
|
||||
|
||||
include_directories(SYSTEM ${Boost_INCLUDE_DIR}
|
||||
${CPPREST_INCLUDE_DIR}
|
||||
# test cases requires the async runtime
|
||||
if(BUILD_ETCD_TESTS)
|
||||
message(STATUS "Building etcd-cpp-apiv3 with tests requires the async runtime, "
|
||||
"setting BUILD_ETCD_CORE_ONLY to ON ...")
|
||||
set(BUILD_ETCD_CORE_ONLY OFF)
|
||||
endif()
|
||||
|
||||
if (NOT BUILD_ETCD_CORE_ONLY)
|
||||
find_package(cpprestsdk QUIET)
|
||||
if(cpprestsdk_FOUND)
|
||||
set(CPPREST_INCLUDE_DIR)
|
||||
set(CPPREST_LIB cpprestsdk::cpprest)
|
||||
else()
|
||||
find_library(CPPREST_LIB NAMES cpprest)
|
||||
find_path(CPPREST_INCLUDE_DIR NAMES cpprest/http_client.h)
|
||||
endif()
|
||||
else()
|
||||
set(CPPREST_INCLUDE_DIR)
|
||||
set(CPPREST_LIB)
|
||||
endif()
|
||||
|
||||
include_directories(SYSTEM ${CPPREST_INCLUDE_DIR}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
${GRPC_INCLUDE_DIR}
|
||||
${OPENSSL_INCLUDE_DIR})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Werror -Wno-string-compare -std=c++11")
|
||||
if(ETCD_W_STRICT)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wpedantic -Wno-string-compare")
|
||||
endif()
|
||||
|
||||
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)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++17-extensions")
|
||||
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)
|
||||
|
||||
add_subdirectory(src)
|
||||
if(BUILD_ETCD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tst)
|
||||
endif()
|
||||
|
||||
if (BUILD_ETCD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tst)
|
||||
endif ()
|
||||
|
||||
install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/etcd/Client.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/etcd/KeepAlive.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/etcd/SyncClient.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/etcd/Response.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/etcd/Value.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/etcd/Watcher.hpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/proto/gen/proto/kv.pb.h
|
||||
DESTINATION include/etcd)
|
||||
install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/etcd/v3/Transaction.hpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/proto/gen/proto/txn.pb.h
|
||||
DESTINATION include/etcd/v3)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/etcd/KeepAlive.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/etcd/SyncClient.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/etcd/Response.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/etcd/Value.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/etcd/Watcher.hpp
|
||||
DESTINATION include/etcd)
|
||||
if(NOT BUILD_ETCD_CORE_ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/etcd/Client.hpp
|
||||
DESTINATION include/etcd)
|
||||
endif()
|
||||
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/Member.hpp
|
||||
DESTINATION include/etcd/v3)
|
||||
|
||||
configure_file(etcd-cpp-api-config.in.cmake
|
||||
"${PROJECT_BINARY_DIR}/etcd-cpp-api-config.cmake" @ONLY
|
||||
|
|
@ -123,6 +271,17 @@ install(EXPORT etcd-targets
|
|||
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
|
||||
|
||||
set(CPACK_PACKAGE_NAME ${CMAKE_PROJECT_NAME})
|
||||
|
|
@ -168,7 +327,7 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "libcpprest-dev,
|
|||
libgrpc++-dev,
|
||||
libssl-dev")
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_UPSTREAM_COPYRIGHT_YEAR 2016-2021)
|
||||
set(CPACK_DEBIAN_PACKAGE_UPSTREAM_COPYRIGHT_YEAR 2016-2023)
|
||||
set(CPACK_DEBIAN_PACKAGE_LICENSE bsd)
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Tao He <sighingnow@gmail.com>")
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/")
|
||||
|
|
@ -177,16 +336,22 @@ set(CPACK_DEBIAN_PACKAGE_INSTALL "/usr/lib/lib*.so*"
|
|||
"/usr/lib/cmake/etcd-cpp-api/*.cmake"
|
||||
"/usr/include/etcd/*.h"
|
||||
"/usr/include/etcd/*.hpp"
|
||||
"/usr/include/etcd/v3/*.h"
|
||||
"/usr/include/etcd/v3/*.hpp"
|
||||
"/usr/include/etcd/v3/action_constants.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 0)
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_DISTRIBUTION "focal")
|
||||
set(DPUT_HOST "ppa:graphscope/etcd-cpp-api")
|
||||
set(DPUT_SNAPSHOT_HOST "ppa:graphscope/etcd-cpp-api")
|
||||
set(DPUT_HOST "ppa:etcd-cpp-apiv3/etcd-cpp-api")
|
||||
set(DPUT_SNAPSHOT_HOST "ppa:etcd-cpp-apiv3/etcd-cpp-api")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
include(UploadPPA)
|
||||
find_program(DEBUILD_EXECUTABLE debuild)
|
||||
find_program(DPUT_EXECUTABLE dput)
|
||||
|
||||
if(DEBUILD_EXECUTABLE AND DPUT_EXECUTABLE)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
include(UploadPPA)
|
||||
endif()
|
||||
|
|
|
|||
705
README.md
705
README.md
|
|
@ -9,13 +9,16 @@ i.e., `ETCDCTL_API=3`.
|
|||
### Supported OS environments
|
||||
|
||||
+ **Linux**
|
||||
- Ubuntu 18.04, requires upgrade grpc libraries (has been tested with 1.27.x).
|
||||
- Ubuntu 18.04, requires upgrade gRPC libraries (tested with 1.27.x).
|
||||
- Ubuntu 20.04
|
||||
- CentOS 8 (tested with 1.27.x)
|
||||
|
||||
+ **MacOS**
|
||||
- MacOS 10.15
|
||||
- MacOS 11.0
|
||||
|
||||
+ **Windows**
|
||||
- Windows 10, with [vcpkg](https://github.com/microsoft/vcpkg)
|
||||
- Windows 10, with [vcpkg](https://github.com/microsoft/vcpkg/tree/master/ports/etcd-cpp-apiv3)
|
||||
|
||||
### Supported etcd versions:
|
||||
|
||||
|
|
@ -25,18 +28,17 @@ i.e., `ETCDCTL_API=3`.
|
|||
|
||||
## Requirements
|
||||
|
||||
1. boost
|
||||
1. boost and openssl (**Note that boost is only required if you need the asynchronous runtime**)
|
||||
|
||||
+ On Ubuntu, above requirement could be installed as:
|
||||
|
||||
apt-get install libboost-all-dev
|
||||
apt-get install libboost-all-dev libssl-dev
|
||||
|
||||
+ On MacOS, above requirement could be installed as:
|
||||
|
||||
brew install boost
|
||||
brew install boost openssl
|
||||
|
||||
2. protobuf
|
||||
3. gRPC
|
||||
2. protobuf & gRPC
|
||||
|
||||
+ On Ubuntu, above requirements related to protobuf and gRPC can be installed as:
|
||||
|
||||
|
|
@ -47,18 +49,28 @@ i.e., `ETCDCTL_API=3`.
|
|||
|
||||
+ On MacOS, above requirements related to protobuf and gRPC can be installed as:
|
||||
|
||||
|
||||
brew install grpc protobuf
|
||||
|
||||
+ When building grpc from source code (e.g., on [Ubuntu 18.04](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/blob/master/.github/workflows/build-test.yml#L73)
|
||||
and on [CentOS](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/blob/master/.github/workflows/centos-latest.yml#L44-L67)),
|
||||
if the system-installed openssl is preferred, you need to add `-DgRPC_SSL_PROVIDER=package`
|
||||
when building gRPC with CMake.
|
||||
|
||||
4. [cpprestsdk](https://github.com/microsoft/cpprestsdk), the latest version of master branch
|
||||
3. [cpprestsdk](https://github.com/microsoft/cpprestsdk), the latest version of master branch
|
||||
on github should work, you can build and install this dependency using cmake with:
|
||||
|
||||
git clone https://github.com/microsoft/cpprestsdk.git
|
||||
cd cpprestsdk
|
||||
mkdir build && cd build
|
||||
cmake .. -DCPPREST_EXCLUDE_WEBSOCKETS=ON
|
||||
make -j && make install
|
||||
make -j$(nproc) && make install
|
||||
|
||||
## API documentation
|
||||
|
||||
The _etcd-cpp-apiv3_ doesn't maintain a website for documentation, for detail usage of the
|
||||
etcd APIs, please refer to [the "etcd operations" section](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3#etcd-operations)
|
||||
in README, and see the detail C++ interfaces in [Client.hpp](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/blob/master/etcd/Client.hpp)
|
||||
and [SyncClient.hpp](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/blob/master/etcd/SyncClient.hpp).
|
||||
|
||||
## Build and install
|
||||
|
||||
|
|
@ -69,47 +81,103 @@ dependencies have been successfully installed:
|
|||
cd etcd-cpp-apiv3
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make -j && 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
|
||||
|
||||
The _etcd-cpp-apiv3_ should work well with etcd > 3.0. Feel free to issue an issue to us on
|
||||
Github when you encounter problems when working with etcd 3.x releases.
|
||||
|
||||
## Sync vs. Async runtime
|
||||
|
||||
There are various discussion about whether to support a user-transparent multi-thread executor in
|
||||
the background, or, leaving the burden of thread management to the user (e.g., see
|
||||
[issue#100](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/issues/100) and
|
||||
[issue#207](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/issues/207) for more discussion about
|
||||
the implementation of underlying thread model).
|
||||
|
||||
The _etcd-cpp-apiv3_ library supports both synchronous and asynchronous runtime, controlled by the
|
||||
cmake option `BUILD_ETCD_CORE_ONLY=ON/OFF` (defaults to `OFF`).
|
||||
|
||||
- When it is set as `OFF`: the library artifact name will be `libetcd-cpp-api.{a,so,dylib,lib,dll}` and a
|
||||
cmake target `etcd-cpp-api` is exported and pointed to it. The library provides both synchronous runtime
|
||||
(`etcd/SyncClient.hpp`) and asynchronous runtime (`etcd/Client.hpp`), and the `cpprestsdk` is a
|
||||
required dependency.
|
||||
- When it is set as `ON`: the library artifact name will be `libetcd-cpp-api-core.{a,so,dylib,lib,dll}`
|
||||
and a cmake target `etcd-cpp-api` is exported and pointed to it. The library provides only the
|
||||
synchronous runtime (`etcd/SyncClient.hpp`), and the `cpprestsdk` won't be required.
|
||||
|
||||
We encourage the users to use the asynchronous runtime by default, as it provides more flexibility
|
||||
and convenient APIs and less possibilities for errors that block the main thread. Note that the
|
||||
asynchronous runtime requires `cpprestsdk` and will setup a thread pool in the background.
|
||||
|
||||
**Warning: users cannot link both `libetcd-cpp-api.{a,so,dylib,lib,dll}` and `libetcd-cpp-api-core.{a,so,dylib,lib,dll}`
|
||||
to same program.**
|
||||
|
||||
## Usage
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd::Response response = etcd.get("/test/key1").get();
|
||||
std::cout << response.value().as_string();
|
||||
```
|
||||
|
||||
Methods of the etcd client object are sending the corresponding gRPC requests and are returning
|
||||
immediatelly with a ```pplx::task``` object. The task object is responsible for handling the
|
||||
immediately with a `pplx::task` object. The task object is responsible for handling the
|
||||
reception of the HTTP response as well as parsing the gRPC of the response. All of this is done
|
||||
asynchronously in a background thread so you can continue your code to do other operations while the
|
||||
current etcd operation is executing in the background or you can wait for the response with the
|
||||
```wait()``` or ```get()``` methods if a synchron behaviour is enough for your needs. These methods
|
||||
are blocking until the HTTP response arrives or some error situation happens. ```get()``` method
|
||||
also returns the ```etcd::Response``` object.
|
||||
`wait()` or `get()` methods if a synchronous behavior is enough for your needs. These methods
|
||||
are blocking until the HTTP response arrives or some error situation happens. `get()` method
|
||||
also returns the `etcd::Response` object.
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
pplx::task<etcd::Response> response_task = etcd.get("/test/key1");
|
||||
// ... do something else
|
||||
etcd::Response response = response_task.get();
|
||||
std::cout << response.value().as_string();
|
||||
```
|
||||
|
||||
The pplx library allows to do even more. You can attach continuation ojects to the task if you do
|
||||
The pplx library allows to do even more. You can attach continuation objects to the task if you do
|
||||
not care about when the response is coming you only want to specify what to do then. This
|
||||
can be achieved by calling the ```then``` method of the task, giving a funcion object parameter to
|
||||
can be achieved by calling the `then` method of the task, giving a function object parameter to
|
||||
it that can be used as a callback when the response is arrived and processed. The parameter of this
|
||||
callback should be either a ```etcd::Response``` or a ```pplx::task<etcd:Response>```. You should
|
||||
probably use a C++ lambda funcion here as a callback.
|
||||
callback should be either a `etcd::Response` or a `pplx::task<etcd:Response>`. You should
|
||||
probably use a C++ lambda function here as a callback.
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd.get("/test/key1").then([](etcd::Response response)
|
||||
{
|
||||
std::cout << response.value().as_string();
|
||||
|
|
@ -118,22 +186,22 @@ probably use a C++ lambda funcion here as a callback.
|
|||
// ... your code can continue here without any delay
|
||||
```
|
||||
|
||||
Your lambda function should have a parameter of type ```etcd::Response``` or
|
||||
```pplx::task<etcd::Response>```. In the latter case you can get the actual ```etcd::Response```
|
||||
object with the ```get()``` function of the task. Calling get can raise exeptions so this is the way
|
||||
how you can catch the errors generated by the REST interface. The ```get()``` call will not block in
|
||||
this case since the respose has been already arrived (we are inside the callback).
|
||||
Your lambda function should have a parameter of type `etcd::Response` or
|
||||
`pplx::task<etcd::Response>`. In the latter case you can get the actual `etcd::Response`
|
||||
object with the `get()` function of the task. Calling get can raise exceptions so this is the way
|
||||
how you can catch the errors generated by the REST interface. The `get()` call will not block in
|
||||
this case since the response has been already arrived (we are inside the callback).
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd.get("/test/key1").then([](pplx::task<etcd::Response> response_task)
|
||||
{
|
||||
try
|
||||
{
|
||||
etcd::Response response = response.task.get(); // can throw
|
||||
etcd::Response response = response_task.get(); // can throw
|
||||
std::cout << response.value().as_string();
|
||||
}
|
||||
catch (std::ecxeption const & ex)
|
||||
catch (std::exception const & ex)
|
||||
{
|
||||
std::cerr << ex.what();
|
||||
}
|
||||
|
|
@ -142,8 +210,6 @@ this case since the respose has been already arrived (we are inside the callback
|
|||
// ... your code can continue here without any delay
|
||||
```
|
||||
|
||||
## etcd operations
|
||||
|
||||
### Multiple endpoints
|
||||
|
||||
Connecting to multiple endpoints is supported:
|
||||
|
|
@ -156,6 +222,14 @@ Connecting to multiple endpoints is supported:
|
|||
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
|
||||
be used by default.
|
||||
|
||||
|
|
@ -171,13 +245,20 @@ the authentication properly.
|
|||
etcd::Client etcd("http://127.0.0.1:2379", "root", "root");
|
||||
```
|
||||
|
||||
Or the etcd client can be constructed explictly:
|
||||
Or the etcd client can be constructed explicitly:
|
||||
|
||||
```c++
|
||||
etcd::Client *etcd = etcd::Client::WithUser(
|
||||
"http://127.0.0.1:2379", "root", "root");
|
||||
```
|
||||
|
||||
The default authentication token will be expired every 5 minutes (300 seconds), which is controlled by
|
||||
the `--auth-token-ttl` flag of etcd. When constructing a etcd client, a customized TTL value is allow:
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:2379", "root", "root", 300);
|
||||
```
|
||||
|
||||
Enabling v3 authentication requires a bit more work for older versions etcd (etcd 3.2.x and etcd 3.3.x).
|
||||
First you need to set the `ETCDCTL_API=3`, then
|
||||
|
||||
|
|
@ -203,8 +284,8 @@ printf 'root\nroot\n' | /usr/local/bin/etcdctl user add root
|
|||
|
||||
Etcd [transport security](https://etcd.io/docs/v3.4.0/op-guide/security/) and certificate based
|
||||
authentication have been supported as well. The `Client::Client` could accept arguments `ca` ,
|
||||
`cert` and `key` for CA cert, cert and private key files for the SSL/TLS transport and authentication.
|
||||
Note that the later arguments `cert` and `key` could be empty strings or omitted if you just need
|
||||
`cert` and `privkey` for CA cert, cert and private key files for the SSL/TLS transport and authentication.
|
||||
Note that the later arguments `cert` and `privkey` could be empty strings or omitted if you just need
|
||||
secure transport and don't enable certificate-based client authentication (using the `--client-cert-auth`
|
||||
arguments when launching etcd server).
|
||||
|
||||
|
|
@ -214,7 +295,7 @@ arguments when launching etcd server).
|
|||
"round_robin");
|
||||
```
|
||||
|
||||
Or the etcd client can be constructed explictly:
|
||||
Or the etcd client can be constructed explicitly:
|
||||
|
||||
```c++
|
||||
etcd::Client *etcd = etcd::Client::WithSSL(
|
||||
|
|
@ -236,26 +317,148 @@ transport security using openssl.
|
|||
|
||||
We also provide a tool [`setup-ca.sh`](./security-config/setup-ca.sh) as a helper for development and testing.
|
||||
|
||||
#### transport security & multiple endpoints
|
||||
|
||||
If you want to use multiple `https://` endpoints, and you are working with self-signed certificates, you
|
||||
may encountered errors like
|
||||
|
||||
```
|
||||
error: 14: connections to all backends failing
|
||||
```
|
||||
|
||||
That means your DNS have some problems with your DNS resolver and SSL authority, you could put a domain
|
||||
name (a host name) to the `SANS` field when self-signing your certificate, e.g,
|
||||
|
||||
```
|
||||
"sans": [
|
||||
"etcd",
|
||||
"127.0.0.1",
|
||||
"127.0.0.2",
|
||||
"127.0.0.3"
|
||||
],
|
||||
```
|
||||
|
||||
And pass a `target_name_override` arguments to `WithSSL`,
|
||||
|
||||
```cpp
|
||||
etcd::Client *etcd = etcd::Client::WithSSL(
|
||||
"https://127.0.0.1:2379,https://127.0.0.2:2479",
|
||||
"example.rootca.cert", "example.cert", "example.key", "etcd");
|
||||
```
|
||||
|
||||
For more discussion about this feature, see also [#87](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/issues/87),
|
||||
[grpc#20186](https://github.com/grpc/grpc/issues/20186) and [grpc#22119](https://github.com/grpc/grpc/issues/22119).
|
||||
|
||||
### Fine-grained gRPC channel arguments
|
||||
|
||||
By default the etcd-cpp-apiv3 library will set the following arguments for transport layer
|
||||
|
||||
+ `GRPC_ARG_MAX_SEND_MESSAGE_LENGTH` to `INT_MAX`
|
||||
+ `GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH` to `INT_MAX`
|
||||
|
||||
If _load balancer strategy_ is specified, the following argument will be set
|
||||
|
||||
+ `GRPC_ARG_LB_POLICY_NAME`
|
||||
|
||||
When transport security is enabled and `target_name_override` is specified when working with SSL, the
|
||||
following argument will be set
|
||||
|
||||
+ `GRPC_SSL_TARGET_NAME_OVERRIDE_ARG`
|
||||
|
||||
Further, all variants of constructors for `etcd::Client` accepts an extra `grpc::ChannelArguments` argument
|
||||
which can be used for fine-grained control the gRPC settings, e.g.,
|
||||
|
||||
```cpp
|
||||
grpc::ChannelArguments grpc_args;
|
||||
grpc_args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 2000);
|
||||
grpc_args.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 6000);
|
||||
grpc_args.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1);
|
||||
|
||||
etcd::Client etcd("http://127.0.0.1:2379", grpc_args);
|
||||
```
|
||||
|
||||
For more motivation and discussion about the above design, please refer to [issue-103](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/issues/103).
|
||||
|
||||
### gRPC timeout when waiting for responses
|
||||
|
||||
gRPC Timeout is long-standing missing pieces in the etcd-cpp-apiv3 library. The timeout has been
|
||||
supported via a `set_grpc_timeout` interfaces on the client,
|
||||
|
||||
```cpp
|
||||
template <typename Rep = std::micro>
|
||||
void set_grpc_timeout(std::chrono::duration<Rep> const &timeout)
|
||||
```
|
||||
|
||||
Any `std::chrono::duration` value can be used to set the grpc timeout, e.g.,
|
||||
|
||||
```cpp
|
||||
etcd.set_grpc_timeout(std::chrono::seconds(5));
|
||||
```
|
||||
|
||||
Note that the timeout value is the "timeout" when waiting for responses upon the gRPC channel, i.e., `CompletionQueue::AsyncNext`.
|
||||
It doesn't means the timeout between issuing a `.set()` method getting the `etcd::Response`, as in the async mode the such a time
|
||||
duration is unpredictable and the gRPC timeout should be enough to avoid deadly waiting (e.g., waiting for a `lock()`).
|
||||
|
||||
### Error code in responses
|
||||
|
||||
The `class etcd::Response` may yield an error code and error message when error occurs,
|
||||
|
||||
```cpp
|
||||
int error_code() const;
|
||||
std::string const & error_message() const;
|
||||
|
||||
bool is_ok() const;
|
||||
```
|
||||
|
||||
The error code would be `0` when succeed, otherwise the error code might be
|
||||
|
||||
```cpp
|
||||
extern const int ERROR_GRPC_OK;
|
||||
extern const int ERROR_GRPC_CANCELLED;
|
||||
extern const int ERROR_GRPC_UNKNOWN;
|
||||
extern const int ERROR_GRPC_INVALID_ARGUMENT;
|
||||
extern const int ERROR_GRPC_DEADLINE_EXCEEDED;
|
||||
extern const int ERROR_GRPC_NOT_FOUND;
|
||||
extern const int ERROR_GRPC_ALREADY_EXISTS;
|
||||
extern const int ERROR_GRPC_PERMISSION_DENIED;
|
||||
extern const int ERROR_GRPC_UNAUTHENTICATED;
|
||||
extern const int ERROR_GRPC_RESOURCE_EXHAUSTED;
|
||||
extern const int ERROR_GRPC_FAILED_PRECONDITION;
|
||||
extern const int ERROR_GRPC_ABORTED;
|
||||
extern const int ERROR_GRPC_OUT_OF_RANGE;
|
||||
extern const int ERROR_GRPC_UNIMPLEMENTED;
|
||||
extern const int ERROR_GRPC_INTERNAL;
|
||||
extern const int ERROR_GRPC_UNAVAILABLE;
|
||||
extern const int ERROR_GRPC_DATA_LOSS;
|
||||
|
||||
extern const int ERROR_KEY_NOT_FOUND;
|
||||
extern const int ERROR_COMPARE_FAILED;
|
||||
extern const int ERROR_KEY_ALREADY_EXISTS;
|
||||
extern const int ERROR_ACTION_CANCELLED;
|
||||
```
|
||||
|
||||
## Etcd operations
|
||||
|
||||
### Reading a value
|
||||
|
||||
You can read a value with the ```get``` method of the clinent instance. The only parameter is the
|
||||
You can read a value with the `get()` method of the client instance. The only parameter is the
|
||||
key to be read. If the read operation is successful then the value of the key can be acquired with
|
||||
the ```value()``` method of the response. Success of the operation can be checked with the
|
||||
```is_ok()``` method of the response. In case of an error, the ```error_code()``` and
|
||||
```error_message()``` methods can be called for some further detail.
|
||||
the `value()` method of the response. Success of the operation can be checked with the
|
||||
`is_ok()` method of the response. In case of an error, the `error_code()` and
|
||||
`error_message()` methods can be called for some further detail.
|
||||
|
||||
Please note that there can be two kind of error situations. There can be some problem with the
|
||||
communication between the client and the etcd server. In this case the ```get()``` method of the
|
||||
communication between the client and the etcd server. In this case the `get()``` method of the
|
||||
response task will throw an exception as shown above. If the communication is ok but there is some
|
||||
problem with the content of the actual operation, like attemp to read a non-existing key then the
|
||||
problem with the content of the actual operation, like attempting to read a non-existing key then the
|
||||
response object will give you all the details. Let's see this in an example.
|
||||
|
||||
The Value object of the response also holds some extra information besides the string value of the
|
||||
key. You can also get the index number of the creation and the last modification of this key with
|
||||
the ```created_index()``` and the ```modofied_index()``` methods.
|
||||
the `created_index()` and the `modified_index()` methods.
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
pplx::task<etcd::Response> response_task = etcd.get("/test/key1");
|
||||
|
||||
try
|
||||
|
|
@ -266,23 +469,33 @@ the ```created_index()``` and the ```modofied_index()``` methods.
|
|||
else
|
||||
std::cout << "operation failed, details: " << response.error_message();
|
||||
}
|
||||
catch (std::ecxeption const & ex)
|
||||
catch (std::exception const & ex)
|
||||
{
|
||||
std::cerr << "communication problem, details: " << ex.what();
|
||||
}
|
||||
```
|
||||
|
||||
### Modifying a value
|
||||
### Put a value
|
||||
|
||||
Setting the value of a key can be done with the ```set()``` method of the client. You simply pass
|
||||
the key and the value as string parameters and you are done. The newly set value object can be asked
|
||||
from the response object exactly the same way as in case of the reading (with the ```value()```
|
||||
method). This way you can check for example the index value of your modification. You can also check
|
||||
what was the previous value that this operation was overwritten. You can do that with the
|
||||
```prev_value()``` method of the response object.
|
||||
You can put a key-value pair to etcd with the the `put()` method of the client instance. The only
|
||||
parameter is the key and value to be put
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
pplx::task<etcd::Response> response_task = etcd.put("foo", "bar");
|
||||
```
|
||||
|
||||
### Modifying a value
|
||||
|
||||
Setting the value of a key can be done with the `set()` method of the client. You simply pass
|
||||
the key and the value as string parameters and you are done. The newly set value object can be asked
|
||||
from the response object exactly the same way as in case of the reading (with the `value()`
|
||||
method). This way you can check for example the index value of your modification. You can also check
|
||||
what was the previous value that this operation was overwritten. You can do that with the
|
||||
`prev_value()` method of the response object.
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
pplx::task<etcd::Response> response_task = etcd.set("/test/key1", "42");
|
||||
|
||||
try
|
||||
|
|
@ -294,7 +507,7 @@ what was the previous value that this operation was overwritten. You can do that
|
|||
else
|
||||
std::cout << "operation failed, details: " << response.error_message();
|
||||
}
|
||||
catch (std::ecxeption const & ex)
|
||||
catch (std::exception const & ex)
|
||||
{
|
||||
std::cerr << "communication problem, details: " << ex.what();
|
||||
}
|
||||
|
|
@ -304,28 +517,29 @@ The set method creates a new leaf node if it weren't exists already or modifies
|
|||
There are a couple of other modification methods that are executing the write operation only upon
|
||||
some specific conditions.
|
||||
|
||||
* ```add(key, value)``` creates a new value if it's key does not exists and returns a "Key
|
||||
already exists" error otherwise (error code 105)
|
||||
* ```modify(key, value)``` modifies an already existing value or returns a "Key not found" error
|
||||
otherwise (error code 100)
|
||||
* ```modify_if(key, value, old_value)``` modifies 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
|
||||
(code 101)
|
||||
* ```modify_if(key, value, old_index)``` modifies 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
|
||||
failed" error (code 101)
|
||||
* `add(key, value)` creates a new value if it's key does not exists and returns a "Key
|
||||
already exists" error otherwise (error code `ERROR_KEY_ALREADY_EXISTS`)
|
||||
* `modify(key, value)` modifies an already existing value or returns a "etcd-cpp-apiv3: key not found" error
|
||||
otherwise (error code `KEY_NOT_FOUND`)
|
||||
* `modify_if(key, value, old_value)` modifies 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
|
||||
(code `ERROR_COMPARE_FAILED`)
|
||||
* `modify_if(key, value, old_index)` modifies 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
|
||||
failed" error (code `ERROR_COMPARE_FAILED`)
|
||||
|
||||
### Deleting a value
|
||||
|
||||
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.
|
||||
|
||||
* ```rm_if(key, value, 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
|
||||
(code 101)
|
||||
* ```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
|
||||
failed" error (code 101)
|
||||
* `rm(std::string const& key)` unconditionally deletes the given key
|
||||
* `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
|
||||
(code `ERROR_COMPARE_FAILED`)
|
||||
* `rm_if(key, 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
|
||||
failed" error (code `ERROR_COMPARE_FAILED`)
|
||||
|
||||
### Handling directory nodes
|
||||
|
||||
|
|
@ -339,78 +553,103 @@ keys defined by the prefix. mkdir method is removed since etcdv3 treats everythi
|
|||
|
||||
2. Listing a directory:
|
||||
|
||||
Listing directory in etcd3 cpp client will return all keys that matched the given prefix
|
||||
recursively.
|
||||
Listing directory in etcd3 cpp client will return all keys that matched the given prefix
|
||||
recursively.
|
||||
|
||||
```c++
|
||||
etcd.set("/test/key1", "value1").wait();
|
||||
etcd.set("/test/key2", "value2").wait();
|
||||
etcd.set("/test/key3", "value3").wait();
|
||||
etcd.set("/test/subdir/foo", "foo").wait();
|
||||
```c++
|
||||
etcd.set("/test/key1", "value1").wait();
|
||||
etcd.set("/test/key2", "value2").wait();
|
||||
etcd.set("/test/key3", "value3").wait();
|
||||
etcd.set("/test/subdir/foo", "foo").wait();
|
||||
|
||||
etcd::Response resp = etcd.ls("/test/new_dir").get();
|
||||
```
|
||||
etcd::Response resp = etcd.ls("/test").get();
|
||||
```
|
||||
|
||||
```resp.key()``` will have the following values:
|
||||
`resp.key()` will have the following values:
|
||||
|
||||
```
|
||||
/test/key1
|
||||
/test/key2
|
||||
/test/key3
|
||||
/test/subdir/foo
|
||||
```
|
||||
```
|
||||
/test/key1s
|
||||
/test/key2
|
||||
/test/key3
|
||||
/test/subdir/foo
|
||||
```
|
||||
|
||||
Note: Regarding the returned keys when listing a directory:
|
||||
Note: Regarding the returned keys when listing a directory:
|
||||
|
||||
+ In etcdv3 cpp client, resp.key(0) will return "/test/new_dir/key1" since everything is
|
||||
treated as keys in etcdv3.
|
||||
+ While in etcdv2 cpp client it will return "key1" and "/test/new_dir" directory should
|
||||
be created first before you can set "key1".
|
||||
+ In etcdv3 cpp client, resp.key(0) will return "/test/new_dir/key1" since everything is
|
||||
treated as keys in etcdv3.
|
||||
+ While in etcdv2 cpp client it will return "key1" and "/test/new_dir" directory should
|
||||
be created first before you can set "key1".
|
||||
|
||||
When you list a directory the response object's ```keys()``` and ```values()``` methods gives
|
||||
you a vector of key names and values. The ```value()``` method with an integer parameter also
|
||||
returns with the i-th element of the values vector, so ```response.values()[i] ==
|
||||
response.value(i)```.
|
||||
When you list a directory the response object's `keys()` and `values()` methods gives
|
||||
you a vector of key names and values. The `value()` method with an integer parameter also
|
||||
returns with the i-th element of the values vector, so `response.values()[i] == response.value(i)`.
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd::Response resp = etcd.ls("/test/new_dir").get();
|
||||
for (int i = 0; i < resp.keys().size(); ++i)
|
||||
{
|
||||
std::cout << resp.keys(i);
|
||||
std::cout << " = " << resp.value(i).as_string() << std::endl;
|
||||
}
|
||||
```
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd::Response resp = etcd.ls("/test/new_dir").get();
|
||||
for (int i = 0; i < resp.keys().size(); ++i)
|
||||
{
|
||||
std::cout << resp.keys(i);
|
||||
std::cout << " = " << resp.value(i).as_string() << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
etcd-cpp-apiv3 supports lists keys only without fetching values from etcd server:
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd::Response resp = etcd.keys("/test/new_dir").get();
|
||||
for (int i = 0; i < resp.keys().size(); ++i)
|
||||
{
|
||||
std::cout << resp.keys(i);
|
||||
}
|
||||
```
|
||||
|
||||
3. Removing directory:
|
||||
|
||||
If you want the delete recursively then you have to pass a second ```true``` parameter
|
||||
to rmdir and supply a key. This key will be treated as a prefix. All keys that match the
|
||||
prefix will be deleted. All deleted keys will be placed in ```response.values()``` and
|
||||
```response.keys()```. This parameter defaults to ```false```.
|
||||
If you want the delete recursively then you have to pass a second `true` parameter
|
||||
to rmdir and supply a key. This key will be treated as a prefix. All keys that match the
|
||||
prefix will be deleted. All deleted keys will be placed in `response.values()` and
|
||||
`response.keys()`. This parameter defaults to `false`.
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd.set("/test/key1", "foo");
|
||||
etcd.set("/test/key2", "bar");
|
||||
etcd.set("/test/key3", "foo_bar");
|
||||
etcd::Response resp = etcd.rmdir("/test", true).get();
|
||||
for (int i = 0; i < resp.keys().size(); ++i)
|
||||
{
|
||||
std::cout << resp.keys(i);
|
||||
std::cout << " = " << resp.value(i).as_string() << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
However, if recursive parameter is false, functionality will be the same as just deleting a key.
|
||||
The key supplied will NOT be treated as a prefix and will be treated as a normal key name.
|
||||
|
||||
### Using binary data as key and value
|
||||
|
||||
Etcd itself support using arbitrary binary data as the key and value, i.e., the key and value
|
||||
can contain `\NUL` (`\0`) and not necessary NUL-terminated strings. `std::string` in C++ supports
|
||||
embed `\0` as well, but please note that when constructing `std::string` from a C-style string
|
||||
the string will be terminated by the first `\0` character. Rather, you need to use the constructor
|
||||
with the `count` parameter explicitly. When unpack a `std::string` that contains `\0`, you need
|
||||
`.data()`,
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd.set("/test/key1", "foo");
|
||||
etcd.set("/test/key2", "bar");
|
||||
etcd.set("/test/key3", "foo_bar");
|
||||
etcd::Response resp = etcd.rmdir("/test", true).get();
|
||||
for (int i = 0; i < resp.keys().size(); ++i)
|
||||
{
|
||||
std::cout << resp.keys(i);
|
||||
std::cout << " = " << resp.value(i).as_string() << std::endl;
|
||||
}
|
||||
std::string key = "key-foo\0bar";
|
||||
std::string value = "value-foo\0bar";
|
||||
etcd.put(key, value).wait();
|
||||
```
|
||||
|
||||
However, if recursive parameter is false, functionality will be the same as just deleting a key.
|
||||
The key supplied will NOT be treated as a prefix and will be treated as a normal key name.
|
||||
|
||||
### Lock
|
||||
|
||||
Etcd lock has been supported as follows:
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd.lock("/test/lock");
|
||||
```
|
||||
|
||||
|
|
@ -420,28 +659,41 @@ the lock is unlocked.
|
|||
Users can also feed their own lease directory for lock:
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd.lock_with_lease("/test/lock", lease_id);
|
||||
```
|
||||
|
||||
Note that the arguments for `unlock()` is the the same key that used for `lock()`, but the
|
||||
`response.lock_key()` that return by `lock()`:
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
|
||||
// lock
|
||||
auto response = etcd.lock("/test/lock").get();
|
||||
|
||||
// unlock
|
||||
auto _ = etcd.unlock(response.lock_key()).get();
|
||||
```
|
||||
|
||||
### Watching for changes
|
||||
|
||||
Watching for a change is possible with the ```watch()``` operation of the client. The watch method
|
||||
Watching for a change is possible with the `watch()` operation of the client. The watch method
|
||||
simply does not deliver a response object until the watched value changes in any way (modified or
|
||||
deleted). When a change happens the returned result object will be the same as the result object of
|
||||
the modification operation. So if the change is triggered by a value change, then
|
||||
```response.action()``` will return "set", ```response.value()``` will hold the new
|
||||
value and ```response.prev_value()``` will contain the previous value. In case of a delete
|
||||
```response.action()``` will return "delete", ```response.value()``` will be empty and should not be
|
||||
called at all and ```response.prev_value()``` will contain the deleted value.
|
||||
`response.action()` will return "set", `response.value()` will hold the new
|
||||
value and `response.prev_value()` will contain the previous value. In case of a delete
|
||||
`response.action()` will return "delete", `response.value()` will be empty and should not be
|
||||
called at all and `response.prev_value()` will contain the deleted value.
|
||||
|
||||
As mentioned in the section "handling directory nodes", directory nodes are not supported anymore
|
||||
in etcdv3.
|
||||
|
||||
However it is still possible to watch a whole "directory subtree", or more specifically a set of
|
||||
keys that match the prefix, for changes with passing ```true``` to the second ```recursive```
|
||||
parameter of ```watch``` (this parameter defaults to ```false``` if omitted). In this case the
|
||||
modified value object's ```key()``` method can be handy to determine what key is actually changed.
|
||||
keys that match the prefix, for changes with passing `true` to the second `recursive`
|
||||
parameter of `watch` (this parameter defaults to `false` if omitted). In this case the
|
||||
modified value object's `key()` method can be handy to determine what key is actually changed.
|
||||
Since this can be a long lasting operation you have to be prepared that is terminated by an
|
||||
exception and you have to restart the watch operation.
|
||||
|
||||
|
|
@ -468,17 +720,17 @@ void watch_for_changes()
|
|||
}
|
||||
```
|
||||
|
||||
At first glance it seems that ```watch_for_changes()``` calls itself on every value change but in
|
||||
fact it just sends the asynchron request, sets up a callback for the response and then returns. The
|
||||
At first glance it seems that `watch_for_changes()` calls itself on every value change but in
|
||||
fact it just sends the asynchronous request, sets up a callback for the response and then returns. The
|
||||
callback is executed by some thread from the pplx library's thread pool and the callback (in this
|
||||
case a small lambda function actually) will call ```watch_for_changes``` again from there.
|
||||
case a small lambda function actually) will call `watch_for_changes()` again from there.
|
||||
|
||||
#### Watcher Class
|
||||
|
||||
Users can watch a key indefinitely or until user cancels the watch. This can be done by
|
||||
instantiating a Watcher class. The supplied callback function in Watcher class will be
|
||||
called every time there is an event for the specified key. Watch stream will be cancelled
|
||||
either by user implicitly calling ```Cancel()``` or when watcher class is destroyed.
|
||||
either by user implicitly calling `Cancel()` or when watcher class is destroyed.
|
||||
|
||||
```c++
|
||||
etcd::Watcher watcher("http://127.0.0.1:2379", "/test", printResponse);
|
||||
|
|
@ -487,18 +739,77 @@ either by user implicitly calling ```Cancel()``` or when watcher class is destro
|
|||
watcher.Cancel();
|
||||
etcd.set("/test/key", "43"); /* print response will NOT be called,
|
||||
since watch is already cancelled */
|
||||
```
|
||||
|
||||
#### Watcher re-connection
|
||||
|
||||
A watcher will be disconnected from etcd server in some cases, for some examples, the etcd
|
||||
server is restarted, or the network is temporarily unavailable. It is users' responsibility
|
||||
to decide if a watcher should re-connect to the etcd server.
|
||||
|
||||
Here is an example how users can make a watcher re-connect to server after disconnected.
|
||||
|
||||
```c++
|
||||
// wait the client ready
|
||||
void wait_for_connection(etcd::Client &client) {
|
||||
// wait until the client connects to etcd server
|
||||
while (!client.head().get().is_ok()) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
// a loop for initialized a watcher with auto-restart capability
|
||||
void initialize_watcher(const std::string& endpoints,
|
||||
const std::string& prefix,
|
||||
std::function<void(etcd::Response)> callback,
|
||||
std::shared_ptr<etcd::Watcher>& watcher) {
|
||||
etcd::Client client(endpoints);
|
||||
wait_for_connection(client);
|
||||
|
||||
// Check if the failed one has been cancelled first
|
||||
if (watcher && watcher->Cancelled()) {
|
||||
std::cout << "watcher's reconnect loop been cancelled" << std::endl;
|
||||
return;
|
||||
}
|
||||
watcher.reset(new etcd::Watcher(client, prefix, callback, true));
|
||||
|
||||
// Note that lambda requires `mutable`qualifier.
|
||||
watcher->Wait([endpoints, prefix, callback,
|
||||
/* By reference for renewing */ &watcher](bool cancelled) mutable {
|
||||
if (cancelled) {
|
||||
std::cout << "watcher's reconnect loop stopped as been cancelled" << std::endl;
|
||||
return;
|
||||
}
|
||||
initialize_watcher(endpoints, prefix, callback, watcher);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The functionalities can be used as
|
||||
|
||||
```c++
|
||||
std::string endpoints = "http://127.0.0.1:2379";
|
||||
std::function<void(Response)> callback = printResponse;
|
||||
const std::string prefix = "/test/key";
|
||||
|
||||
// the watcher initialized in this way will auto re-connect to etcd
|
||||
std::shared_ptr<etcd::Watcher> watcher;
|
||||
initialize_watcher(endpoints, prefix, callback, watcher);
|
||||
```
|
||||
|
||||
For a complete runnable example, see also [./tst/RewatchTest.cpp](./tst/RewatchTest.cpp). 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 watcher may have been destroyed.
|
||||
|
||||
### Requesting for lease
|
||||
|
||||
Users can request for lease which is governed by a time-to-live(TTL) value given by the user.
|
||||
Moreover, user can attached the lease to a key(s) by indicating the lease id in ```add()```,
|
||||
```set()```, ```modify()``` and ```modify_if()```. Also the ttl will that was granted by etcd
|
||||
server will be indicated in ```ttl()```.
|
||||
Moreover, user can attached the lease to a key(s) by indicating the lease id in `add()`,
|
||||
`set()`, `modify()` and `modify_if()`. Also the ttl will that was granted by etcd
|
||||
server will be indicated in `ttl()`.
|
||||
|
||||
```c++
|
||||
etcd::Client etcd("http://127.0.0.1:4001");
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd::Response resp = etcd.leasegrant(60).get();
|
||||
etcd.set("/test/key2", "bar", resp.value().lease());
|
||||
std::cout << "ttl" << resp.value().ttl();
|
||||
|
|
@ -526,13 +837,13 @@ The remaining time-to-live of a lease can be inspected by
|
|||
|
||||
#### Keep alive
|
||||
|
||||
Keep alive for leases is implemented using a seperate class `KeepAlive`, which can be used as:
|
||||
Keep alive for leases is implemented using a separate class `KeepAlive`, which can be used as:
|
||||
|
||||
```c++
|
||||
etcd::KeepAlive keepalive(etcd, ttl, lease_id);
|
||||
```
|
||||
|
||||
It will perform a periodly keep-alive action before it is cancelled explicitly, or destructed implicitly.
|
||||
It will perform a period keep-alive action before it is cancelled explicitly, or destructed implicitly.
|
||||
|
||||
`KeepAlive` may fails (e.g., when the etcd server stopped unexpectedly), the constructor of `KeepAlive`
|
||||
could accept a handler of type `std::function<std::exception_ptr>` and the handler will be invoked
|
||||
|
|
@ -559,7 +870,115 @@ is constructed.
|
|||
Without handler, the internal state can be checked via `KeepAlive::Check()` and it will rethrow
|
||||
the async exception when there are errors during keeping the lease alive.
|
||||
|
||||
### TODO
|
||||
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 v3's [Transaction APIs](https://etcd.io/docs/v3.4/learning/api/#transaction) is supported via the
|
||||
`etcdv3::Transaction` interfaces. A set of convenient APIs are use to add operations to a transaction, e.g.,
|
||||
|
||||
```cpp
|
||||
etcdv3::Transaction txn;
|
||||
txn.setup_put("/test/x1", "1");
|
||||
txn.setup_put("/test/x2", "2");
|
||||
txn.setup_put("/test/x3", "3");
|
||||
etcd::Response resp = etcd.txn(txn).get();
|
||||
```
|
||||
|
||||
Transactions in etcd supports set a set of comparison targets to specify the condition of transaction, e.g.,
|
||||
|
||||
```cpp
|
||||
etcdv3::Transaction txn;
|
||||
|
||||
// setup the conditions
|
||||
txn.add_compare_value("/test/x1", "1");
|
||||
txn.add_compare_value("/test/x2", "2");
|
||||
|
||||
// or, compare the last modified revision
|
||||
txn.add_compare_mod("/test/x3", 0); // not exists
|
||||
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),
|
||||
for full list of the transaction operation APIs, see [./etcd/v3/Transaction.hpp](./etcd/v3/Transaction.hpp).
|
||||
|
||||
### Election API
|
||||
|
||||
Etcd v3's [election APIs](https://github.com/etcd-io/etcd/blob/main/server/etcdserver/api/v3election/v3electionpb/v3election.proto)
|
||||
are supported via the following interfaces,
|
||||
|
||||
```c++
|
||||
pplx::task<Response> campaign(std::string const &name, int64_t lease_id,
|
||||
std::string const &value);
|
||||
|
||||
pplx::task<Response> proclaim(std::string const &name, int64_t lease_id,
|
||||
std::string const &key, int64_t revision,
|
||||
std::string const &value);
|
||||
|
||||
pplx::task<Response> leader(std::string const &name);
|
||||
|
||||
std::unique_ptr<SyncClient::Observer> observe(std::string const &name);
|
||||
|
||||
pplx::task<Response> resign(std::string const &name, int64_t lease_id,
|
||||
std::string const &key, int64_t revision);
|
||||
```
|
||||
|
||||
Note that if grpc timeout is set, `campaign()` will return an timeout error response if it
|
||||
cannot acquire the election ownership within the timeout period. Otherwise will block until
|
||||
become the leader.
|
||||
|
||||
The `Observer` returned by `observe()` can be use to monitor the changes of election ownership.
|
||||
The observer stream will be canceled when been destructed.
|
||||
|
||||
```c++
|
||||
std::unique_ptr<etcd::SyncClient::Observer> observer = etcd.observe("test");
|
||||
|
||||
// wait one change event, blocked execution
|
||||
etcd::Response resp = observer->WaitOnce();
|
||||
|
||||
// wait many change events, blocked execution
|
||||
for (size_t i = 0; i < ...; ++i) {
|
||||
etcd::Response resp = observer->WaitOnce();
|
||||
...
|
||||
}
|
||||
|
||||
// cancel the observer
|
||||
observer.reset(nullptr);
|
||||
```
|
||||
|
||||
for more details, please refer to [etcd/Client.hpp](./etcd/Client.hpp) and [tst/ElectionTest.cpp](./tst/ElectionTest.cpp).
|
||||
|
||||
## `-fno-exceptions`
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,51 +22,89 @@ mark_as_advanced(GRPC_INCLUDE_DIR)
|
|||
# Find gGPR library
|
||||
find_library(GPR_LIBRARY NAMES gpr)
|
||||
mark_as_advanced(GRPC_GPR_LIBRARY)
|
||||
add_library(gRPC::gpr UNKNOWN IMPORTED)
|
||||
set_target_properties(gRPC::gpr PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
|
||||
INTERFACE_LINK_LIBRARIES "-lpthread;-ldl"
|
||||
IMPORTED_LOCATION ${GPR_LIBRARY}
|
||||
)
|
||||
if(NOT TARGET gRPC::gpr)
|
||||
add_library(gRPC::gpr UNKNOWN IMPORTED)
|
||||
set_target_properties(gRPC::gpr PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
|
||||
INTERFACE_LINK_LIBRARIES "-lpthread;-ldl"
|
||||
IMPORTED_LOCATION ${GPR_LIBRARY}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Find gRPC library
|
||||
find_library(GRPC_LIBRARY NAMES grpc)
|
||||
mark_as_advanced(GRPC_LIBRARY)
|
||||
add_library(gRPC::grpc UNKNOWN IMPORTED)
|
||||
set_target_properties(gRPC::grpc PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
|
||||
INTERFACE_LINK_LIBRARIES gRPC::gpr
|
||||
IMPORTED_LOCATION ${GRPC_LIBRARY}
|
||||
)
|
||||
if(NOT TARGET gRPC::grpc)
|
||||
add_library(gRPC::grpc UNKNOWN IMPORTED)
|
||||
set_target_properties(gRPC::grpc PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
|
||||
INTERFACE_LINK_LIBRARIES gRPC::gpr
|
||||
IMPORTED_LOCATION ${GRPC_LIBRARY}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Find gRPC C++ library
|
||||
find_library(GRPC_GRPC++_LIBRARY NAMES grpc++)
|
||||
mark_as_advanced(GRPC_GRPC++_LIBRARY)
|
||||
add_library(gRPC::grpc++ UNKNOWN IMPORTED)
|
||||
set_target_properties(gRPC::grpc++ PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
|
||||
INTERFACE_LINK_LIBRARIES gRPC::grpc
|
||||
IMPORTED_LOCATION ${GRPC_GRPC++_LIBRARY}
|
||||
)
|
||||
if(NOT TARGET gRPC::grpc++)
|
||||
add_library(gRPC::grpc++ UNKNOWN IMPORTED)
|
||||
set_target_properties(gRPC::grpc++ PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
|
||||
INTERFACE_LINK_LIBRARIES gRPC::grpc
|
||||
IMPORTED_LOCATION ${GRPC_GRPC++_LIBRARY}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Find gRPC C++ reflection library
|
||||
find_library(GRPC_GRPC++_REFLECTION_LIBRARY NAMES grpc++_reflection)
|
||||
mark_as_advanced(GRPC_GRPC++_REFLECTION_LIBRARY)
|
||||
add_library(gRPC::grpc++_reflection UNKNOWN IMPORTED)
|
||||
set_target_properties(gRPC::grpc++_reflection PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
|
||||
INTERFACE_LINK_LIBRARIES gRPC::grpc++
|
||||
IMPORTED_LOCATION ${GRPC_GRPC++_REFLECTION_LIBRARY}
|
||||
)
|
||||
if(NOT TARGET gRPC::grpc++_reflection)
|
||||
add_library(gRPC::grpc++_reflection UNKNOWN IMPORTED)
|
||||
set_target_properties(gRPC::grpc++_reflection PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${GRPC_INCLUDE_DIR}
|
||||
INTERFACE_LINK_LIBRARIES gRPC::grpc++
|
||||
IMPORTED_LOCATION ${GRPC_GRPC++_REFLECTION_LIBRARY}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Find gRPC CPP generator
|
||||
find_program(GRPC_CPP_PLUGIN NAMES grpc_cpp_plugin)
|
||||
mark_as_advanced(GRPC_CPP_PLUGIN)
|
||||
add_executable(gRPC::grpc_cpp_plugin IMPORTED)
|
||||
set_target_properties(gRPC::grpc_cpp_plugin PROPERTIES
|
||||
IMPORTED_LOCATION ${GRPC_CPP_PLUGIN}
|
||||
)
|
||||
if(NOT TARGET gRPC::grpc_cpp_plugin)
|
||||
add_executable(gRPC::grpc_cpp_plugin IMPORTED)
|
||||
set_target_properties(gRPC::grpc_cpp_plugin PROPERTIES
|
||||
IMPORTED_LOCATION ${GRPC_CPP_PLUGIN}
|
||||
)
|
||||
endif()
|
||||
|
||||
file(
|
||||
WRITE "${CMAKE_BINARY_DIR}/get_gRPC_version.cc"
|
||||
[====[
|
||||
#include <grpc++/grpc++.h>
|
||||
#include <iostream>
|
||||
int main() {
|
||||
std::cout << grpc::Version(); // no newline to simplify CMake module
|
||||
return 0;
|
||||
}
|
||||
]====])
|
||||
|
||||
try_run(
|
||||
_gRPC_GET_VERSION_STATUS
|
||||
_gRPC_GET_VERSION_COMPILE_STATUS
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
"${CMAKE_BINARY_DIR}/get_gRPC_version.cc"
|
||||
CMAKE_FLAGS
|
||||
-DCMAKE_CXX_STANDARD=11
|
||||
LINK_LIBRARIES
|
||||
gRPC::grpc++
|
||||
gRPC::grpc
|
||||
COMPILE_OUTPUT_VARIABLE _gRPC_GET_VERSION_COMPILE_OUTPUT
|
||||
RUN_OUTPUT_VARIABLE gRPC_VERSION)
|
||||
|
||||
file(REMOVE "${CMAKE_BINARY_DIR}/get_gRPC_version.cc")
|
||||
|
||||
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(gRPC DEFAULT_MSG
|
||||
GPR_LIBRARY GRPC_LIBRARY GRPC_INCLUDE_DIR GRPC_GRPC++_REFLECTION_LIBRARY GRPC_CPP_PLUGIN)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(gRPC
|
||||
FOUND_VAR gRPC_FOUND
|
||||
REQUIRED_VARS GRPC_LIBRARY GPR_LIBRARY GRPC_INCLUDE_DIR GRPC_GRPC++_REFLECTION_LIBRARY GRPC_CPP_PLUGIN
|
||||
VERSION_VAR gRPC_VERSION)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ if(NOT gRPC_FOUND)
|
|||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
|
||||
find_dependency(GRPC)
|
||||
endif()
|
||||
|
||||
find_dependency(cpprestsdk)
|
||||
if(cpprestsdk_FOUND)
|
||||
set(CPPREST_LIB cpprestsdk::cpprest)
|
||||
|
|
@ -22,12 +23,17 @@ endif()
|
|||
set(ETCD_CPP_HOME "${CMAKE_CURRENT_LIST_DIR}/../../..")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/etcd-targets.cmake")
|
||||
|
||||
set(etcd-cpp-api_FOUND TRUE)
|
||||
set(ETCD_CPP_LIBRARIES etcd-cpp-api)
|
||||
set(ETCD_CPP_CORE_LIBRARIES etcd-cpp-api-core)
|
||||
set(ETCD_CPP_INCLUDE_DIR "${ETCD_CPP_HOME}/include")
|
||||
set(ETCD_CPP_INCLUDE_DIRS "${ETCD_CPP_INCLUDE_DIR}")
|
||||
|
||||
include(FindPackageMessage)
|
||||
find_package_message(etcd
|
||||
"Found etcd: ${CMAKE_CURRENT_LIST_FILE} (found version \"@etcd-cpp-api_VERSION@\")"
|
||||
"etcd-cpp-apiv3 version: @etcd-cpp-api_VERSION@\netcd-cpp-apiv3 libraries: ${ETCD_CPP_LIBRARIES}, include directories: ${ETCD_CPP_INCLUDE_DIRS}"
|
||||
"etcd-cpp-apiv3 version: @etcd-cpp-api_VERSION@\n"
|
||||
"etcd-cpp-apiv3 libraries: ${ETCD_CPP_LIBRARIES}\n"
|
||||
"etcd-cpp-apiv3 core libraries: ${ETCD_CPP_CORE_LIBRARIES}\n"
|
||||
"include directories: ${ETCD_CPP_INCLUDE_DIRS}"
|
||||
)
|
||||
|
|
|
|||
1099
etcd/Client.hpp
1099
etcd/Client.hpp
File diff suppressed because it is too large
Load Diff
|
|
@ -2,96 +2,122 @@
|
|||
#define __ETCD_KEEPALIVE_HPP__
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "etcd/Client.hpp"
|
||||
#include "etcd/Response.hpp"
|
||||
#include "etcd/SyncClient.hpp"
|
||||
|
||||
#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
|
||||
class Client;
|
||||
|
||||
/**
|
||||
* If ID is set to 0, the library will choose an ID, and can be accessed from
|
||||
* ".Lease()".
|
||||
*/
|
||||
class KeepAlive {
|
||||
public:
|
||||
KeepAlive(Client const& client, int ttl, int64_t lease_id = 0);
|
||||
KeepAlive(SyncClient const& client, 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);
|
||||
|
||||
KeepAlive(Client const& client,
|
||||
std::function<void(std::exception_ptr)> const& handler, int ttl,
|
||||
int64_t lease_id = 0);
|
||||
KeepAlive(SyncClient const& client,
|
||||
std::function<void(std::exception_ptr)> const& handler, int ttl,
|
||||
int64_t lease_id = 0);
|
||||
KeepAlive(std::string const& address,
|
||||
std::function<void(std::exception_ptr)> const& handler, int ttl,
|
||||
int64_t lease_id = 0);
|
||||
KeepAlive(std::string const& address, std::string const& username,
|
||||
std::string const& password,
|
||||
std::function<void(std::exception_ptr)> const& handler, int ttl,
|
||||
int64_t lease_id = 0, int const auth_token_ttl = 300);
|
||||
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 = 0, std::string const& target_name_override = "");
|
||||
|
||||
KeepAlive(KeepAlive const&) = delete;
|
||||
KeepAlive(KeepAlive&&) = delete;
|
||||
|
||||
int64_t Lease() const { return lease_id; }
|
||||
|
||||
namespace etcd
|
||||
{
|
||||
/**
|
||||
* If ID is set to 0, the library will choose an ID, and can be accessed from ".Lease()".
|
||||
* Stop the keep alive action.
|
||||
*/
|
||||
class KeepAlive
|
||||
{
|
||||
public:
|
||||
KeepAlive(Client const &client,
|
||||
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);
|
||||
void Cancel();
|
||||
|
||||
KeepAlive(Client const &client,
|
||||
std::function<void (std::exception_ptr)> const &handler,
|
||||
int ttl, int64_t lease_id=0);
|
||||
KeepAlive(std::string const & address,
|
||||
std::function<void (std::exception_ptr)> const &handler,
|
||||
int ttl, int64_t lease_id=0);
|
||||
KeepAlive(std::string const & address,
|
||||
std::string const & username, std::string const & password,
|
||||
std::function<void (std::exception_ptr)> const &handler,
|
||||
int ttl, int64_t lease_id=0);
|
||||
/**
|
||||
* Check if the keep alive is still valid (invalid when there's an async
|
||||
* exception).
|
||||
*
|
||||
* Nothing will happen if valid and an exception will be rethrowed if invalid.
|
||||
*/
|
||||
void Check();
|
||||
|
||||
KeepAlive(KeepAlive const &) = delete;
|
||||
KeepAlive(KeepAlive &&) = delete;
|
||||
/**
|
||||
* Set a timeout value for grpc operations.
|
||||
*/
|
||||
template <typename Rep = std::micro>
|
||||
void set_grpc_timeout(std::chrono::duration<Rep> const& timeout) {
|
||||
this->grpc_timeout =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(timeout);
|
||||
}
|
||||
|
||||
int64_t Lease() const { return lease_id; }
|
||||
/**
|
||||
* Get the current timeout value for grpc operations.
|
||||
*/
|
||||
std::chrono::microseconds get_grpc_timeout() const {
|
||||
return this->grpc_timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the keep alive action.
|
||||
*/
|
||||
void Cancel();
|
||||
~KeepAlive();
|
||||
|
||||
/**
|
||||
* Check if the keep alive is still valid (invalid when there's an async exception).
|
||||
*
|
||||
* Nothing will happen if valid and an exception will be rethrowed if invalid.
|
||||
*/
|
||||
void Check();
|
||||
protected:
|
||||
// automatically refresh loop, returns the error message if failed
|
||||
std::string refresh();
|
||||
// refresh once immediately, returns the error message if failed
|
||||
std::string refresh_once();
|
||||
|
||||
~KeepAlive();
|
||||
|
||||
protected:
|
||||
void refresh();
|
||||
|
||||
struct EtcdServerStubs;
|
||||
struct EtcdServerStubsDeleter {
|
||||
void operator()(EtcdServerStubs *stubs);
|
||||
};
|
||||
std::unique_ptr<EtcdServerStubs, EtcdServerStubsDeleter> stubs;
|
||||
|
||||
private:
|
||||
// error handling
|
||||
std::exception_ptr eptr_;
|
||||
std::function<void (std::exception_ptr)> handler_;
|
||||
|
||||
// Don't use `pplx::task` to avoid sharing thread pool with other actions on the client
|
||||
// to avoid any potential blocking, which may block the keepalive loop and evict the lease.
|
||||
std::thread task_;
|
||||
|
||||
int ttl;
|
||||
int64_t lease_id;
|
||||
std::atomic_bool continue_next;
|
||||
#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_;
|
||||
struct EtcdServerStubs;
|
||||
struct EtcdServerStubsDeleter {
|
||||
void operator()(EtcdServerStubs* stubs);
|
||||
};
|
||||
}
|
||||
std::unique_ptr<EtcdServerStubs, EtcdServerStubsDeleter> stubs;
|
||||
|
||||
private:
|
||||
// error handling
|
||||
std::exception_ptr eptr_;
|
||||
std::function<void(std::exception_ptr)> handler_;
|
||||
|
||||
// Don't use `pplx::task` to avoid sharing thread pool with other actions on
|
||||
// the client to avoid any potential blocking, which may block the keepalive
|
||||
// loop and evict the lease.
|
||||
std::thread refresh_task_;
|
||||
|
||||
int ttl;
|
||||
int64_t lease_id;
|
||||
|
||||
// protect the initializing status of `timer`.
|
||||
std::mutex mutex_for_refresh_;
|
||||
std::condition_variable cv_for_refresh_;
|
||||
std::atomic_bool continue_next;
|
||||
|
||||
// grpc timeout in `refresh()`
|
||||
mutable std::chrono::microseconds grpc_timeout =
|
||||
std::chrono::microseconds::zero();
|
||||
};
|
||||
} // namespace etcd
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,170 +1,262 @@
|
|||
#ifndef __ETCD_RESPONSE_HPP__
|
||||
#define __ETCD_RESPONSE_HPP__
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "pplx/pplxtasks.h"
|
||||
|
||||
#include "etcd/Value.hpp"
|
||||
#include "kv.pb.h"
|
||||
#include "etcd/v3/Member.hpp"
|
||||
|
||||
namespace etcdv3 {
|
||||
class AsyncWatchAction;
|
||||
class AsyncLeaseKeepAliveAction;
|
||||
class V3Response;
|
||||
}
|
||||
class AsyncWatchAction;
|
||||
class AsyncLeaseKeepAliveAction;
|
||||
class AsyncObserveAction;
|
||||
class V3Response;
|
||||
} // namespace etcdv3
|
||||
|
||||
namespace etcd
|
||||
{
|
||||
typedef std::vector<std::string> Keys;
|
||||
namespace etcd {
|
||||
typedef std::vector<std::string> Keys;
|
||||
|
||||
namespace detail {
|
||||
// Compute the duration between given start time point and now
|
||||
inline const std::chrono::microseconds duration_till_now(
|
||||
std::chrono::high_resolution_clock::time_point const& start_timepoint) {
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now() - start_timepoint);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// forward declaration
|
||||
class KeepAlive;
|
||||
class Watcher;
|
||||
|
||||
/**
|
||||
* The Response object received for the requests of etcd::Client
|
||||
*/
|
||||
class Response {
|
||||
public:
|
||||
template <typename T>
|
||||
static etcd::Response create(std::unique_ptr<T> call) {
|
||||
call->waitForResponse();
|
||||
auto v3resp = call->ParseResponse();
|
||||
return etcd::Response(v3resp,
|
||||
detail::duration_till_now(call->startTimepoint()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static etcd::Response create(std::shared_ptr<T> call) {
|
||||
call->waitForResponse();
|
||||
auto v3resp = call->ParseResponse();
|
||||
return etcd::Response(v3resp,
|
||||
detail::duration_till_now(call->startTimepoint()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static etcd::Response create(std::unique_ptr<T> call,
|
||||
std::function<void(Response)> callback) {
|
||||
call->waitForResponse(callback);
|
||||
auto v3resp = call->ParseResponse();
|
||||
return etcd::Response(v3resp,
|
||||
detail::duration_till_now(call->startTimepoint()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static etcd::Response create(std::function<std::unique_ptr<T>()> fn) {
|
||||
auto call = fn();
|
||||
|
||||
call->waitForResponse();
|
||||
auto v3resp = call->ParseResponse();
|
||||
return etcd::Response(v3resp,
|
||||
detail::duration_till_now(call->startTimepoint()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static etcd::Response create(std::function<std::shared_ptr<T>()> fn) {
|
||||
auto call = fn();
|
||||
|
||||
call->waitForResponse();
|
||||
auto v3resp = call->ParseResponse();
|
||||
return etcd::Response(v3resp,
|
||||
detail::duration_till_now(call->startTimepoint()));
|
||||
}
|
||||
|
||||
Response();
|
||||
|
||||
Response(const Response&);
|
||||
|
||||
/**
|
||||
* The Reponse object received for the requests of etcd::Client
|
||||
* Returns the error code received from the etcd server. In case of success
|
||||
* the error code is 0.
|
||||
*/
|
||||
class Response
|
||||
{
|
||||
public:
|
||||
int error_code() const;
|
||||
|
||||
template <typename T>
|
||||
static pplx::task<etcd::Response> create(std::shared_ptr<T> call)
|
||||
{
|
||||
return pplx::task<etcd::Response>([call]()
|
||||
{
|
||||
call->waitForResponse();
|
||||
auto v3resp = call->ParseResponse();
|
||||
/**
|
||||
* Returns the string representation of the error code
|
||||
*/
|
||||
std::string const& error_message() const;
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now() - call->startTimepoint());
|
||||
return etcd::Response(v3resp, duration);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Returns true if this is a successful response
|
||||
*/
|
||||
bool is_ok() const;
|
||||
|
||||
template <typename T>
|
||||
static pplx::task<etcd::Response> create(std::function<std::shared_ptr<T>()> callfn)
|
||||
{
|
||||
return pplx::task<etcd::Response>([callfn]()
|
||||
{
|
||||
auto call = callfn();
|
||||
/**
|
||||
* Returns true if the error is a network unavailable error.
|
||||
*/
|
||||
bool is_network_unavailable() const;
|
||||
|
||||
call->waitForResponse();
|
||||
auto v3resp = call->ParseResponse();
|
||||
/**
|
||||
* Check whether the response contains a grpc TIMEOUT error.
|
||||
*/
|
||||
bool is_grpc_timeout() const;
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now() - call->startTimepoint());
|
||||
return etcd::Response(v3resp, duration);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Returns the action type of the operation that this response belongs to.
|
||||
*/
|
||||
std::string const& action() const;
|
||||
|
||||
template <typename T>
|
||||
static etcd::Response create_sync(std::shared_ptr<T> call)
|
||||
{
|
||||
call->waitForResponse();
|
||||
auto v3resp = call->ParseResponse();
|
||||
/**
|
||||
* Returns the current index value of etcd
|
||||
*/
|
||||
int64_t index() const;
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now() - call->startTimepoint());
|
||||
return etcd::Response(v3resp, duration);
|
||||
}
|
||||
/**
|
||||
* Returns the value object of the response to a get/set/modify operation.
|
||||
*/
|
||||
Value const& value() const;
|
||||
|
||||
Response();
|
||||
/**
|
||||
* Returns the previous value object of the response to a set/modify/rm
|
||||
* operation.
|
||||
*/
|
||||
Value const& prev_value() const;
|
||||
|
||||
/**
|
||||
* Returns true if this is a successful response
|
||||
*/
|
||||
bool is_ok() const;
|
||||
/**
|
||||
* Returns the index-th value of the response to an 'ls' operation. Equivalent
|
||||
* to values()[index]
|
||||
*/
|
||||
Value const& value(int index) const;
|
||||
|
||||
/**
|
||||
* Returns true if the error is a network unavailable error.
|
||||
*/
|
||||
bool is_network_unavailable() const;
|
||||
/**
|
||||
* Returns the vector of values in a directory in response to an 'ls'
|
||||
* operation.
|
||||
*/
|
||||
Values const& values() const;
|
||||
|
||||
/**
|
||||
* Returns the error code received from the etcd server. In case of success the error code is 0.
|
||||
*/
|
||||
int error_code() const;
|
||||
/**
|
||||
* Returns the vector of keys in a directory in response to an 'ls' operation.
|
||||
*/
|
||||
Keys const& keys() const;
|
||||
|
||||
/**
|
||||
* Returns the string representation of the error code
|
||||
*/
|
||||
std::string const & error_message() const;
|
||||
/**
|
||||
* Returns the index-th key in a directory listing. Same as keys()[index]
|
||||
*/
|
||||
std::string const& key(int index) const;
|
||||
|
||||
/**
|
||||
* Returns the action type of the operation that this response belongs to.
|
||||
*/
|
||||
std::string const & action() const;
|
||||
/**
|
||||
* Returns the compact_revision if the response is a watch-cancelled revision.
|
||||
* `-1` means uninitialized (the response is not watch-cancelled)
|
||||
*/
|
||||
int64_t compact_revision() const;
|
||||
|
||||
/**
|
||||
* Returns the current index value of etcd
|
||||
*/
|
||||
int index() 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 value object of the response to a get/set/modify operation.
|
||||
*/
|
||||
Value const & value() const;
|
||||
/**
|
||||
* Returns the lock key.
|
||||
*/
|
||||
std::string const& lock_key() const;
|
||||
|
||||
/**
|
||||
* Returns the previous value object of the response to a set/modify/rm operation.
|
||||
*/
|
||||
Value const & prev_value() const;
|
||||
/**
|
||||
* Return the "name" in response.
|
||||
*/
|
||||
std::string const& name() const;
|
||||
|
||||
/**
|
||||
* Returns the index-th value of the response to an 'ls' operation. Equivalent to values()[index]
|
||||
*/
|
||||
Value const & value(int index) const;
|
||||
/**
|
||||
* Returns the watched events.
|
||||
*/
|
||||
std::vector<Event> const& events() const;
|
||||
|
||||
/**
|
||||
* Returns the vector of values in a directory in response to an 'ls' operation.
|
||||
*/
|
||||
Values const & values() const;
|
||||
/**
|
||||
* Returns the duration of request execution in microseconds.
|
||||
*/
|
||||
std::chrono::microseconds const& duration() const;
|
||||
|
||||
/**
|
||||
* Returns the vector of keys in a directory in response to an 'ls' operation.
|
||||
*/
|
||||
Keys const & keys() const;
|
||||
/**
|
||||
* Returns the current cluster id.
|
||||
*/
|
||||
uint64_t cluster_id() const;
|
||||
|
||||
/**
|
||||
* Returns the index-th key in a directory listing. Same as keys()[index]
|
||||
*/
|
||||
std::string const & key(int index) const;
|
||||
/**
|
||||
* Returns the current member id.
|
||||
*/
|
||||
uint64_t member_id() const;
|
||||
|
||||
/**
|
||||
* Returns the lock key.
|
||||
*/
|
||||
std::string const & lock_key() const;
|
||||
/**
|
||||
* Returns ther current raft term.
|
||||
*/
|
||||
uint64_t raft_term() const;
|
||||
|
||||
/**
|
||||
* Returns the watched events.
|
||||
*/
|
||||
std::vector<mvccpb::Event> const & events() const;
|
||||
/**
|
||||
* Returns ther current raft term.
|
||||
*/
|
||||
std::vector<int64_t> const& leases() const;
|
||||
|
||||
/**
|
||||
* Returns the duration of request execution in microseconds.
|
||||
*/
|
||||
std::chrono::microseconds const & duration() const;
|
||||
/**
|
||||
* Returns the member list.
|
||||
*/
|
||||
std::vector<etcdv3::Member> const& members() const;
|
||||
|
||||
protected:
|
||||
Response(const etcdv3::V3Response& response, std::chrono::microseconds const& duration);
|
||||
Response(int error_code, char const * error_message);
|
||||
protected:
|
||||
Response(const etcdv3::V3Response& response,
|
||||
std::chrono::microseconds const& duration);
|
||||
Response(int error_code, std::string const& error_message);
|
||||
Response(int error_code, char const* error_message);
|
||||
|
||||
int _error_code;
|
||||
std::string _error_message;
|
||||
int _index;
|
||||
std::string _action;
|
||||
Value _value;
|
||||
Value _prev_value;
|
||||
Values _values;
|
||||
Keys _keys;
|
||||
std::string _lock_key; // for lock
|
||||
std::vector<mvccpb::Event> _events; // for watch
|
||||
std::chrono::microseconds _duration; // execute duration (in microseconds), during the action created and response parsed
|
||||
friend class SyncClient;
|
||||
friend class etcdv3::AsyncWatchAction;
|
||||
friend class etcdv3::AsyncLeaseKeepAliveAction;
|
||||
friend class Client;
|
||||
};
|
||||
}
|
||||
int _error_code;
|
||||
std::string _error_message;
|
||||
int64_t _index;
|
||||
std::string _action;
|
||||
Value _value;
|
||||
Value _prev_value;
|
||||
Values _values;
|
||||
Keys _keys;
|
||||
int64_t _compact_revision = -1; // for watch
|
||||
int64_t _watch_id = -1; // for watch
|
||||
std::string _lock_key; // for lock
|
||||
std::string _name; // for campaign (in v3election)
|
||||
std::vector<Event> _events; // for watch
|
||||
// execute duration (in microseconds), during the action created and response
|
||||
// parsed
|
||||
std::chrono::microseconds _duration;
|
||||
|
||||
// cluster metadata
|
||||
uint64_t _cluster_id;
|
||||
uint64_t _member_id;
|
||||
uint64_t _raft_term;
|
||||
|
||||
// for lease list
|
||||
std::vector<int64_t> _leases;
|
||||
|
||||
// for member list
|
||||
std::vector<etcdv3::Member> _members;
|
||||
|
||||
friend class Client;
|
||||
friend class SyncClient;
|
||||
friend class KeepAlive;
|
||||
friend class Watcher;
|
||||
|
||||
friend class etcdv3::AsyncWatchAction;
|
||||
friend class etcdv3::AsyncLeaseKeepAliveAction;
|
||||
friend class etcdv3::AsyncObserveAction;
|
||||
};
|
||||
} // namespace etcd
|
||||
|
||||
#endif
|
||||
|
|
|
|||
1015
etcd/SyncClient.hpp
1015
etcd/SyncClient.hpp
File diff suppressed because it is too large
Load Diff
162
etcd/Value.hpp
162
etcd/Value.hpp
|
|
@ -5,67 +5,131 @@
|
|||
#include <vector>
|
||||
|
||||
namespace etcdv3 {
|
||||
class KeyValue;
|
||||
class KeyValue;
|
||||
}
|
||||
|
||||
namespace etcd
|
||||
{
|
||||
namespace mvccpb {
|
||||
class KeyValue;
|
||||
class Event;
|
||||
} // namespace mvccpb
|
||||
|
||||
namespace electionpb {
|
||||
class LeaderKey;
|
||||
}
|
||||
|
||||
namespace etcd {
|
||||
class Value;
|
||||
class Event;
|
||||
class Response;
|
||||
class Client;
|
||||
class SyncClient;
|
||||
|
||||
/**
|
||||
* Represents a value object received from the etcd server
|
||||
*/
|
||||
class Value {
|
||||
public:
|
||||
/**
|
||||
* Represents a value object received from the etcd server
|
||||
* Returns true if this value represents a directory on the server. If true
|
||||
* the as_string() method is meaningless.
|
||||
*/
|
||||
class Value
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns true if this value represents a directory on the server. If true the as_string()
|
||||
* method is meaningless.
|
||||
*/
|
||||
bool is_dir() const;
|
||||
bool is_dir() const;
|
||||
|
||||
/**
|
||||
* Returns the key of this value as an "absolute path".
|
||||
*/
|
||||
std::string const & key() const;
|
||||
/**
|
||||
* Returns the key of this value as an "absolute path".
|
||||
*/
|
||||
std::string const& key() const;
|
||||
|
||||
/**
|
||||
* Returns the string representation of the value
|
||||
*/
|
||||
std::string const & as_string() const;
|
||||
/**
|
||||
* Returns the string representation of the value
|
||||
*/
|
||||
std::string const& as_string() const;
|
||||
|
||||
/**
|
||||
* Returns the creation index of this value.
|
||||
*/
|
||||
int created_index() const;
|
||||
/**
|
||||
* Returns the creation index of this value.
|
||||
*/
|
||||
int64_t created_index() const;
|
||||
|
||||
/**
|
||||
* Returns the last modification's index of this value.
|
||||
*/
|
||||
int modified_index() const;
|
||||
/**
|
||||
* Returns the last modification's index of this value.
|
||||
*/
|
||||
int64_t modified_index() const;
|
||||
|
||||
/**
|
||||
* Returns the ttl of this value or 0 if ttl is not set
|
||||
*/
|
||||
int ttl() const;
|
||||
/**
|
||||
* Returns the version of this value.
|
||||
*/
|
||||
int64_t version() const;
|
||||
|
||||
int64_t lease() const;
|
||||
/**
|
||||
* Returns the ttl of this value or 0 if ttl is not set
|
||||
*/
|
||||
int ttl() const;
|
||||
|
||||
protected:
|
||||
friend class Response;
|
||||
friend class BaseResponse; //deliberately done since Value class will be removed during full V3
|
||||
friend class DeleteRpcResponse;
|
||||
friend class AsyncDeleteResponse;
|
||||
Value();
|
||||
Value(etcdv3::KeyValue const & kvs);
|
||||
std::string _key;
|
||||
bool dir;
|
||||
std::string value;
|
||||
int created;
|
||||
int modified;
|
||||
int _ttl;
|
||||
int64_t leaseId;
|
||||
int64_t lease() const;
|
||||
|
||||
protected:
|
||||
friend class Client;
|
||||
friend class SyncClient;
|
||||
friend class Response;
|
||||
friend class BaseResponse; // deliberately done since Value class will be
|
||||
// removed during full V3
|
||||
friend class DeleteRpcResponse;
|
||||
friend class AsyncDeleteResponse;
|
||||
|
||||
friend class Event;
|
||||
|
||||
Value();
|
||||
Value(etcdv3::KeyValue const& kvs);
|
||||
Value(mvccpb::KeyValue const& kvs);
|
||||
std::string _key;
|
||||
bool dir;
|
||||
std::string value;
|
||||
int64_t created;
|
||||
int64_t modified;
|
||||
int64_t _version;
|
||||
int _ttl;
|
||||
int64_t leaseId;
|
||||
};
|
||||
|
||||
using Values = std::vector<Value>;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Value& value);
|
||||
|
||||
class Event {
|
||||
public:
|
||||
enum class EventType {
|
||||
PUT,
|
||||
DELETE_,
|
||||
INVALID,
|
||||
};
|
||||
|
||||
typedef std::vector<Value> Values;
|
||||
}
|
||||
enum EventType event_type() const;
|
||||
|
||||
bool has_kv() const;
|
||||
|
||||
bool has_prev_kv() const;
|
||||
|
||||
const Value& kv() const;
|
||||
|
||||
const Value& prev_kv() const;
|
||||
|
||||
protected:
|
||||
friend class Response;
|
||||
|
||||
Event(mvccpb::Event const& event);
|
||||
|
||||
private:
|
||||
enum EventType event_type_;
|
||||
Value _kv, _prev_kv;
|
||||
bool _has_kv, _has_prev_kv;
|
||||
};
|
||||
|
||||
using Events = std::vector<Event>;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Event::EventType& value);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Event& event);
|
||||
|
||||
} // namespace etcd
|
||||
|
||||
#endif
|
||||
|
|
|
|||
269
etcd/Watcher.hpp
269
etcd/Watcher.hpp
|
|
@ -6,101 +6,204 @@
|
|||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "etcd/Client.hpp"
|
||||
#include "etcd/Response.hpp"
|
||||
|
||||
namespace etcd
|
||||
{
|
||||
class Watcher
|
||||
{
|
||||
public:
|
||||
Watcher(Client const &client, std::string const & key,
|
||||
std::function<void(Response)> callback, bool recursive=false);
|
||||
Watcher(Client const &client, std::string const & key,
|
||||
std::string const &range_end,
|
||||
std::function<void(Response)> callback);
|
||||
Watcher(Client const &client, std::string const & key, int fromIndex,
|
||||
std::function<void(Response)> callback, bool recursive=false);
|
||||
Watcher(Client const &client, std::string const & key,
|
||||
std::string const &range_end, int fromIndex,
|
||||
std::function<void(Response)> callback);
|
||||
Watcher(std::string const & address, std::string const & key,
|
||||
std::function<void(Response)> callback, bool recursive=false);
|
||||
Watcher(std::string const & address, std::string const & key,
|
||||
std::string const &range_end,
|
||||
std::function<void(Response)> callback);
|
||||
Watcher(std::string const & address, std::string const & key, int fromIndex,
|
||||
std::function<void(Response)> callback, bool recursive=false);
|
||||
Watcher(std::string const & address, std::string const & key,
|
||||
std::string const &range_end, int fromIndex,
|
||||
std::function<void(Response)> callback);
|
||||
Watcher(std::string const & address,
|
||||
std::string const & username, std::string const & password,
|
||||
std::string const & key,
|
||||
std::function<void(Response)> callback, bool recursive=false);
|
||||
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);
|
||||
Watcher(std::string const & address,
|
||||
std::string const & username, std::string const & password,
|
||||
std::string const & key, int fromIndex,
|
||||
std::function<void(Response)> callback, bool recursive=false);
|
||||
Watcher(std::string const & address,
|
||||
std::string const & username, std::string const & password,
|
||||
std::string const & key, std::string const &range_end, int fromIndex,
|
||||
std::function<void(Response)> callback);
|
||||
namespace etcd {
|
||||
// forward declaration to avoid header/library dependency
|
||||
class Client;
|
||||
|
||||
Watcher(Watcher const &) = delete;
|
||||
Watcher(Watcher &&) = delete;
|
||||
class Watcher {
|
||||
public:
|
||||
Watcher(Client const& client, std::string const& key,
|
||||
std::function<void(Response)> callback, bool recursive = false);
|
||||
Watcher(SyncClient const& client, std::string const& key,
|
||||
std::function<void(Response)> callback, bool recursive = false);
|
||||
Watcher(Client const& client, std::string const& key,
|
||||
std::string const& range_end, std::function<void(Response)> callback);
|
||||
Watcher(SyncClient const& client, std::string const& key,
|
||||
std::string const& range_end, std::function<void(Response)> callback);
|
||||
Watcher(Client const& client, std::string const& key, int64_t fromIndex,
|
||||
std::function<void(Response)> callback, bool recursive = false);
|
||||
Watcher(SyncClient const& client, std::string const& key, int64_t fromIndex,
|
||||
std::function<void(Response)> callback, bool recursive = false);
|
||||
Watcher(Client const& client, std::string const& key,
|
||||
std::string const& range_end, int64_t fromIndex,
|
||||
std::function<void(Response)> callback);
|
||||
Watcher(SyncClient const& client, std::string const& key,
|
||||
std::string const& range_end, int64_t fromIndex,
|
||||
std::function<void(Response)> callback);
|
||||
Watcher(std::string const& address, std::string const& key,
|
||||
std::function<void(Response)> callback, bool recursive = false);
|
||||
Watcher(std::string const& address, std::string const& key,
|
||||
std::string const& range_end, std::function<void(Response)> callback);
|
||||
Watcher(std::string const& address, std::string const& key, int64_t fromIndex,
|
||||
std::function<void(Response)> callback, bool recursive = false);
|
||||
Watcher(std::string const& address, std::string const& key,
|
||||
std::string const& range_end, int64_t fromIndex,
|
||||
std::function<void(Response)> callback);
|
||||
Watcher(std::string const& address, std::string const& username,
|
||||
std::string const& password, std::string const& key,
|
||||
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, std::function<void(Response)> callback,
|
||||
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,
|
||||
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, bool recursive = false,
|
||||
std::string const& target_name_override = "");
|
||||
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 = "");
|
||||
|
||||
/**
|
||||
* Wait util the task has been stopped, actively or passively, e.g., the watcher
|
||||
* get cancelled or the server closes the connection.
|
||||
*
|
||||
* Returns true if the watcher is been normally cancalled, otherwise false.
|
||||
*/
|
||||
bool Wait();
|
||||
Watcher(Client const& client, std::string const& key,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive = false);
|
||||
Watcher(SyncClient const& client, std::string const& key,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive = false);
|
||||
Watcher(Client const& client, std::string const& key,
|
||||
std::string const& range_end, std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback);
|
||||
Watcher(SyncClient const& client, std::string const& key,
|
||||
std::string const& range_end, std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback);
|
||||
Watcher(Client const& client, std::string const& key, int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive = false);
|
||||
Watcher(SyncClient const& client, std::string const& key, int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive = false);
|
||||
Watcher(Client const& client, std::string const& key,
|
||||
std::string const& range_end, int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback);
|
||||
Watcher(SyncClient const& client, std::string const& key,
|
||||
std::string const& range_end, int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback);
|
||||
Watcher(std::string const& address, std::string const& key,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive = false);
|
||||
Watcher(std::string const& address, std::string const& key,
|
||||
std::string const& range_end, std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback);
|
||||
Watcher(std::string const& address, std::string const& key, int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive = false);
|
||||
Watcher(std::string const& address, std::string const& key,
|
||||
std::string const& range_end, int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback);
|
||||
Watcher(std::string const& address, std::string const& username,
|
||||
std::string const& password, std::string const& key,
|
||||
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,
|
||||
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,
|
||||
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 = "");
|
||||
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 = "");
|
||||
|
||||
/**
|
||||
* 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 cancalled.
|
||||
*/
|
||||
void Wait(std::function<void(bool)> callback);
|
||||
Watcher(Watcher const&) = delete;
|
||||
Watcher(Watcher&&) = delete;
|
||||
|
||||
/**
|
||||
* Stop the watching action.
|
||||
*/
|
||||
void Cancel();
|
||||
/**
|
||||
* Wait util the task has been stopped, actively or passively, e.g., the
|
||||
* watcher get cancelled or the server closes the connection.
|
||||
*
|
||||
* Returns true if the watcher is been normally cancelled, otherwise false.
|
||||
*/
|
||||
bool Wait();
|
||||
|
||||
~Watcher();
|
||||
/**
|
||||
* 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
|
||||
* cancelled.
|
||||
*
|
||||
* 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
|
||||
* watcher may have been destroyed.
|
||||
*
|
||||
* @return true if the callback has been set successfully (no existing
|
||||
* callback).
|
||||
*/
|
||||
bool Wait(std::function<void(bool)> callback);
|
||||
|
||||
protected:
|
||||
void doWatch(std::string const & key,
|
||||
std::string const & range_end,
|
||||
std::string const & auth_token,
|
||||
std::function<void(Response)> callback);
|
||||
/**
|
||||
* Stop the watching action.
|
||||
*/
|
||||
bool Cancel();
|
||||
|
||||
int index;
|
||||
std::function<void(Response)> callback;
|
||||
std::function<void(bool)> wait_callback;
|
||||
/**
|
||||
* Whether the watcher has been cancelled.
|
||||
*/
|
||||
bool Cancelled() const;
|
||||
|
||||
// Don't use `pplx::task` to avoid sharing thread pool with other actions on the client
|
||||
// to avoid any potential blocking, which may block the keepalive loop and evict the lease.
|
||||
std::thread task_;
|
||||
~Watcher();
|
||||
|
||||
struct EtcdServerStubs;
|
||||
struct EtcdServerStubsDeleter {
|
||||
void operator()(etcd::Watcher::EtcdServerStubs *stubs);
|
||||
};
|
||||
std::unique_ptr<EtcdServerStubs, EtcdServerStubsDeleter> stubs;
|
||||
protected:
|
||||
void doWatch(std::string const& key, std::string const& range_end,
|
||||
std::string const& auth_token,
|
||||
std::function<void(Response)> callback);
|
||||
|
||||
private:
|
||||
int fromIndex;
|
||||
bool recursive;
|
||||
std::atomic_bool cancelled;
|
||||
std::function<void(Response)> callback;
|
||||
std::function<void(bool)> wait_callback;
|
||||
|
||||
// Don't use `pplx::task` to avoid sharing thread pool with other actions on
|
||||
// the client to avoid any potential blocking, which may block the keepalive
|
||||
// loop and evict the lease.
|
||||
std::thread task_;
|
||||
|
||||
struct EtcdServerStubs;
|
||||
struct EtcdServerStubsDeleter {
|
||||
void operator()(etcd::Watcher::EtcdServerStubs* stubs);
|
||||
};
|
||||
}
|
||||
std::unique_ptr<EtcdServerStubs, EtcdServerStubsDeleter> stubs;
|
||||
|
||||
private:
|
||||
int64_t fromIndex;
|
||||
bool recursive;
|
||||
std::atomic_bool cancelled;
|
||||
};
|
||||
} // namespace etcd
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -2,64 +2,116 @@
|
|||
#define __V3_ACTION_HPP__
|
||||
|
||||
#include <chrono>
|
||||
#include <ostream>
|
||||
|
||||
#include <grpc++/grpc++.h>
|
||||
#include "proto/rpc.grpc.pb.h"
|
||||
#include "proto/v3election.grpc.pb.h"
|
||||
#include "proto/v3lock.grpc.pb.h"
|
||||
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
|
||||
using grpc::ClientContext;
|
||||
using grpc::CompletionQueue;
|
||||
using grpc::Status;
|
||||
|
||||
using etcdserverpb::Cluster;
|
||||
using etcdserverpb::KV;
|
||||
using etcdserverpb::Watch;
|
||||
using etcdserverpb::Lease;
|
||||
using etcdserverpb::Watch;
|
||||
using v3electionpb::Election;
|
||||
using v3lockpb::Lock;
|
||||
|
||||
namespace etcdv3
|
||||
{
|
||||
enum Atomicity_Type
|
||||
{
|
||||
PREV_INDEX = 0,
|
||||
PREV_VALUE = 1
|
||||
};
|
||||
namespace etcd {
|
||||
class Response;
|
||||
}
|
||||
|
||||
struct ActionParameters
|
||||
{
|
||||
ActionParameters();
|
||||
bool withPrefix;
|
||||
int revision;
|
||||
int old_revision;
|
||||
int64_t lease_id;
|
||||
int ttl;
|
||||
int limit;
|
||||
std::string key;
|
||||
std::string range_end;
|
||||
std::string value;
|
||||
std::string old_value;
|
||||
std::string auth_token;
|
||||
KV::Stub* kv_stub;
|
||||
Watch::Stub* watch_stub;
|
||||
Lease::Stub* lease_stub;
|
||||
Lock::Stub* lock_stub;
|
||||
};
|
||||
namespace etcdv3 {
|
||||
enum class AtomicityType { PREV_INDEX = 0, PREV_VALUE = 1 };
|
||||
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
Action(etcdv3::ActionParameters params);
|
||||
void waitForResponse();
|
||||
const std::chrono::high_resolution_clock::time_point startTimepoint();
|
||||
protected:
|
||||
Status status;
|
||||
ClientContext context;
|
||||
CompletionQueue cq_;
|
||||
etcdv3::ActionParameters parameters;
|
||||
std::chrono::high_resolution_clock::time_point start_timepoint;
|
||||
};
|
||||
struct ActionParameters {
|
||||
ActionParameters();
|
||||
bool withPrefix;
|
||||
int64_t revision = 0;
|
||||
int64_t old_revision = 0;
|
||||
int64_t lease_id = 0; // no lease
|
||||
int ttl;
|
||||
int limit;
|
||||
std::string name; // for campaign (in v3election)
|
||||
std::string key;
|
||||
std::string range_end;
|
||||
bool keys_only;
|
||||
bool count_only;
|
||||
std::string value;
|
||||
std::string old_value;
|
||||
std::string auth_token;
|
||||
|
||||
namespace detail {
|
||||
std::string string_plus_one(std::string const &value);
|
||||
// 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();
|
||||
KV::Stub* kv_stub;
|
||||
Watch::Stub* watch_stub;
|
||||
Cluster::Stub* cluster_stub;
|
||||
Lease::Stub* lease_stub;
|
||||
Lock::Stub* lock_stub;
|
||||
Election::Stub* election_stub;
|
||||
|
||||
bool has_grpc_timeout() const;
|
||||
std::chrono::system_clock::time_point grpc_deadline() const;
|
||||
|
||||
void dump(std::ostream& os) const;
|
||||
};
|
||||
|
||||
class Action {
|
||||
public:
|
||||
Action(etcdv3::ActionParameters const& params);
|
||||
Action(etcdv3::ActionParameters&& params);
|
||||
virtual ~Action();
|
||||
|
||||
void waitForResponse();
|
||||
const std::chrono::high_resolution_clock::time_point startTimepoint();
|
||||
|
||||
protected:
|
||||
Status status;
|
||||
ClientContext context;
|
||||
CompletionQueue cq_;
|
||||
etcdv3::ActionParameters parameters;
|
||||
std::chrono::high_resolution_clock::time_point start_timepoint;
|
||||
|
||||
private:
|
||||
// Init things like auth token, etc.
|
||||
void InitAction();
|
||||
|
||||
friend class etcd::Response;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
std::string string_plus_one(std::string const& value);
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
#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 param, etcdv3::Atomicity_Type type);
|
||||
AsyncTxnResponse ParseResponse();
|
||||
private:
|
||||
TxnResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#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 param, etcdv3::Atomicity_Type type);
|
||||
AsyncTxnResponse ParseResponse();
|
||||
private:
|
||||
TxnResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#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/AsyncDeleteRangeResponse.hpp"
|
||||
|
||||
|
||||
using grpc::ClientAsyncResponseReader;
|
||||
using etcdserverpb::DeleteRangeResponse;
|
||||
|
||||
namespace etcdv3
|
||||
{
|
||||
class AsyncDeleteAction : public etcdv3::Action
|
||||
{
|
||||
public:
|
||||
AsyncDeleteAction(etcdv3::ActionParameters param);
|
||||
AsyncDeleteRangeResponse ParseResponse();
|
||||
private:
|
||||
DeleteRangeResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<DeleteRangeResponse>> response_reader;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#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 AsyncDeleteRangeResponse : public etcdv3::V3Response
|
||||
{
|
||||
public:
|
||||
AsyncDeleteRangeResponse(){};
|
||||
void ParseResponse(std::string const& key, bool prefix, DeleteRangeResponse& resp);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,491 @@
|
|||
#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
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef __ASYNC_GET_HPP__
|
||||
#define __ASYNC_GET_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 AsyncGetAction : public etcdv3::Action
|
||||
{
|
||||
public:
|
||||
AsyncGetAction(etcdv3::ActionParameters param);
|
||||
AsyncRangeResponse ParseResponse();
|
||||
private:
|
||||
RangeResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<RangeResponse>> response_reader;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
#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 etcdv3
|
||||
{
|
||||
class AsyncLeaseGrantAction : public etcdv3::Action {
|
||||
public:
|
||||
AsyncLeaseGrantAction(etcdv3::ActionParameters param);
|
||||
AsyncLeaseGrantResponse ParseResponse();
|
||||
private:
|
||||
LeaseGrantResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<LeaseGrantResponse>> response_reader;
|
||||
};
|
||||
|
||||
class AsyncLeaseRevokeAction: public etcdv3::Action {
|
||||
public:
|
||||
AsyncLeaseRevokeAction(etcdv3::ActionParameters param);
|
||||
AsyncLeaseRevokeResponse ParseResponse();
|
||||
private:
|
||||
LeaseRevokeResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<LeaseRevokeResponse>> response_reader;
|
||||
};
|
||||
|
||||
class AsyncLeaseKeepAliveAction: public etcdv3::Action {
|
||||
public:
|
||||
AsyncLeaseKeepAliveAction(etcdv3::ActionParameters param);
|
||||
AsyncLeaseKeepAliveResponse ParseResponse();
|
||||
|
||||
etcd::Response Refresh();
|
||||
void CancelKeepAlive();
|
||||
bool Cancelled() const;
|
||||
|
||||
private:
|
||||
LeaseKeepAliveResponse reply;
|
||||
std::unique_ptr<ClientAsyncReaderWriter<LeaseKeepAliveRequest, LeaseKeepAliveResponse>> stream;
|
||||
|
||||
LeaseKeepAliveRequest req;
|
||||
bool isCancelled;
|
||||
std::mutex protect_is_cancelled;
|
||||
};
|
||||
|
||||
class AsyncLeaseTimeToLiveAction: public etcdv3::Action {
|
||||
public:
|
||||
AsyncLeaseTimeToLiveAction(etcdv3::ActionParameters param);
|
||||
AsyncLeaseTimeToLiveResponse ParseResponse();
|
||||
private:
|
||||
LeaseTimeToLiveResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<LeaseTimeToLiveResponse>> response_reader;
|
||||
};
|
||||
|
||||
class AsyncLeaseLeasesAction: public etcdv3::Action {
|
||||
public:
|
||||
AsyncLeaseLeasesAction(etcdv3::ActionParameters param);
|
||||
AsyncLeaseLeasesResponse ParseResponse();
|
||||
private:
|
||||
LeaseLeasesResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<LeaseLeasesResponse>> response_reader;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#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 param);
|
||||
AsyncLockResponse ParseResponse();
|
||||
private:
|
||||
LockResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<LockResponse>> response_reader;
|
||||
};
|
||||
|
||||
class AsyncUnlockAction : public etcdv3::Action
|
||||
{
|
||||
public:
|
||||
AsyncUnlockAction(etcdv3::ActionParameters param);
|
||||
AsyncUnlockResponse ParseResponse();
|
||||
private:
|
||||
UnlockResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<UnlockResponse>> response_reader;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
#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
|
||||
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#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 param, bool create=false);
|
||||
AsyncTxnResponse ParseResponse();
|
||||
private:
|
||||
TxnResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
|
||||
bool isCreate;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#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::TxnResponse;
|
||||
using etcdserverpb::KV;
|
||||
|
||||
namespace etcdv3
|
||||
{
|
||||
class AsyncTxnAction : public etcdv3::Action
|
||||
{
|
||||
public:
|
||||
AsyncTxnAction(etcdv3::ActionParameters param, etcdv3::Transaction const &tx);
|
||||
AsyncTxnResponse ParseResponse();
|
||||
private:
|
||||
TxnResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#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
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#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 param);
|
||||
AsyncTxnResponse ParseResponse();
|
||||
private:
|
||||
TxnResponse reply;
|
||||
std::unique_ptr<ClientAsyncResponseReader<TxnResponse>> response_reader;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#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 param);
|
||||
AsyncWatchResponse ParseResponse();
|
||||
void waitForResponse();
|
||||
void waitForResponse(std::function<void(etcd::Response)> callback);
|
||||
void CancelWatch();
|
||||
void WatchReq(std::string const & key);
|
||||
bool Cancelled() const;
|
||||
private:
|
||||
WatchResponse reply;
|
||||
std::unique_ptr<ClientAsyncReaderWriter<WatchRequest,WatchResponse>> stream;
|
||||
std::atomic_bool isCancelled;
|
||||
std::mutex protect_is_cancalled;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
#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
|
||||
|
||||
|
|
@ -3,18 +3,16 @@
|
|||
|
||||
#include "proto/kv.pb.h"
|
||||
|
||||
namespace etcdv3 {
|
||||
class KeyValue {
|
||||
public:
|
||||
KeyValue();
|
||||
mvccpb::KeyValue kvs;
|
||||
void set_ttl(int ttl);
|
||||
int get_ttl() const;
|
||||
|
||||
namespace etcdv3
|
||||
{
|
||||
class KeyValue
|
||||
{
|
||||
public:
|
||||
KeyValue();
|
||||
mvccpb::KeyValue kvs;
|
||||
void set_ttl(int ttl);
|
||||
int get_ttl() const;
|
||||
private:
|
||||
int ttl;
|
||||
};
|
||||
}
|
||||
private:
|
||||
int ttl;
|
||||
};
|
||||
} // namespace etcdv3
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
#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
|
||||
|
|
@ -1,42 +1,223 @@
|
|||
#ifndef V3_SRC_TRANSACTION_HPP_
|
||||
#define V3_SRC_TRANSACTION_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "txn.pb.h"
|
||||
|
||||
namespace etcdserverpb {
|
||||
class TxnRequest;
|
||||
class TxnRequest;
|
||||
}
|
||||
|
||||
namespace etcdv3 {
|
||||
|
||||
class Transaction {
|
||||
public:
|
||||
Transaction();
|
||||
Transaction(std::string const&);
|
||||
virtual ~Transaction();
|
||||
void init_compare(etcdserverpb::Compare::CompareResult, etcdserverpb::Compare::CompareTarget);
|
||||
void init_compare(std::string const &, etcdserverpb::Compare::CompareResult, etcdserverpb::Compare::CompareTarget);
|
||||
void init_compare(int, etcdserverpb::Compare::CompareResult, etcdserverpb::Compare::CompareTarget);
|
||||
|
||||
void setup_basic_failure_operation(std::string const &key);
|
||||
void setup_set_failure_operation(std::string const &key, std::string const &value, int64_t leaseid);
|
||||
void setup_basic_create_sequence(std::string const &key, std::string const &value, int64_t leaseid);
|
||||
void setup_compare_and_swap_sequence(std::string const &valueToSwap, int64_t leaseid);
|
||||
void setup_delete_sequence(std::string const &key, std::string const &range_end, bool recursive);
|
||||
void setup_delete_failure_operation(std::string const &key, std::string const &range_end, bool recursive);
|
||||
void setup_compare_and_delete_operation(std::string const& key);
|
||||
|
||||
// update without `get` and no `prev_kv` returned
|
||||
void setup_put(std::string const &key, std::string const &value);
|
||||
void setup_delete(std::string const &key);
|
||||
|
||||
std::unique_ptr<etcdserverpb::TxnRequest> txn_request;
|
||||
private:
|
||||
std::string key;
|
||||
enum class CompareResult {
|
||||
EQUAL = 0,
|
||||
GREATER = 1,
|
||||
LESS = 2,
|
||||
NOT_EQUAL = 3,
|
||||
};
|
||||
|
||||
}
|
||||
enum class CompareTarget {
|
||||
VERSION = 0,
|
||||
CREATE = 1,
|
||||
MOD = 2,
|
||||
VALUE = 3,
|
||||
LEASE = 4,
|
||||
};
|
||||
|
||||
class Transaction {
|
||||
public:
|
||||
Transaction();
|
||||
~Transaction();
|
||||
|
||||
union Value {
|
||||
int64_t version;
|
||||
int64_t create_revision;
|
||||
int64_t mod_revision;
|
||||
std::string value;
|
||||
int64_t lease;
|
||||
};
|
||||
|
||||
void add_compare(std::string const& key, CompareTarget const& target,
|
||||
CompareResult const& result, Value const& target_value,
|
||||
std::string const& range_end = "");
|
||||
|
||||
void add_compare_version(std::string const& key, int64_t const& version,
|
||||
std::string const& range_end = "");
|
||||
void add_compare_version(std::string const& key, CompareResult const& result,
|
||||
int64_t const& version,
|
||||
std::string const& range_end = "");
|
||||
void add_compare_create(std::string const& key,
|
||||
int64_t const& create_revision,
|
||||
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
|
||||
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, std::string const& range_end,
|
||||
const bool recursive = false);
|
||||
|
||||
std::shared_ptr<etcdserverpb::TxnRequest> txn_request;
|
||||
};
|
||||
|
||||
} // namespace etcdv3
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,41 +3,66 @@
|
|||
|
||||
#include <grpc++/grpc++.h>
|
||||
#include "proto/kv.pb.h"
|
||||
#include "proto/v3election.pb.h"
|
||||
|
||||
#include "etcd/v3/KeyValue.hpp"
|
||||
#include "etcd/v3/Member.hpp"
|
||||
|
||||
namespace etcdv3
|
||||
{
|
||||
class V3Response
|
||||
{
|
||||
public:
|
||||
V3Response(): error_code(0), index(0){};
|
||||
void set_error_code(int code);
|
||||
int get_error_code() const;
|
||||
std::string const & get_error_message() const;
|
||||
void set_error_message(std::string msg);
|
||||
void set_action(std::string action);
|
||||
int get_index() const;
|
||||
std::string const & get_action() const;
|
||||
std::vector<etcdv3::KeyValue> const & get_values() const;
|
||||
std::vector<etcdv3::KeyValue> const & get_prev_values() const;
|
||||
etcdv3::KeyValue const & get_value() const;
|
||||
etcdv3::KeyValue const & get_prev_value() const;
|
||||
bool has_values() const;
|
||||
void set_lock_key(std::string const &key);
|
||||
std::string const &get_lock_key() const;
|
||||
std::vector<mvccpb::Event> const & get_events() const;
|
||||
protected:
|
||||
int error_code;
|
||||
int index;
|
||||
std::string error_message;
|
||||
std::string action;
|
||||
etcdv3::KeyValue value;
|
||||
etcdv3::KeyValue prev_value;
|
||||
std::vector<etcdv3::KeyValue> values;
|
||||
std::vector<etcdv3::KeyValue> prev_values;
|
||||
std::string lock_key; // for lock
|
||||
std::vector<mvccpb::Event> events; // for watch
|
||||
};
|
||||
}
|
||||
namespace etcdv3 {
|
||||
class V3Response {
|
||||
public:
|
||||
V3Response() : error_code(0), index(0){};
|
||||
void set_error_code(int code);
|
||||
int get_error_code() const;
|
||||
std::string const& get_error_message() const;
|
||||
void set_error_message(std::string msg);
|
||||
void set_action(std::string action);
|
||||
int64_t get_index() const;
|
||||
std::string const& get_action() const;
|
||||
std::vector<etcdv3::KeyValue> const& get_values() const;
|
||||
std::vector<etcdv3::KeyValue> const& get_prev_values() const;
|
||||
etcdv3::KeyValue const& get_value() const;
|
||||
etcdv3::KeyValue const& get_prev_value() const;
|
||||
bool has_values() 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);
|
||||
std::string const& get_lock_key() const;
|
||||
void set_name(std::string const& name);
|
||||
std::string const& get_name() const;
|
||||
std::vector<mvccpb::Event> const& get_events() const;
|
||||
uint64_t get_cluster_id() const;
|
||||
uint64_t get_member_id() const;
|
||||
uint64_t get_raft_term() const;
|
||||
std::vector<int64_t> const& get_leases() const;
|
||||
std::vector<etcdv3::Member> const& get_members() const;
|
||||
|
||||
protected:
|
||||
int error_code;
|
||||
int64_t index;
|
||||
std::string error_message;
|
||||
std::string action;
|
||||
etcdv3::KeyValue value;
|
||||
etcdv3::KeyValue prev_value;
|
||||
std::vector<etcdv3::KeyValue> values;
|
||||
std::vector<etcdv3::KeyValue> prev_values;
|
||||
int64_t compact_revision = -1;
|
||||
int64_t watch_id = -1;
|
||||
std::string lock_key; // for lock
|
||||
std::string name; // for campaign (in v3election)
|
||||
std::vector<mvccpb::Event> events; // for watch
|
||||
|
||||
// cluster metadata
|
||||
uint64_t cluster_id;
|
||||
uint64_t member_id;
|
||||
uint64_t raft_term;
|
||||
|
||||
// for lease list
|
||||
std::vector<int64_t> leases;
|
||||
// for member list
|
||||
std::vector<etcdv3::Member> members;
|
||||
};
|
||||
} // namespace etcdv3
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,18 +1,77 @@
|
|||
#ifndef __ETCD_ACTION_CONSTANTS_HPP__
|
||||
#define __ETCD_ACTION_CONSTANTS_HPP__
|
||||
|
||||
namespace etcdv3
|
||||
{
|
||||
extern char const * CREATE_ACTION;
|
||||
extern char const * UPDATE_ACTION;
|
||||
extern char const * SET_ACTION;
|
||||
extern char const * GET_ACTION;
|
||||
extern char const * DELETE_ACTION;
|
||||
extern char const * COMPARESWAP_ACTION;
|
||||
extern char const * COMPAREDELETE_ACTION;
|
||||
extern char const * LOCK_ACTION;
|
||||
extern char const * UNLOCK_ACTION;
|
||||
extern char const * TXN_ACTION;
|
||||
}
|
||||
#include <string>
|
||||
|
||||
namespace etcdv3 {
|
||||
extern char const* CREATE_ACTION;
|
||||
extern char const* UPDATE_ACTION;
|
||||
extern char const* SET_ACTION;
|
||||
extern char const* GET_ACTION;
|
||||
extern char const* PUT_ACTION;
|
||||
extern char const* DELETE_ACTION;
|
||||
extern char const* COMPARESWAP_ACTION;
|
||||
extern char const* COMPAREDELETE_ACTION;
|
||||
extern char const* LOCK_ACTION;
|
||||
extern char const* UNLOCK_ACTION;
|
||||
extern char const* TXN_ACTION;
|
||||
extern char const* WATCH_ACTION;
|
||||
|
||||
extern char const* LEASEGRANT;
|
||||
extern char const* LEASEREVOKE;
|
||||
extern char const* LEASEKEEPALIVE;
|
||||
extern char const* LEASETIMETOLIVE;
|
||||
extern char const* LEASELEASES;
|
||||
|
||||
extern char const* ADDMEMBER;
|
||||
extern char const* LISTMEMBER;
|
||||
extern char const* REMOVEMEMBER;
|
||||
|
||||
extern char const* CAMPAIGN_ACTION;
|
||||
extern char const* PROCLAIM_ACTION;
|
||||
extern char const* LEADER_ACTION;
|
||||
extern char const* OBSERVE_ACTION;
|
||||
extern char const* RESIGN_ACTION;
|
||||
|
||||
extern std::string const NUL;
|
||||
|
||||
extern char const* KEEPALIVE_CREATE;
|
||||
extern char const* KEEPALIVE_WRITE;
|
||||
extern char const* KEEPALIVE_READ;
|
||||
extern char const* KEEPALIVE_DONE;
|
||||
extern char const* KEEPALIVE_FINISH;
|
||||
|
||||
extern char const* WATCH_CREATE;
|
||||
extern char const* WATCH_WRITE;
|
||||
extern char const* WATCH_WRITE_CANCEL;
|
||||
extern char const* WATCH_WRITES_DONE;
|
||||
extern char const* WATCH_FINISH;
|
||||
|
||||
extern char const* ELECTION_OBSERVE_CREATE;
|
||||
extern char const* ELECTION_OBSERVE_FINISH;
|
||||
|
||||
extern const int ERROR_GRPC_OK;
|
||||
extern const int ERROR_GRPC_CANCELLED;
|
||||
extern const int ERROR_GRPC_UNKNOWN;
|
||||
extern const int ERROR_GRPC_INVALID_ARGUMENT;
|
||||
extern const int ERROR_GRPC_DEADLINE_EXCEEDED;
|
||||
extern const int ERROR_GRPC_NOT_FOUND;
|
||||
extern const int ERROR_GRPC_ALREADY_EXISTS;
|
||||
extern const int ERROR_GRPC_PERMISSION_DENIED;
|
||||
extern const int ERROR_GRPC_UNAUTHENTICATED;
|
||||
extern const int ERROR_GRPC_RESOURCE_EXHAUSTED;
|
||||
extern const int ERROR_GRPC_FAILED_PRECONDITION;
|
||||
extern const int ERROR_GRPC_ABORTED;
|
||||
extern const int ERROR_GRPC_OUT_OF_RANGE;
|
||||
extern const int ERROR_GRPC_UNIMPLEMENTED;
|
||||
extern const int ERROR_GRPC_INTERNAL;
|
||||
extern const int ERROR_GRPC_UNAVAILABLE;
|
||||
extern const int ERROR_GRPC_DATA_LOSS;
|
||||
|
||||
extern const int ERROR_KEY_NOT_FOUND;
|
||||
extern const int ERROR_COMPARE_FAILED;
|
||||
extern const int ERROR_KEY_ALREADY_EXISTS;
|
||||
extern const int ERROR_ACTION_CANCELLED;
|
||||
} // namespace etcdv3
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ protobuf_generate_latest(
|
|||
compute_generated_srcs(PROTO_GENERATES_SRCS "${PROTO_GEN_OUT_DIR}" false ${PROTO_SRCS})
|
||||
|
||||
set(PROTO_GRPC_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/rpc.proto"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/v3election.proto"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/v3lock.proto")
|
||||
grpc_generate_cpp(PROTO_GRPC_GENERATES PROTO_GRPC_GENERATES_HDRS
|
||||
"${PROTO_GEN_OUT_DIR}"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package etcdserverpb;
|
|||
import "gogoproto/gogo.proto";
|
||||
import "kv.proto";
|
||||
import "auth.proto";
|
||||
import "txn.proto";
|
||||
|
||||
// for grpc-gateway
|
||||
import "google/api/annotations.proto";
|
||||
|
|
@ -533,6 +532,46 @@ message ResponseOp {
|
|||
}
|
||||
}
|
||||
|
||||
message Compare {
|
||||
enum CompareResult {
|
||||
EQUAL = 0;
|
||||
GREATER = 1;
|
||||
LESS = 2;
|
||||
NOT_EQUAL = 3;
|
||||
}
|
||||
enum CompareTarget {
|
||||
VERSION = 0;
|
||||
CREATE = 1;
|
||||
MOD = 2;
|
||||
VALUE = 3;
|
||||
LEASE = 4;
|
||||
}
|
||||
// result is logical comparison operation for this comparison.
|
||||
CompareResult result = 1;
|
||||
// target is the key-value field to inspect for the comparison.
|
||||
CompareTarget target = 2;
|
||||
// key is the subject key for the comparison operation.
|
||||
bytes key = 3;
|
||||
oneof target_union {
|
||||
// version is the version of the given key
|
||||
int64 version = 4;
|
||||
// create_revision is the creation revision of the given key
|
||||
int64 create_revision = 5;
|
||||
// mod_revision is the last modified revision of the given key.
|
||||
int64 mod_revision = 6;
|
||||
// value is the value of the given key, in bytes.
|
||||
bytes value = 7;
|
||||
// lease is the lease id of the given key.
|
||||
int64 lease = 8;
|
||||
// leave room for more target_union field tags, jump to 64
|
||||
}
|
||||
|
||||
// range_end compares the given target to all keys in the range [key, range_end).
|
||||
// See RangeRequest for more details on key ranges.
|
||||
bytes range_end = 64;
|
||||
// TODO: fill out with most of the rest of RangeRequest fields when needed.
|
||||
}
|
||||
|
||||
// From google paxosdb paper:
|
||||
// Our implementation hinges around a powerful primitive which we call MultiOp. All other database
|
||||
// operations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
syntax = "proto3";
|
||||
package etcdserverpb;
|
||||
|
||||
message Compare {
|
||||
enum CompareResult {
|
||||
EQUAL = 0;
|
||||
GREATER = 1;
|
||||
LESS = 2;
|
||||
NOT_EQUAL = 3;
|
||||
}
|
||||
enum CompareTarget {
|
||||
VERSION = 0;
|
||||
CREATE = 1;
|
||||
MOD = 2;
|
||||
VALUE = 3;
|
||||
LEASE = 4;
|
||||
}
|
||||
// result is logical comparison operation for this comparison.
|
||||
CompareResult result = 1;
|
||||
// target is the key-value field to inspect for the comparison.
|
||||
CompareTarget target = 2;
|
||||
// key is the subject key for the comparison operation.
|
||||
bytes key = 3;
|
||||
oneof target_union {
|
||||
// version is the version of the given key
|
||||
int64 version = 4;
|
||||
// create_revision is the creation revision of the given key
|
||||
int64 create_revision = 5;
|
||||
// mod_revision is the last modified revision of the given key.
|
||||
int64 mod_revision = 6;
|
||||
// value is the value of the given key, in bytes.
|
||||
bytes value = 7;
|
||||
// lease is the lease id of the given key.
|
||||
int64 lease = 8;
|
||||
// leave room for more target_union field tags, jump to 64
|
||||
}
|
||||
|
||||
// range_end compares the given target to all keys in the range [key, range_end).
|
||||
// See RangeRequest for more details on key ranges.
|
||||
bytes range_end = 64;
|
||||
// TODO: fill out with most of the rest of RangeRequest fields when needed.
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
syntax = "proto3";
|
||||
package v3electionpb;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "rpc.proto";
|
||||
import "kv.proto";
|
||||
|
||||
// for grpc-gateway
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
option (gogoproto.marshaler_all) = true;
|
||||
option (gogoproto.unmarshaler_all) = true;
|
||||
|
||||
// The election service exposes client-side election facilities as a gRPC interface.
|
||||
service Election {
|
||||
// Campaign waits to acquire leadership in an election, returning a LeaderKey
|
||||
// representing the leadership if successful. The LeaderKey can then be used
|
||||
// to issue new values on the election, transactionally guard API requests on
|
||||
// leadership still being held, and resign from the election.
|
||||
rpc Campaign(CampaignRequest) returns (CampaignResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3/election/campaign"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
// Proclaim updates the leader's posted value with a new value.
|
||||
rpc Proclaim(ProclaimRequest) returns (ProclaimResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3/election/proclaim"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
// Leader returns the current election proclamation, if any.
|
||||
rpc Leader(LeaderRequest) returns (LeaderResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3/election/leader"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
// Observe streams election proclamations in-order as made by the election's
|
||||
// elected leaders.
|
||||
rpc Observe(LeaderRequest) returns (stream LeaderResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3/election/observe"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
// Resign releases election leadership so other campaigners may acquire
|
||||
// leadership on the election.
|
||||
rpc Resign(ResignRequest) returns (ResignResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v3/election/resign"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
message CampaignRequest {
|
||||
// name is the election's identifier for the campaign.
|
||||
bytes name = 1;
|
||||
// lease is the ID of the lease attached to leadership of the election. If the
|
||||
// lease expires or is revoked before resigning leadership, then the
|
||||
// leadership is transferred to the next campaigner, if any.
|
||||
int64 lease = 2;
|
||||
// value is the initial proclaimed value set when the campaigner wins the
|
||||
// election.
|
||||
bytes value = 3;
|
||||
}
|
||||
|
||||
message CampaignResponse {
|
||||
etcdserverpb.ResponseHeader header = 1;
|
||||
// leader describes the resources used for holding leadereship of the election.
|
||||
LeaderKey leader = 2;
|
||||
}
|
||||
|
||||
message LeaderKey {
|
||||
// name is the election identifier that correponds to the leadership key.
|
||||
bytes name = 1;
|
||||
// key is an opaque key representing the ownership of the election. If the key
|
||||
// is deleted, then leadership is lost.
|
||||
bytes key = 2;
|
||||
// rev is the creation revision of the key. It can be used to test for ownership
|
||||
// of an election during transactions by testing the key's creation revision
|
||||
// matches rev.
|
||||
int64 rev = 3;
|
||||
// lease is the lease ID of the election leader.
|
||||
int64 lease = 4;
|
||||
}
|
||||
|
||||
message LeaderRequest {
|
||||
// name is the election identifier for the leadership information.
|
||||
bytes name = 1;
|
||||
}
|
||||
|
||||
message LeaderResponse {
|
||||
etcdserverpb.ResponseHeader header = 1;
|
||||
// kv is the key-value pair representing the latest leader update.
|
||||
mvccpb.KeyValue kv = 2;
|
||||
}
|
||||
|
||||
message ResignRequest {
|
||||
// leader is the leadership to relinquish by resignation.
|
||||
LeaderKey leader = 1;
|
||||
}
|
||||
|
||||
message ResignResponse {
|
||||
etcdserverpb.ResponseHeader header = 1;
|
||||
}
|
||||
|
||||
message ProclaimRequest {
|
||||
// leader is the leadership hold on the election.
|
||||
LeaderKey leader = 1;
|
||||
// value is an update meant to overwrite the leader's current value.
|
||||
bytes value = 2;
|
||||
}
|
||||
|
||||
message ProclaimResponse {
|
||||
etcdserverpb.ResponseHeader header = 1;
|
||||
}
|
||||
|
|
@ -1,31 +1,86 @@
|
|||
file(GLOB_RECURSE CPP_CLIENT_SRC
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/**/*.cpp")
|
||||
|
||||
# grpc stuffs
|
||||
set_source_files_properties(${PROTOBUF_GENERATES} PROPERTIES GENERATED TRUE)
|
||||
add_library(etcd-cpp-api ${CPP_CLIENT_SRC} ${PROTOBUF_GENERATES})
|
||||
add_dependencies(etcd-cpp-api protobuf_generates)
|
||||
set_property(TARGET etcd-cpp-api PROPERTY CXX_STANDARD 11)
|
||||
|
||||
target_link_libraries(etcd-cpp-api PUBLIC
|
||||
${Boost_LIBRARIES}
|
||||
${CPPREST_LIB}
|
||||
${PROTOBUF_LIBRARIES}
|
||||
macro(include_generated_protobuf_files target)
|
||||
target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen)
|
||||
target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen/proto)
|
||||
endmacro()
|
||||
|
||||
# prepare common objects
|
||||
file(GLOB_RECURSE CPP_CLIENT_CORE_SRC
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/KeepAlive.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Response.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/SyncClient.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Value.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Watcher.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/**/*.cpp"
|
||||
)
|
||||
|
||||
add_library(etcd-cpp-api-core-objects OBJECT ${CPP_CLIENT_CORE_SRC} ${PROTOBUF_GENERATES})
|
||||
use_cxx(etcd-cpp-api-core-objects)
|
||||
set_exceptions(etcd-cpp-api-core-objects)
|
||||
add_dependencies(etcd-cpp-api-core-objects protobuf_generates)
|
||||
include_generated_protobuf_files(etcd-cpp-api-core-objects)
|
||||
target_link_libraries(etcd-cpp-api-core-objects PUBLIC
|
||||
${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()
|
||||
|
||||
target_include_directories(etcd-cpp-api PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen)
|
||||
target_include_directories(etcd-cpp-api PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen/proto)
|
||||
if(BUILD_ETCD_CORE_ONLY)
|
||||
# add the core library, includes the sycnhronous client only
|
||||
add_library(etcd-cpp-api-core $<TARGET_OBJECTS:etcd-cpp-api-core-objects>)
|
||||
use_cxx(etcd-cpp-api-core)
|
||||
set_exceptions(etcd-cpp-api-core)
|
||||
target_link_libraries(etcd-cpp-api-core PUBLIC
|
||||
${OPENSSL_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)
|
||||
else()
|
||||
# add the client with asynchronus client
|
||||
add_library(etcd-cpp-api $<TARGET_OBJECTS:etcd-cpp-api-core-objects>
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/Client.cpp")
|
||||
use_cxx(etcd-cpp-api)
|
||||
set_exceptions(etcd-cpp-api)
|
||||
target_link_libraries(etcd-cpp-api PUBLIC
|
||||
${CPPREST_LIB} # n.b.: the asynchronous client requires pplx in cpprestsdk
|
||||
${OPENSSL_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)
|
||||
endif()
|
||||
|
||||
if(BUILD_ETCD_CORE_ONLY)
|
||||
add_library(etcd-cpp-api INTERFACE)
|
||||
target_link_libraries(etcd-cpp-api INTERFACE etcd-cpp-api-core)
|
||||
else()
|
||||
add_library(etcd-cpp-api-core INTERFACE)
|
||||
target_link_libraries(etcd-cpp-api-core INTERFACE etcd-cpp-api)
|
||||
endif()
|
||||
|
||||
if("${CMAKE_VERSION}" VERSION_LESS "3.14")
|
||||
install(TARGETS etcd-cpp-api
|
||||
install(TARGETS etcd-cpp-api-core etcd-cpp-api
|
||||
EXPORT etcd-targets
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib)
|
||||
else()
|
||||
install(TARGETS etcd-cpp-api
|
||||
install(TARGETS etcd-cpp-api-core etcd-cpp-api
|
||||
EXPORT etcd-targets)
|
||||
endif()
|
||||
|
||||
|
|
|
|||
1219
src/Client.cpp
1219
src/Client.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -1,13 +1,15 @@
|
|||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <ratio>
|
||||
|
||||
#include "etcd/KeepAlive.hpp"
|
||||
#include "etcd/v3/AsyncLeaseAction.hpp"
|
||||
#include "etcd/v3/AsyncGRPC.hpp"
|
||||
|
||||
#include <grpc++/grpc++.h>
|
||||
#include "proto/rpc.grpc.pb.h"
|
||||
|
||||
namespace etcdv3 {
|
||||
class AsyncLeaseKeepAliveAction;
|
||||
class AsyncLeaseKeepAliveAction;
|
||||
}
|
||||
|
||||
struct etcd::KeepAlive::EtcdServerStubs {
|
||||
|
|
@ -15,108 +17,144 @@ struct etcd::KeepAlive::EtcdServerStubs {
|
|||
std::unique_ptr<etcdv3::AsyncLeaseKeepAliveAction> call;
|
||||
};
|
||||
|
||||
void etcd::KeepAlive::EtcdServerStubsDeleter::operator()(etcd::KeepAlive::EtcdServerStubs *stubs) {
|
||||
void etcd::KeepAlive::EtcdServerStubsDeleter::operator()(
|
||||
etcd::KeepAlive::EtcdServerStubs* stubs) {
|
||||
if (stubs) {
|
||||
delete stubs;
|
||||
}
|
||||
}
|
||||
|
||||
etcd::KeepAlive::KeepAlive(Client const &client, int ttl, int64_t lease_id):
|
||||
ttl(ttl), lease_id(lease_id), continue_next(true) {
|
||||
etcd::KeepAlive::KeepAlive(SyncClient const& client, int ttl, int64_t lease_id)
|
||||
: 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();
|
||||
}
|
||||
stubs.reset(new EtcdServerStubs{});
|
||||
stubs->leaseServiceStub = Lease::NewStub(client.channel);
|
||||
stubs->leaseServiceStub = Lease::NewStub(client.grpc_channel());
|
||||
|
||||
etcdv3::ActionParameters params;
|
||||
params.auth_token.assign(client.auth_token);
|
||||
params.auth_token.assign(client.current_auth_token());
|
||||
params.grpc_timeout = grpc_timeout;
|
||||
params.lease_id = this->lease_id;
|
||||
params.lease_stub = stubs->leaseServiceStub.get();
|
||||
|
||||
continue_next.store(true);
|
||||
|
||||
stubs->call.reset(new etcdv3::AsyncLeaseKeepAliveAction(params));
|
||||
task_ = std::thread([this]() {
|
||||
stubs->call.reset(new etcdv3::AsyncLeaseKeepAliveAction(std::move(params)));
|
||||
refresh_task_ = std::thread([this]() {
|
||||
#ifndef _ETCD_NO_EXCEPTIONS
|
||||
try {
|
||||
// start refresh
|
||||
this->refresh();
|
||||
context.run();
|
||||
} catch (...) {
|
||||
} catch (const std::exception& e) {
|
||||
// propagate the exception
|
||||
eptr_ = std::current_exception();
|
||||
}
|
||||
context.stop(); // clean up
|
||||
#else
|
||||
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, int64_t lease_id):
|
||||
KeepAlive(Client(address), ttl, lease_id) {
|
||||
}
|
||||
etcd::KeepAlive::KeepAlive(std::string const& address, int ttl,
|
||||
int64_t lease_id)
|
||||
: KeepAlive(SyncClient(address), ttl, lease_id) {}
|
||||
|
||||
etcd::KeepAlive::KeepAlive(std::string const & address,
|
||||
std::string const & username, std::string const & password,
|
||||
int ttl, int64_t lease_id):
|
||||
KeepAlive(Client(address, username, password), ttl, lease_id) {
|
||||
}
|
||||
etcd::KeepAlive::KeepAlive(std::string const& address,
|
||||
std::string const& username,
|
||||
std::string const& password, int ttl,
|
||||
int64_t lease_id, int const auth_token_ttl)
|
||||
: KeepAlive(SyncClient(address, username, password, auth_token_ttl), ttl,
|
||||
lease_id) {}
|
||||
|
||||
etcd::KeepAlive::KeepAlive(Client 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) {
|
||||
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();
|
||||
}
|
||||
stubs.reset(new EtcdServerStubs{});
|
||||
stubs->leaseServiceStub = Lease::NewStub(client.channel);
|
||||
stubs->leaseServiceStub = Lease::NewStub(client.grpc_channel());
|
||||
|
||||
etcdv3::ActionParameters params;
|
||||
params.auth_token.assign(client.auth_token);
|
||||
params.auth_token.assign(client.current_auth_token());
|
||||
// n.b.: keepalive: no need for timeout
|
||||
params.lease_id = this->lease_id;
|
||||
params.lease_stub = stubs->leaseServiceStub.get();
|
||||
|
||||
stubs->call.reset(new etcdv3::AsyncLeaseKeepAliveAction(params));
|
||||
task_ = std::thread([this]() {
|
||||
stubs->call.reset(new etcdv3::AsyncLeaseKeepAliveAction(std::move(params)));
|
||||
refresh_task_ = std::thread([this]() {
|
||||
#ifndef _ETCD_NO_EXCEPTIONS
|
||||
try {
|
||||
// start refresh
|
||||
this->refresh();
|
||||
context.run();
|
||||
} catch (...) {
|
||||
if (handler_) {
|
||||
handler_(std::current_exception());
|
||||
} else {
|
||||
eptr_ = std::current_exception();
|
||||
}
|
||||
this->Cancel();
|
||||
// propagate the exception
|
||||
eptr_ = std::current_exception();
|
||||
}
|
||||
#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_);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
etcd::KeepAlive::KeepAlive(std::string const & address,
|
||||
std::function<void (std::exception_ptr)> const &handler,
|
||||
int ttl, int64_t lease_id):
|
||||
KeepAlive(Client(address), handler, ttl, lease_id) {
|
||||
}
|
||||
etcd::KeepAlive::KeepAlive(
|
||||
std::string const& address,
|
||||
std::function<void(std::exception_ptr)> const& handler, int ttl,
|
||||
int64_t lease_id)
|
||||
: KeepAlive(SyncClient(address), handler, ttl, lease_id) {}
|
||||
|
||||
etcd::KeepAlive::KeepAlive(std::string const & address,
|
||||
std::string const & username, std::string const & password,
|
||||
std::function<void (std::exception_ptr)> const &handler,
|
||||
int ttl, int64_t lease_id):
|
||||
KeepAlive(Client(address, username, password), handler, ttl, lease_id) {
|
||||
}
|
||||
etcd::KeepAlive::KeepAlive(
|
||||
std::string const& address, std::string const& username,
|
||||
std::string const& password,
|
||||
std::function<void(std::exception_ptr)> const& handler, int ttl,
|
||||
int64_t lease_id, const int auth_token_ttl)
|
||||
: KeepAlive(SyncClient(address, username, password, auth_token_ttl),
|
||||
handler, ttl, lease_id) {}
|
||||
|
||||
etcd::KeepAlive::~KeepAlive()
|
||||
{
|
||||
this->Cancel();
|
||||
}
|
||||
etcd::KeepAlive::~KeepAlive() { this->Cancel(); }
|
||||
|
||||
void etcd::KeepAlive::Cancel()
|
||||
{
|
||||
void etcd::KeepAlive::Cancel() {
|
||||
if (!continue_next.exchange(false)) {
|
||||
return;
|
||||
}
|
||||
stubs->call->CancelKeepAlive();
|
||||
if (keepalive_timer_) {
|
||||
keepalive_timer_->cancel();
|
||||
}
|
||||
|
||||
// clean up
|
||||
context.stop();
|
||||
if (task_.joinable()) {
|
||||
task_.join();
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,35 +162,97 @@ void etcd::KeepAlive::Check() {
|
|||
if (eptr_) {
|
||||
std::rethrow_exception(eptr_);
|
||||
}
|
||||
// issue an refresh to make sure it still alive
|
||||
#ifndef _ETCD_NO_EXCEPTIONS
|
||||
try {
|
||||
this->refresh_once();
|
||||
} catch (...) {
|
||||
// run canceller first
|
||||
this->Cancel();
|
||||
|
||||
// propagate the exception, as we throw in `Check()`, the `handler` won't be
|
||||
// touched
|
||||
eptr_ = std::current_exception();
|
||||
}
|
||||
#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_);
|
||||
}
|
||||
|
||||
#ifndef _ETCD_NO_EXCEPTIONS
|
||||
if (eptr_) {
|
||||
// rethrow in `Check()` to keep the consistent semantics
|
||||
std::rethrow_exception(eptr_);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void etcd::KeepAlive::refresh()
|
||||
{
|
||||
if (!continue_next.load()) {
|
||||
return;
|
||||
}
|
||||
// minimal resolution: 1 second
|
||||
int keepalive_ttl = std::max(ttl - 1, 1);
|
||||
keepalive_timer_.reset(new boost::asio::steady_timer(
|
||||
context, std::chrono::seconds(keepalive_ttl)));
|
||||
keepalive_timer_->async_wait([this](const boost::system::error_code& error) {
|
||||
if (error) {
|
||||
std::string etcd::KeepAlive::refresh() {
|
||||
while (true) {
|
||||
if (!continue_next.load()) {
|
||||
return std::string{};
|
||||
}
|
||||
// minimal resolution: 1 second
|
||||
int keepalive_ttl = std::max(ttl - 1, 1);
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_for_refresh_);
|
||||
if (cv_for_refresh_.wait_for(lock, std::chrono::seconds(keepalive_ttl)) ==
|
||||
std::cv_status::no_timeout) {
|
||||
if (!continue_next.load()) {
|
||||
return std::string{};
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
std::cerr << "keepalive timer cancelled: " << error << ", " << error.message() << std::endl;
|
||||
std::cerr
|
||||
<< "[warn] awaked from condition_variable but continue_next is "
|
||||
"not set, maybe due to clock drift."
|
||||
<< std::endl;
|
||||
#endif
|
||||
} else {
|
||||
if (this->continue_next.load()) {
|
||||
auto resp = this->stubs->call->Refresh();
|
||||
if (!resp.is_ok()) {
|
||||
throw std::runtime_error("Failed to refresh lease: error code: " + std::to_string(resp.error_code()) +
|
||||
", message: " + resp.error_message());
|
||||
}
|
||||
if (resp.value().ttl() == 0) {
|
||||
throw std::out_of_range("Failed to refresh lease due to expiration: the new TTL is 0.");
|
||||
}
|
||||
// trigger the next round;
|
||||
this->refresh();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// execute refresh
|
||||
const std::string err = this->refresh_once();
|
||||
if (!err.empty()) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
std::string etcd::KeepAlive::refresh_once() {
|
||||
std::lock_guard<std::mutex> scope_lock(mutex_for_refresh_);
|
||||
if (!continue_next.load()) {
|
||||
return std::string{};
|
||||
}
|
||||
this->stubs->call->mutable_parameters().grpc_timeout = this->grpc_timeout;
|
||||
auto resp = this->stubs->call->Refresh();
|
||||
if (!resp.is_ok()) {
|
||||
const std::string err = "Failed to refresh lease: error code: " +
|
||||
std::to_string(resp.error_code()) +
|
||||
", message: " + resp.error_message();
|
||||
#ifndef _ETCD_NO_EXCEPTIONS
|
||||
throw std::runtime_error(err);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
if (resp.value().ttl() == 0) {
|
||||
const std::string err =
|
||||
"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{};
|
||||
}
|
||||
|
|
|
|||
160
src/Response.cpp
160
src/Response.cpp
|
|
@ -3,118 +3,138 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
etcd::Response::Response() : _error_code(0), _index(0) {}
|
||||
|
||||
etcd::Response::Response(const etcdv3::V3Response& reply, std::chrono::microseconds const& duration)
|
||||
{
|
||||
etcd::Response::Response(const etcd::Response& response) {
|
||||
this->_error_code = response._error_code;
|
||||
this->_error_message = response._error_message;
|
||||
this->_index = response._index;
|
||||
this->_action = response._action;
|
||||
this->_value = response._value;
|
||||
this->_prev_value = response._prev_value;
|
||||
this->_values = response._values;
|
||||
this->_keys = response._keys;
|
||||
this->_compact_revision = response._compact_revision;
|
||||
this->_lock_key = response._lock_key;
|
||||
this->_name = response._name;
|
||||
this->_events = response._events;
|
||||
this->_duration = response._duration;
|
||||
|
||||
this->_cluster_id = response._cluster_id;
|
||||
this->_member_id = response._member_id;
|
||||
this->_raft_term = response._raft_term;
|
||||
|
||||
this->_leases = response._leases;
|
||||
this->_members = response._members;
|
||||
}
|
||||
|
||||
etcd::Response::Response(const etcdv3::V3Response& reply,
|
||||
std::chrono::microseconds const& duration) {
|
||||
_index = reply.get_index();
|
||||
_action = reply.get_action();
|
||||
_error_code = reply.get_error_code();
|
||||
_error_message = reply.get_error_message();
|
||||
if(reply.has_values())
|
||||
{
|
||||
if (reply.has_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]));
|
||||
_keys.push_back(val[index].kvs.key());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_value = Value(reply.get_values()[0]);
|
||||
} else {
|
||||
_value = Value(reply.get_value());
|
||||
}
|
||||
|
||||
_prev_value = Value(reply.get_prev_value());
|
||||
|
||||
_compact_revision = reply.get_compact_revision();
|
||||
_watch_id = reply.get_watch_id();
|
||||
_lock_key = reply.get_lock_key();
|
||||
_events = reply.get_events();
|
||||
_name = reply.get_name();
|
||||
|
||||
for (auto const& ev : reply.get_events()) {
|
||||
_events.emplace_back(etcd::Event(ev));
|
||||
}
|
||||
|
||||
// duration
|
||||
_duration = duration;
|
||||
|
||||
// etcd head
|
||||
_cluster_id = reply.get_cluster_id();
|
||||
_member_id = reply.get_member_id();
|
||||
_raft_term = reply.get_raft_term();
|
||||
|
||||
// lease list
|
||||
this->_leases = reply.get_leases();
|
||||
// member list
|
||||
this->_members = reply.get_members();
|
||||
}
|
||||
|
||||
etcd::Response::Response(int error_code, std::string const& error_message)
|
||||
: _error_code(error_code), _error_message(error_message), _index(0) {}
|
||||
|
||||
etcd::Response::Response()
|
||||
: _error_code(0),
|
||||
_index(0)
|
||||
{
|
||||
}
|
||||
etcd::Response::Response(int error_code, char const* error_message)
|
||||
: _error_code(error_code), _error_message(error_message), _index(0) {}
|
||||
|
||||
etcd::Response::Response(int error_code, char const * error_message)
|
||||
: _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;
|
||||
}
|
||||
|
||||
int etcd::Response::index() const
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
bool etcd::Response::is_ok() const { return error_code() == 0; }
|
||||
|
||||
std::string const & etcd::Response::action() const
|
||||
{
|
||||
return _action;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
etcd::Value const & etcd::Response::value() const
|
||||
{
|
||||
return _value;
|
||||
bool etcd::Response::is_grpc_timeout() const {
|
||||
return _error_code == grpc::StatusCode::DEADLINE_EXCEEDED;
|
||||
}
|
||||
|
||||
etcd::Value const & etcd::Response::prev_value() const
|
||||
{
|
||||
return _prev_value;
|
||||
}
|
||||
std::string const& etcd::Response::action() const { return _action; }
|
||||
|
||||
etcd::Values const & etcd::Response::values() const
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
int64_t etcd::Response::index() const { return _index; }
|
||||
|
||||
etcd::Value const & etcd::Response::value(int index) const
|
||||
{
|
||||
etcd::Value const& etcd::Response::value() const { return _value; }
|
||||
|
||||
etcd::Value const& etcd::Response::prev_value() const { return _prev_value; }
|
||||
|
||||
etcd::Values const& etcd::Response::values() const { return _values; }
|
||||
|
||||
etcd::Value const& etcd::Response::value(int index) const {
|
||||
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]; }
|
||||
|
||||
std::string const & etcd::Response::lock_key() const {
|
||||
return _lock_key;
|
||||
}
|
||||
int64_t etcd::Response::compact_revision() const { return _compact_revision; }
|
||||
|
||||
std::vector<mvccpb::Event> const & etcd::Response::events() const {
|
||||
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::name() const { return _name; }
|
||||
|
||||
std::vector<etcd::Event> const& etcd::Response::events() const {
|
||||
return this->_events;
|
||||
}
|
||||
|
||||
std::chrono::microseconds const& etcd::Response::duration() const {
|
||||
return this->_duration;
|
||||
}
|
||||
|
||||
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::raft_term() const { return this->_raft_term; }
|
||||
|
||||
std::vector<int64_t> const& etcd::Response::leases() const {
|
||||
return this->_leases;
|
||||
}
|
||||
|
||||
std::vector<etcdv3::Member> const& etcd::Response::members() const {
|
||||
return this->_members;
|
||||
}
|
||||
|
|
|
|||
1348
src/SyncClient.cpp
1348
src/SyncClient.cpp
File diff suppressed because it is too large
Load Diff
132
src/Value.cpp
132
src/Value.cpp
|
|
@ -1,60 +1,116 @@
|
|||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
|
||||
#include "etcd/Value.hpp"
|
||||
#include "etcd/v3/KeyValue.hpp"
|
||||
|
||||
etcd::Value::Value()
|
||||
: dir(false),
|
||||
created(0),
|
||||
modified(0),
|
||||
_ttl(0),
|
||||
leaseId(0)
|
||||
{
|
||||
}
|
||||
: dir(false), created(0), modified(0), _version(0), _ttl(0), leaseId(0) {}
|
||||
|
||||
|
||||
etcd::Value::Value(etcdv3::KeyValue const & kv)
|
||||
{
|
||||
dir=false;
|
||||
_key=kv.kvs.key();
|
||||
value=kv.kvs.value();
|
||||
created=kv.kvs.create_revision();
|
||||
modified=kv.kvs.mod_revision();
|
||||
etcd::Value::Value(etcdv3::KeyValue const& kv) {
|
||||
dir = false;
|
||||
_key = kv.kvs.key();
|
||||
value = kv.kvs.value();
|
||||
created = kv.kvs.create_revision();
|
||||
modified = kv.kvs.mod_revision();
|
||||
_version = kv.kvs.version();
|
||||
leaseId = kv.kvs.lease();
|
||||
_ttl = kv.get_ttl();
|
||||
}
|
||||
|
||||
std::string const & etcd::Value::key() const
|
||||
{
|
||||
return _key;
|
||||
etcd::Value::Value(mvccpb::KeyValue const& kv) {
|
||||
dir = false;
|
||||
_key = kv.key();
|
||||
value = kv.value();
|
||||
created = kv.create_revision();
|
||||
modified = kv.mod_revision();
|
||||
_version = kv.version();
|
||||
leaseId = kv.lease();
|
||||
_ttl = -1;
|
||||
}
|
||||
|
||||
bool etcd::Value::is_dir() const
|
||||
{
|
||||
return dir;
|
||||
std::string const& etcd::Value::key() const { return _key; }
|
||||
|
||||
bool etcd::Value::is_dir() const { return dir; }
|
||||
|
||||
std::string const& etcd::Value::as_string() const { return value; }
|
||||
|
||||
int64_t etcd::Value::created_index() const { return created; }
|
||||
|
||||
int64_t etcd::Value::modified_index() const { return modified; }
|
||||
|
||||
int64_t etcd::Value::version() const { return _version; }
|
||||
|
||||
int etcd::Value::ttl() const { return _ttl; }
|
||||
|
||||
int64_t etcd::Value::lease() const { return leaseId; }
|
||||
|
||||
std::ostream& etcd::operator<<(std::ostream& os, const etcd::Value& value) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::string const & etcd::Value::as_string() const
|
||||
{
|
||||
return value;
|
||||
etcd::Event::Event(mvccpb::Event const& event) {
|
||||
_has_kv = event.has_kv();
|
||||
_has_prev_kv = event.has_prev_kv();
|
||||
if (_has_kv) {
|
||||
_kv = Value(event.kv());
|
||||
}
|
||||
if (_has_prev_kv) {
|
||||
_prev_kv = Value(event.prev_kv());
|
||||
}
|
||||
if (event.type() == mvccpb::Event::PUT) {
|
||||
event_type_ = EventType::PUT;
|
||||
} else if (event.type() == mvccpb::Event::DELETE_) {
|
||||
event_type_ = EventType::DELETE_;
|
||||
} else {
|
||||
event_type_ = EventType::INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
int etcd::Value::created_index() const
|
||||
{
|
||||
return created;
|
||||
enum etcd::Event::EventType etcd::Event::event_type() const {
|
||||
return event_type_;
|
||||
}
|
||||
|
||||
int etcd::Value::modified_index() const
|
||||
{
|
||||
return modified;
|
||||
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;
|
||||
}
|
||||
|
||||
int etcd::Value::ttl() const
|
||||
{
|
||||
return _ttl;
|
||||
}
|
||||
|
||||
int64_t etcd::Value::lease() const
|
||||
{
|
||||
return leaseId;
|
||||
std::ostream& etcd::operator<<(std::ostream& os, const etcd::Event& event) {
|
||||
os << "Event type: " << event.event_type();
|
||||
if (event.has_kv()) {
|
||||
os << ", KV: " << event.kv();
|
||||
}
|
||||
if (event.has_prev_kv()) {
|
||||
os << ", Prev KV: " << event.prev_kv();
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
|
|
|||
302
src/Watcher.cpp
302
src/Watcher.cpp
|
|
@ -1,102 +1,226 @@
|
|||
#include "etcd/Watcher.hpp"
|
||||
#include "etcd/v3/AsyncWatchAction.hpp"
|
||||
|
||||
#include "etcd/SyncClient.hpp"
|
||||
|
||||
#include "etcd/v3/AsyncGRPC.hpp"
|
||||
|
||||
struct etcd::Watcher::EtcdServerStubs {
|
||||
std::unique_ptr<Watch::Stub> watchServiceStub;
|
||||
std::unique_ptr<etcdv3::AsyncWatchAction> call;
|
||||
};
|
||||
|
||||
void etcd::Watcher::EtcdServerStubsDeleter::operator()(etcd::Watcher::EtcdServerStubs *stubs) {
|
||||
void etcd::Watcher::EtcdServerStubsDeleter::operator()(
|
||||
etcd::Watcher::EtcdServerStubs* stubs) {
|
||||
if (stubs) {
|
||||
if (stubs->watchServiceStub) {
|
||||
stubs->watchServiceStub.reset();
|
||||
}
|
||||
if (stubs->call) {
|
||||
stubs->call.reset();
|
||||
}
|
||||
delete stubs;
|
||||
}
|
||||
}
|
||||
|
||||
etcd::Watcher::Watcher(Client const &client, std::string const & key,
|
||||
std::function<void(Response)> callback, bool recursive):
|
||||
Watcher(client, key, -1, callback, recursive) {
|
||||
}
|
||||
etcd::Watcher::Watcher(SyncClient const& client, std::string const& key,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive)
|
||||
: Watcher(client, key, -1, callback, wait_callback, recursive) {}
|
||||
|
||||
etcd::Watcher::Watcher(Client const &client, std::string const & key,
|
||||
std::string const &range_end,
|
||||
std::function<void(Response)> callback):
|
||||
Watcher(client, key, range_end, -1, callback) {
|
||||
}
|
||||
etcd::Watcher::Watcher(SyncClient const& client, std::string const& key,
|
||||
std::string const& range_end,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback)
|
||||
: Watcher(client, key, range_end, -1, callback, wait_callback) {}
|
||||
|
||||
etcd::Watcher::Watcher(Client const &client, std::string const & key, int fromIndex,
|
||||
std::function<void(Response)> callback, bool recursive):
|
||||
fromIndex(fromIndex), recursive(recursive) {
|
||||
etcd::Watcher::Watcher(SyncClient const& client, std::string const& key,
|
||||
int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive)
|
||||
: wait_callback(wait_callback), fromIndex(fromIndex), recursive(recursive) {
|
||||
stubs.reset(new EtcdServerStubs{});
|
||||
stubs->watchServiceStub = Watch::NewStub(client.channel);
|
||||
doWatch(key, "", client.auth_token, callback);
|
||||
doWatch(key, "", client.current_auth_token(), callback);
|
||||
}
|
||||
|
||||
etcd::Watcher::Watcher(Client const &client, std::string const & key,
|
||||
std::string const &range_end, int fromIndex,
|
||||
std::function<void(Response)> callback):
|
||||
fromIndex(fromIndex), recursive(false) {
|
||||
etcd::Watcher::Watcher(SyncClient const& client, std::string const& key,
|
||||
std::string const& range_end, int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback)
|
||||
: wait_callback(wait_callback), fromIndex(fromIndex), recursive(false) {
|
||||
stubs.reset(new EtcdServerStubs{});
|
||||
stubs->watchServiceStub = Watch::NewStub(client.channel);
|
||||
doWatch(key, range_end, client.auth_token, callback);
|
||||
doWatch(key, range_end, client.current_auth_token(), callback);
|
||||
}
|
||||
|
||||
etcd::Watcher::Watcher(std::string const & address, std::string const & key,
|
||||
std::function<void(Response)> callback, bool recursive):
|
||||
Watcher(address, key, -1, callback, recursive) {
|
||||
}
|
||||
etcd::Watcher::Watcher(std::string const& address, std::string const& key,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive)
|
||||
: Watcher(address, key, -1, callback, wait_callback, 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, -1, callback) {
|
||||
}
|
||||
etcd::Watcher::Watcher(std::string const& address, std::string const& key,
|
||||
std::string const& range_end,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback)
|
||||
: Watcher(address, key, range_end, -1, callback, wait_callback) {}
|
||||
|
||||
etcd::Watcher::Watcher(std::string const & address, std::string const & key, int fromIndex,
|
||||
std::function<void(Response)> callback, bool recursive):
|
||||
Watcher(Client(address), key, fromIndex, callback, recursive) {
|
||||
}
|
||||
etcd::Watcher::Watcher(std::string const& address, std::string const& key,
|
||||
int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive)
|
||||
: Watcher(SyncClient(address), key, fromIndex, callback, wait_callback,
|
||||
recursive) {}
|
||||
|
||||
etcd::Watcher::Watcher(std::string const & address, std::string const & key,
|
||||
std::string const & range_end, int fromIndex,
|
||||
std::function<void(Response)> callback):
|
||||
Watcher(Client(address), key, range_end, fromIndex, callback) {
|
||||
}
|
||||
etcd::Watcher::Watcher(std::string const& address, std::string const& key,
|
||||
std::string const& range_end, int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback)
|
||||
: Watcher(SyncClient(address), key, range_end, fromIndex, callback,
|
||||
wait_callback) {}
|
||||
|
||||
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):
|
||||
Watcher(address, username, password, key, -1, callback, recursive) {
|
||||
}
|
||||
etcd::Watcher::Watcher(std::string const& address, std::string const& username,
|
||||
std::string const& password, std::string const& key,
|
||||
std::function<void(Response)> callback,
|
||||
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):
|
||||
Watcher(address, username, password, key, range_end, -1, callback) {
|
||||
}
|
||||
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(bool)> wait_callback,
|
||||
int const auth_token_ttl)
|
||||
: Watcher(address, username, password, key, range_end, -1, callback,
|
||||
wait_callback, auth_token_ttl) {}
|
||||
|
||||
etcd::Watcher::Watcher(std::string const & address,
|
||||
std::string const & username, std::string const & password,
|
||||
std::string const & key, int fromIndex,
|
||||
std::function<void(Response)> callback, bool recursive):
|
||||
Watcher(Client(address, username, password), key, fromIndex, callback, recursive) {
|
||||
}
|
||||
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,
|
||||
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, int fromIndex,
|
||||
std::function<void(Response)> callback):
|
||||
Watcher(Client(address, username, password), key, range_end, fromIndex, callback) {
|
||||
}
|
||||
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(bool)> wait_callback,
|
||||
int const auth_token_ttl)
|
||||
: Watcher(SyncClient(address, username, password, auth_token_ttl), key,
|
||||
range_end, fromIndex, callback, wait_callback) {}
|
||||
|
||||
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, int64_t fromIndex,
|
||||
std::function<void(Response)> callback,
|
||||
std::function<void(bool)> wait_callback, bool recursive,
|
||||
std::string const& target_name_override)
|
||||
: Watcher(SyncClient(address, ca, cert, privkey, target_name_override), key,
|
||||
fromIndex, callback, wait_callback, recursive) {}
|
||||
|
||||
bool etcd::Watcher::Wait()
|
||||
{
|
||||
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) {}
|
||||
|
||||
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 (task_.joinable()) {
|
||||
task_.join();
|
||||
|
|
@ -105,28 +229,37 @@ bool etcd::Watcher::Wait()
|
|||
return stubs->call->Cancelled();
|
||||
}
|
||||
|
||||
void etcd::Watcher::Wait(std::function<void(bool)> callback)
|
||||
{
|
||||
bool etcd::Watcher::Wait(std::function<void(bool)> callback) {
|
||||
if (wait_callback == nullptr) {
|
||||
wait_callback = callback;
|
||||
return true;
|
||||
} else {
|
||||
std::cerr << "Failed to set a asynchronous wait callback since it has already been set" << std::endl;
|
||||
#ifndef NDEBUG
|
||||
std::cerr
|
||||
<< "[warn] failed to set a asynchronous wait callback since it has "
|
||||
"already been set"
|
||||
<< std::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void etcd::Watcher::Cancel()
|
||||
{
|
||||
bool etcd::Watcher::Cancel() {
|
||||
stubs->call->CancelWatch();
|
||||
this->Wait();
|
||||
return this->Wait();
|
||||
}
|
||||
|
||||
void etcd::Watcher::doWatch(std::string const & key,
|
||||
std::string const & range_end,
|
||||
std::string const & auth_token,
|
||||
std::function<void(Response)> callback)
|
||||
{
|
||||
bool etcd::Watcher::Cancelled() const {
|
||||
return cancelled.load() || stubs->call->Cancelled();
|
||||
}
|
||||
|
||||
void etcd::Watcher::doWatch(std::string const& key,
|
||||
std::string const& range_end,
|
||||
std::string const& auth_token,
|
||||
std::function<void(Response)> callback) {
|
||||
etcdv3::ActionParameters params;
|
||||
params.auth_token.assign(auth_token);
|
||||
// n.b.: watch: no need for timeout
|
||||
params.key.assign(key);
|
||||
params.range_end.assign(range_end);
|
||||
if (fromIndex >= 0) {
|
||||
|
|
@ -135,12 +268,19 @@ void etcd::Watcher::doWatch(std::string const & key,
|
|||
params.withPrefix = recursive;
|
||||
params.watch_stub = stubs->watchServiceStub.get();
|
||||
|
||||
stubs->call.reset(new etcdv3::AsyncWatchAction(params));
|
||||
stubs->call.reset(new etcdv3::AsyncWatchAction(std::move(params)));
|
||||
|
||||
task_ = std::thread([this, callback]() {
|
||||
stubs->call->waitForResponse(callback);
|
||||
if (wait_callback != nullptr) {
|
||||
wait_callback(stubs->call->Cancelled());
|
||||
// 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
|
||||
// pplx::task in the core library
|
||||
bool cancelled = stubs->call->Cancelled();
|
||||
std::function<void(bool)> wait_callback = this->wait_callback;
|
||||
std::thread canceller(
|
||||
[wait_callback, cancelled]() { wait_callback(cancelled); });
|
||||
canceller.detach();
|
||||
}
|
||||
});
|
||||
cancelled.store(false);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,35 @@
|
|||
#include <grpc/support/log.h>
|
||||
#include "etcd/v3/Action.hpp"
|
||||
#include <grpc/support/log.h>
|
||||
#include <grpcpp/support/status.h>
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
#include <cstdlib>
|
||||
|
||||
etcdv3::Action::Action(etcdv3::ActionParameters params)
|
||||
{
|
||||
#ifndef GPR_ASSERT
|
||||
#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;
|
||||
this->InitAction();
|
||||
}
|
||||
|
||||
etcdv3::Action::Action(etcdv3::ActionParameters&& params) {
|
||||
parameters = std::move(params);
|
||||
this->InitAction();
|
||||
}
|
||||
|
||||
etcdv3::Action::~Action() {
|
||||
cq_.Shutdown();
|
||||
|
||||
// cancel on-the-fly calls
|
||||
context.TryCancel();
|
||||
}
|
||||
|
||||
void etcdv3::Action::InitAction() {
|
||||
if (!parameters.auth_token.empty()) {
|
||||
// use `token` as the key, see:
|
||||
//
|
||||
|
|
@ -13,44 +39,97 @@ etcdv3::Action::Action(etcdv3::ActionParameters params)
|
|||
start_timepoint = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
etcdv3::ActionParameters::ActionParameters()
|
||||
{
|
||||
etcdv3::ActionParameters::ActionParameters() {
|
||||
withPrefix = false;
|
||||
revision = 0;
|
||||
old_revision = 0;
|
||||
lease_id = 0;
|
||||
ttl = 0;
|
||||
keys_only = false;
|
||||
count_only = false;
|
||||
kv_stub = NULL;
|
||||
watch_stub = NULL;
|
||||
lease_stub = NULL;
|
||||
}
|
||||
|
||||
void etcdv3::Action::waitForResponse()
|
||||
{
|
||||
bool etcdv3::ActionParameters::has_grpc_timeout() const {
|
||||
return this->grpc_timeout != std::chrono::microseconds::zero();
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point etcdv3::ActionParameters::grpc_deadline()
|
||||
const {
|
||||
return std::chrono::system_clock::now() + this->grpc_timeout;
|
||||
}
|
||||
|
||||
void etcdv3::ActionParameters::dump(std::ostream& os) const {
|
||||
os << "ActionParameters:" << std::endl;
|
||||
os << " withPrefix: " << withPrefix << std::endl;
|
||||
os << " revision: " << revision << std::endl;
|
||||
os << " old_revision: " << old_revision << std::endl;
|
||||
os << " lease_id: " << lease_id << std::endl;
|
||||
os << " ttl: " << ttl << std::endl;
|
||||
os << " limit: " << limit << std::endl;
|
||||
os << " name: " << name << std::endl;
|
||||
os << " key: " << key << std::endl;
|
||||
os << " range_end: " << range_end << std::endl;
|
||||
os << " keys_only: " << keys_only << std::endl;
|
||||
os << " count_only: " << count_only << std::endl;
|
||||
os << " value: " << value << std::endl;
|
||||
os << " old_value: " << old_value << std::endl;
|
||||
os << " auth_token: " << auth_token << std::endl;
|
||||
os << " grpc_timeout: " << grpc_timeout.count() << "(ms)" << std::endl;
|
||||
}
|
||||
|
||||
void etcdv3::Action::waitForResponse() {
|
||||
void* got_tag;
|
||||
bool ok = false;
|
||||
|
||||
cq_.Next(&got_tag, &ok);
|
||||
GPR_ASSERT(got_tag == (void*)this);
|
||||
if (parameters.has_grpc_timeout()) {
|
||||
switch (cq_.AsyncNext(&got_tag, &ok, parameters.grpc_deadline())) {
|
||||
case CompletionQueue::NextStatus::TIMEOUT: {
|
||||
status =
|
||||
grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, "gRPC timeout");
|
||||
break;
|
||||
}
|
||||
case CompletionQueue::NextStatus::SHUTDOWN: {
|
||||
status =
|
||||
grpc::Status(grpc::StatusCode::UNAVAILABLE, "gRPC already shutdown");
|
||||
break;
|
||||
}
|
||||
case CompletionQueue::NextStatus::GOT_EVENT: {
|
||||
if (!ok) {
|
||||
status =
|
||||
grpc::Status(grpc::StatusCode::ABORTED,
|
||||
"Failed to execute the action: not ok or invalid tag");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cq_.Next(&got_tag, &ok);
|
||||
GPR_ASSERT(got_tag == (void*) this);
|
||||
}
|
||||
}
|
||||
|
||||
const std::chrono::high_resolution_clock::time_point etcdv3::Action::startTimepoint() {
|
||||
const std::chrono::high_resolution_clock::time_point
|
||||
etcdv3::Action::startTimepoint() {
|
||||
return this->start_timepoint;
|
||||
}
|
||||
|
||||
std::string etcdv3::detail::string_plus_one(std::string const &value) {
|
||||
// referred from the Go implementation in etcd.
|
||||
char *s = static_cast<char *>(calloc(value.size() + 1, sizeof(char)));
|
||||
std::memcpy(s, value.c_str(), value.size());
|
||||
for (int i = value.size() - 1; i >= 0; --i) {
|
||||
if (static_cast<unsigned char>(s[i]) < 0xff) {
|
||||
std::string etcdv3::detail::string_plus_one(std::string const& value) {
|
||||
// Referred from the Go implementation in etcd.
|
||||
for (int32_t i = value.size() - 1; i >= 0; --i) {
|
||||
if (static_cast<unsigned char>(value[i]) < 0xff) {
|
||||
std::string s = value.substr(0, i + 1);
|
||||
s[i] = s[i] + 1;
|
||||
std::string ret = std::string(s, i + 1);
|
||||
free(s);
|
||||
return ret;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
// see: noPrefixEnd in etcd, however c++ doesn't allows '\0' inside a string, thus we use
|
||||
// the UTF-8 char U+0000 (i.e., "\xC0\x80").
|
||||
return {"\xC0\x80"};
|
||||
return {etcdv3::NUL};
|
||||
}
|
||||
|
||||
std::string etcdv3::detail::resolve_etcd_endpoints(
|
||||
std::string const& default_endpoints) {
|
||||
const char* ep = std::getenv("ETCD_ENDPOINTS");
|
||||
return ep ? ep : default_endpoints;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
#include "etcd/v3/AsyncCompareAndDeleteAction.hpp"
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
#include "etcd/v3/Transaction.hpp"
|
||||
|
||||
using etcdserverpb::Compare;
|
||||
using etcdserverpb::RangeRequest;
|
||||
using etcdserverpb::PutRequest;
|
||||
using etcdserverpb::RequestOp;
|
||||
using etcdserverpb::ResponseOp;
|
||||
using etcdserverpb::TxnRequest;
|
||||
|
||||
etcdv3::AsyncCompareAndDeleteAction::AsyncCompareAndDeleteAction(etcdv3::ActionParameters param, etcdv3::Atomicity_Type type)
|
||||
:etcdv3::Action(param)
|
||||
{
|
||||
etcdv3::Transaction transaction(parameters.key);
|
||||
if(type == etcdv3::Atomicity_Type::PREV_VALUE)
|
||||
{
|
||||
transaction.init_compare(parameters.old_value, Compare::CompareResult::Compare_CompareResult_EQUAL,
|
||||
Compare::CompareTarget::Compare_CompareTarget_VALUE);
|
||||
}
|
||||
else if (type == etcdv3::Atomicity_Type::PREV_INDEX)
|
||||
{
|
||||
transaction.init_compare(parameters.old_revision, Compare::CompareResult::Compare_CompareResult_EQUAL,
|
||||
Compare::CompareTarget::Compare_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;
|
||||
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);
|
||||
txn_resp.set_action(etcdv3::COMPAREDELETE_ACTION);
|
||||
|
||||
if(!reply.succeeded())
|
||||
{
|
||||
txn_resp.set_error_code(101);
|
||||
txn_resp.set_error_message("Compare failed");
|
||||
}
|
||||
}
|
||||
|
||||
return txn_resp;
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
#include "etcd/v3/AsyncCompareAndSwapAction.hpp"
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
#include "etcd/v3/Transaction.hpp"
|
||||
|
||||
using etcdserverpb::Compare;
|
||||
using etcdserverpb::RangeRequest;
|
||||
using etcdserverpb::PutRequest;
|
||||
using etcdserverpb::RequestOp;
|
||||
using etcdserverpb::ResponseOp;
|
||||
using etcdserverpb::TxnRequest;
|
||||
|
||||
etcdv3::AsyncCompareAndSwapAction::AsyncCompareAndSwapAction(etcdv3::ActionParameters param, etcdv3::Atomicity_Type type)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
etcdv3::Transaction transaction(parameters.key);
|
||||
if(type == etcdv3::Atomicity_Type::PREV_VALUE)
|
||||
{
|
||||
transaction.init_compare(parameters.old_value, Compare::CompareResult::Compare_CompareResult_EQUAL,
|
||||
Compare::CompareTarget::Compare_CompareTarget_VALUE);
|
||||
}
|
||||
else if (type == etcdv3::Atomicity_Type::PREV_INDEX)
|
||||
{
|
||||
transaction.init_compare(parameters.old_revision, Compare::CompareResult::Compare_CompareResult_EQUAL,
|
||||
Compare::CompareTarget::Compare_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;
|
||||
|
||||
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);
|
||||
txn_resp.set_action(etcdv3::COMPARESWAP_ACTION);
|
||||
|
||||
//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(101);
|
||||
txn_resp.set_error_message("Compare failed");
|
||||
}
|
||||
}
|
||||
|
||||
return txn_resp;
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#include "etcd/v3/AsyncDeleteAction.hpp"
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
|
||||
using etcdserverpb::DeleteRangeRequest;
|
||||
|
||||
etcdv3::AsyncDeleteAction::AsyncDeleteAction(ActionParameters param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
DeleteRangeRequest del_request;
|
||||
del_request.set_key(parameters.key);
|
||||
del_request.set_prev_kv(true);
|
||||
if(parameters.withPrefix)
|
||||
{
|
||||
if (parameters.key.empty()) {
|
||||
del_request.set_range_end(detail::string_plus_one("\0"));
|
||||
} else {
|
||||
del_request.set_range_end(detail::string_plus_one(parameters.key));
|
||||
}
|
||||
}
|
||||
if(!parameters.range_end.empty()) {
|
||||
del_request.set_range_end(parameters.range_end);
|
||||
}
|
||||
|
||||
response_reader = parameters.kv_stub->AsyncDeleteRange(&context, del_request, &cq_);
|
||||
response_reader->Finish(&reply, &status, (void*)this);
|
||||
}
|
||||
|
||||
etcdv3::AsyncDeleteRangeResponse etcdv3::AsyncDeleteAction::ParseResponse()
|
||||
{
|
||||
AsyncDeleteRangeResponse del_resp;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#include "etcd/v3/AsyncDeleteRangeResponse.hpp"
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
|
||||
|
||||
void etcdv3::AsyncDeleteRangeResponse::ParseResponse(std::string const& key, bool prefix, DeleteRangeResponse& resp)
|
||||
{
|
||||
index = resp.header().revision();
|
||||
|
||||
if(resp.prev_kvs_size() == 0)
|
||||
{
|
||||
error_code=100;
|
||||
error_message="Key not found";
|
||||
}
|
||||
else
|
||||
{
|
||||
action = etcdv3::DELETE_ACTION;
|
||||
//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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,49 +0,0 @@
|
|||
#include "etcd/v3/AsyncGetAction.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
|
||||
using etcdserverpb::RangeRequest;
|
||||
|
||||
etcdv3::AsyncGetAction::AsyncGetAction(etcdv3::ActionParameters param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
RangeRequest get_request;
|
||||
if (parameters.key.empty()) {
|
||||
get_request.set_key("\0");
|
||||
} else {
|
||||
get_request.set_key(parameters.key);
|
||||
}
|
||||
get_request.set_limit(param.limit);
|
||||
if(parameters.withPrefix)
|
||||
{
|
||||
if (parameters.key.empty()) {
|
||||
get_request.set_range_end(detail::string_plus_one("\0"));
|
||||
} else {
|
||||
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_sort_order(RangeRequest::SortOrder::RangeRequest_SortOrder_NONE);
|
||||
|
||||
response_reader = parameters.kv_stub->AsyncRange(&context,get_request,&cq_);
|
||||
response_reader->Finish(&reply, &status, (void*)this);
|
||||
}
|
||||
|
||||
etcdv3::AsyncRangeResponse etcdv3::AsyncGetAction::ParseResponse()
|
||||
{
|
||||
AsyncRangeResponse range_resp;
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
#include "etcd/v3/AsyncLeaseAction.hpp"
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
#include "etcd/v3/Transaction.hpp"
|
||||
|
||||
using etcdserverpb::LeaseGrantRequest;
|
||||
using etcdserverpb::LeaseRevokeRequest;
|
||||
using etcdserverpb::LeaseCheckpointRequest;
|
||||
using etcdserverpb::LeaseKeepAliveRequest;
|
||||
using etcdserverpb::LeaseTimeToLiveRequest;
|
||||
using etcdserverpb::LeaseLeasesRequest;
|
||||
|
||||
etcdv3::AsyncLeaseGrantAction::AsyncLeaseGrantAction(etcdv3::ActionParameters param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
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;
|
||||
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 param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
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;
|
||||
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 param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
isCancelled = false;
|
||||
stream = parameters.lease_stub->AsyncLeaseKeepAlive(&context, &cq_, (void*)"keepalive create");
|
||||
|
||||
void *got_tag = nullptr;
|
||||
bool ok = false;
|
||||
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)"keepalive create") {
|
||||
// ok
|
||||
} else {
|
||||
throw std::runtime_error("Failed to create a lease keep-alive connection");
|
||||
}
|
||||
}
|
||||
|
||||
etcdv3::AsyncLeaseKeepAliveResponse etcdv3::AsyncLeaseKeepAliveAction::ParseResponse()
|
||||
{
|
||||
AsyncLeaseKeepAliveResponse lease_resp;
|
||||
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::mutex> scope_lock(this->protect_is_cancelled);
|
||||
|
||||
auto start_timepoint = std::chrono::high_resolution_clock::now();
|
||||
if (isCancelled) {
|
||||
auto resp = ParseResponse();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now() - start_timepoint);
|
||||
return etcd::Response(resp, duration);
|
||||
}
|
||||
|
||||
LeaseKeepAliveRequest leasekeepalive_request;
|
||||
leasekeepalive_request.set_id(parameters.lease_id);
|
||||
|
||||
void *got_tag = nullptr;
|
||||
bool ok = false;
|
||||
|
||||
stream->Write(leasekeepalive_request, (void *)"keepalive write");
|
||||
// wait write finish
|
||||
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)"keepalive write") {
|
||||
stream->Read(&reply, (void*)"keepalive read");
|
||||
// wait read finish
|
||||
if (cq_.Next(&got_tag, &ok) && ok && got_tag == (void *)"keepalive read") {
|
||||
auto resp = ParseResponse();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now() - start_timepoint);
|
||||
return etcd::Response(resp, duration);
|
||||
}
|
||||
}
|
||||
return etcd::Response(grpc::StatusCode::ABORTED, "Failed to create a lease keep-alive connection");
|
||||
}
|
||||
|
||||
void etcdv3::AsyncLeaseKeepAliveAction::CancelKeepAlive()
|
||||
{
|
||||
std::lock_guard<std::mutex> scope_lock(this->protect_is_cancelled);
|
||||
if(isCancelled == false)
|
||||
{
|
||||
isCancelled = true;
|
||||
stream->WritesDone((void*)"keepalive done");
|
||||
grpc::Status status;
|
||||
stream->Finish(&status, (void *)this);
|
||||
cq_.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool etcdv3::AsyncLeaseKeepAliveAction::Cancelled() const
|
||||
{
|
||||
return isCancelled;
|
||||
}
|
||||
|
||||
etcdv3::AsyncLeaseTimeToLiveAction::AsyncLeaseTimeToLiveAction(etcdv3::ActionParameters param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
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;
|
||||
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 param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#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();
|
||||
// FIXME: only the first leases is recorded.
|
||||
if (resp.leases_size() > 0) {
|
||||
value.kvs.set_lease(resp.leases(0).id());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#include "etcd/v3/AsyncLockAction.hpp"
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
|
||||
using v3lockpb::LockRequest;
|
||||
using v3lockpb::UnlockRequest;
|
||||
|
||||
etcdv3::AsyncLockAction::AsyncLockAction(ActionParameters param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
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;
|
||||
|
||||
if(!status.ok())
|
||||
{
|
||||
lock_resp.set_error_code(status.error_code());
|
||||
lock_resp.set_error_message(status.error_message());
|
||||
}
|
||||
else
|
||||
{
|
||||
lock_resp.ParseResponse(reply);
|
||||
lock_resp.set_action(etcdv3::LOCK_ACTION);
|
||||
}
|
||||
|
||||
return lock_resp;
|
||||
}
|
||||
|
||||
etcdv3::AsyncUnlockAction::AsyncUnlockAction(ActionParameters param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
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;
|
||||
|
||||
if(!status.ok())
|
||||
{
|
||||
unlock_resp.set_error_code(status.error_code());
|
||||
unlock_resp.set_error_message(status.error_message());
|
||||
}
|
||||
else
|
||||
{
|
||||
unlock_resp.ParseResponse(reply);
|
||||
unlock_resp.set_action(etcdv3::UNLOCK_ACTION);
|
||||
}
|
||||
|
||||
return unlock_resp;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#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();
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
#include "etcd/v3/AsyncRangeResponse.hpp"
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
|
||||
|
||||
void etcdv3::AsyncRangeResponse::ParseResponse(RangeResponse& resp, bool prefix)
|
||||
{
|
||||
action = etcdv3::GET_ACTION;
|
||||
index = resp.header().revision();
|
||||
if(resp.kvs_size() == 0 && !prefix)
|
||||
{
|
||||
error_code=100;
|
||||
error_message="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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
#include "etcd/v3/AsyncSetAction.hpp"
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
#include "etcd/v3/Transaction.hpp"
|
||||
|
||||
using etcdserverpb::Compare;
|
||||
|
||||
etcdv3::AsyncSetAction::AsyncSetAction(etcdv3::ActionParameters param, bool create)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
etcdv3::Transaction transaction(parameters.key);
|
||||
isCreate = create;
|
||||
transaction.init_compare(Compare::CompareResult::Compare_CompareResult_EQUAL,
|
||||
Compare::CompareTarget::Compare_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;
|
||||
|
||||
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);
|
||||
std::string action = isCreate? etcdv3::CREATE_ACTION:etcdv3::SET_ACTION;
|
||||
txn_resp.set_action(action);
|
||||
|
||||
if(!reply.succeeded() && action == etcdv3::CREATE_ACTION)
|
||||
{
|
||||
txn_resp.set_error_code(105);
|
||||
txn_resp.set_error_message("Key already exists");
|
||||
}
|
||||
}
|
||||
return txn_resp;
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#include "etcd/v3/action_constants.hpp"
|
||||
#include "etcd/v3/AsyncTxnAction.hpp"
|
||||
#include "etcd/v3/Transaction.hpp"
|
||||
|
||||
|
||||
etcdv3::AsyncTxnAction::AsyncTxnAction(etcdv3::ActionParameters param, etcdv3::Transaction const &tx)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
txn_resp.set_action(etcdv3::TXN_ACTION);
|
||||
|
||||
//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(101);
|
||||
txn_resp.set_error_message("compare failed");
|
||||
}
|
||||
}
|
||||
|
||||
return txn_resp;
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
#include "etcd/v3/AsyncTxnResponse.hpp"
|
||||
#include "etcd/v3/AsyncRangeResponse.hpp"
|
||||
#include "etcd/v3/AsyncDeleteRangeResponse.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())
|
||||
{
|
||||
AsyncDeleteRangeResponse 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#include "etcd/v3/AsyncUpdateAction.hpp"
|
||||
#include "etcd/v3/AsyncRangeResponse.hpp"
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
#include "etcd/v3/Transaction.hpp"
|
||||
|
||||
using etcdserverpb::Compare;
|
||||
using etcdserverpb::RangeRequest;
|
||||
using etcdserverpb::PutRequest;
|
||||
using etcdserverpb::RequestOp;
|
||||
using etcdserverpb::ResponseOp;
|
||||
using etcdserverpb::TxnRequest;
|
||||
|
||||
etcdv3::AsyncUpdateAction::AsyncUpdateAction(etcdv3::ActionParameters param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
etcdv3::Transaction transaction(parameters.key);
|
||||
transaction.init_compare(Compare::CompareResult::Compare_CompareResult_GREATER,
|
||||
Compare::CompareTarget::Compare_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(100);
|
||||
txn_resp.set_error_message("Key not found");
|
||||
}
|
||||
|
||||
}
|
||||
return txn_resp;
|
||||
}
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
#include "etcd/v3/AsyncWatchAction.hpp"
|
||||
#include "etcd/v3/action_constants.hpp"
|
||||
|
||||
|
||||
using etcdserverpb::RangeRequest;
|
||||
using etcdserverpb::RangeResponse;
|
||||
using etcdserverpb::WatchCreateRequest;
|
||||
|
||||
etcdv3::AsyncWatchAction::AsyncWatchAction(etcdv3::ActionParameters param)
|
||||
: etcdv3::Action(param)
|
||||
{
|
||||
isCancelled.store(false);
|
||||
stream = parameters.watch_stub->AsyncWatch(&context,&cq_,(void*)"create");
|
||||
|
||||
WatchRequest watch_req;
|
||||
WatchCreateRequest watch_create_req;
|
||||
watch_create_req.set_key(parameters.key);
|
||||
watch_create_req.set_prev_kv(true);
|
||||
watch_create_req.set_start_revision(parameters.revision);
|
||||
|
||||
if(parameters.withPrefix)
|
||||
{
|
||||
if (parameters.key.empty()) {
|
||||
watch_create_req.set_range_end(detail::string_plus_one("\0"));
|
||||
} else {
|
||||
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_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 *)"create") {
|
||||
stream->Write(watch_req, (void *)"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 *)"write") {
|
||||
stream->Read(&reply, (void*)this);
|
||||
} 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*)"writes done") {
|
||||
isCancelled.store(true);
|
||||
cq_.Shutdown();
|
||||
break;
|
||||
}
|
||||
if(got_tag == (void*)this) // read tag
|
||||
{
|
||||
if (reply.canceled()) {
|
||||
isCancelled.store(true);
|
||||
cq_.Shutdown();
|
||||
}
|
||||
else if ((reply.created() && reply.header().revision() < parameters.revision) ||
|
||||
reply.events_size() > 0) {
|
||||
// we stop watch under two conditions:
|
||||
//
|
||||
// 1. watch for a future revision, return immediately with empty events set
|
||||
// 2. receive any effective events.
|
||||
isCancelled.store(true);
|
||||
stream->WritesDone((void*)"writes done");
|
||||
grpc::Status status;
|
||||
stream->Finish(&status, (void *)this);
|
||||
cq_.Shutdown();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, start next round read-reply
|
||||
stream->Read(&reply, (void*)this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void etcdv3::AsyncWatchAction::CancelWatch()
|
||||
{
|
||||
std::lock_guard<std::mutex> scope_lock(this->protect_is_cancalled);
|
||||
if (!isCancelled.exchange(true)) {
|
||||
stream->WritesDone((void*)"writes done");
|
||||
grpc::Status status;
|
||||
stream->Finish(&status, (void *)this);
|
||||
cq_.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
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*)"writes done")
|
||||
{
|
||||
isCancelled.store(true);
|
||||
cq_.Shutdown();
|
||||
break;
|
||||
}
|
||||
else if(got_tag == (void*)this) // read tag
|
||||
{
|
||||
if (reply.canceled()) {
|
||||
isCancelled.store(true);
|
||||
cq_.Shutdown();
|
||||
if (reply.compact_revision() != 0) {
|
||||
callback(etcd::Response(grpc::StatusCode::OUT_OF_RANGE /* error code */,
|
||||
"required revision has been compacted"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(reply.events_size())
|
||||
{
|
||||
// for the callback case, we don't stop immediately if watching for a future revison,
|
||||
// we wait until there are some expected events.
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
etcdv3::AsyncWatchResponse etcdv3::AsyncWatchAction::ParseResponse()
|
||||
{
|
||||
AsyncWatchResponse watch_resp;
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#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";
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,7 @@
|
|||
#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; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
#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; }
|
||||
|
|
@ -2,180 +2,359 @@
|
|||
|
||||
#include "proto/rpc.grpc.pb.h"
|
||||
|
||||
using etcdserverpb::Compare;
|
||||
using etcdserverpb::RangeRequest;
|
||||
using etcdserverpb::PutRequest;
|
||||
using etcdserverpb::RequestOp;
|
||||
using etcdserverpb::DeleteRangeRequest;
|
||||
#include "etcd/v3/Action.hpp"
|
||||
|
||||
namespace etcdv3 {
|
||||
|
||||
namespace detail {
|
||||
|
||||
static etcdserverpb::Compare::CompareResult to_compare_result(CompareResult r) {
|
||||
return static_cast<etcdserverpb::Compare::CompareResult>(static_cast<int>(r));
|
||||
}
|
||||
|
||||
static etcdserverpb::Compare::CompareTarget to_compare_target(CompareTarget t) {
|
||||
return static_cast<etcdserverpb::Compare::CompareTarget>(static_cast<int>(t));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace etcdv3
|
||||
|
||||
etcdv3::Transaction::Transaction() {
|
||||
txn_request.reset(new etcdserverpb::TxnRequest{});
|
||||
txn_request.reset(new etcdserverpb::TxnRequest{});
|
||||
}
|
||||
|
||||
etcdv3::Transaction::Transaction(const std::string& key) : key(key) {
|
||||
txn_request.reset(new etcdserverpb::TxnRequest{});
|
||||
etcdv3::Transaction::~Transaction() {}
|
||||
|
||||
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::init_compare(Compare::CompareResult result, Compare::CompareTarget target){
|
||||
Compare* compare = txn_request->add_compare();
|
||||
compare->set_result(result);
|
||||
compare->set_target(target);
|
||||
compare->set_key(key);
|
||||
|
||||
compare->set_version(0);
|
||||
void etcdv3::Transaction::add_compare_version(std::string const& key,
|
||||
int64_t const& version,
|
||||
std::string const& range_end) {
|
||||
this->add_compare_version(key, CompareResult::EQUAL, version, range_end);
|
||||
}
|
||||
|
||||
void etcdv3::Transaction::init_compare(std::string const& old_value, Compare::CompareResult result, Compare::CompareTarget target){
|
||||
Compare* compare = txn_request->add_compare();
|
||||
compare->set_result(result);
|
||||
compare->set_target(target);
|
||||
compare->set_key(key);
|
||||
|
||||
compare->set_value(old_value);
|
||||
void etcdv3::Transaction::add_compare_version(std::string const& key,
|
||||
CompareResult const& result,
|
||||
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_target(detail::to_compare_target(CompareTarget::VERSION));
|
||||
compare->set_key(key);
|
||||
compare->set_version(version);
|
||||
compare->set_range_end(range_end);
|
||||
}
|
||||
|
||||
void etcdv3::Transaction::init_compare(int old_index, Compare::CompareResult result, Compare::CompareTarget target){
|
||||
Compare* compare = txn_request->add_compare();
|
||||
compare->set_result(result);
|
||||
compare->set_target(target);
|
||||
compare->set_key(key);
|
||||
|
||||
compare->set_mod_revision(old_index);
|
||||
void etcdv3::Transaction::add_compare_create(std::string const& key,
|
||||
int64_t const& create_revision,
|
||||
std::string const& range_end) {
|
||||
this->add_compare_create(key, CompareResult::EQUAL, create_revision,
|
||||
range_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* get key on failure
|
||||
*/
|
||||
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_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_target(detail::to_compare_target(CompareTarget::CREATE));
|
||||
compare->set_key(key);
|
||||
compare->set_create_revision(create_revision);
|
||||
compare->set_range_end(range_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* get key on failure, get key before put, modify and then get updated key
|
||||
*/
|
||||
void etcdv3::Transaction::setup_set_failure_operation(std::string const &key, std::string const &value, int64_t leaseid) {
|
||||
std::unique_ptr<PutRequest> put_request(new PutRequest());
|
||||
put_request->set_key(key);
|
||||
put_request->set_value(value);
|
||||
put_request->set_prev_kv(true);
|
||||
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_compare_mod(std::string const& key,
|
||||
int64_t const& mod_revision,
|
||||
std::string const& range_end) {
|
||||
this->add_compare_mod(key, CompareResult::EQUAL, mod_revision, range_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* add key and then get new value of key
|
||||
*/
|
||||
void etcdv3::Transaction::setup_basic_create_sequence(std::string const& key, std::string const& value, int64_t leaseid) {
|
||||
std::unique_ptr<PutRequest> put_request(new PutRequest());
|
||||
put_request->set_key(key);
|
||||
put_request->set_value(value);
|
||||
put_request->set_prev_kv(true);
|
||||
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_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_target(detail::to_compare_target(CompareTarget::MOD));
|
||||
compare->set_key(key);
|
||||
compare->set_mod_revision(mod_revision);
|
||||
compare->set_range_end(range_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* get key value then modify and get new value
|
||||
*/
|
||||
void etcdv3::Transaction::setup_compare_and_swap_sequence(std::string const& value, int64_t leaseid) {
|
||||
std::unique_ptr<PutRequest> put_request(new PutRequest());
|
||||
put_request->set_key(key);
|
||||
put_request->set_value(value);
|
||||
put_request->set_prev_kv(true);
|
||||
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_compare_value(std::string const& key,
|
||||
std::string const& value,
|
||||
std::string const& range_end) {
|
||||
this->add_compare_value(key, CompareResult::EQUAL, value, range_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* get key, delete
|
||||
*/
|
||||
void etcdv3::Transaction::setup_delete_sequence(std::string const &key, std::string const &range_end, bool recursive) {
|
||||
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);
|
||||
}
|
||||
|
||||
RequestOp* req_success = txn_request->add_success();
|
||||
req_success->set_allocated_request_delete_range(del_request.release());
|
||||
void etcdv3::Transaction::add_compare_value(std::string const& key,
|
||||
CompareResult const& result,
|
||||
std::string const& value,
|
||||
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::VALUE));
|
||||
compare->set_key(key);
|
||||
compare->set_value(value);
|
||||
compare->set_range_end(range_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* get key, delete
|
||||
*/
|
||||
void etcdv3::Transaction::setup_delete_failure_operation(std::string const &key, std::string const &range_end, bool recursive) {
|
||||
std::unique_ptr<RangeRequest> get_request(new RangeRequest());
|
||||
std::unique_ptr<DeleteRangeRequest> del_request(new DeleteRangeRequest());
|
||||
get_request.reset(new RangeRequest());
|
||||
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);
|
||||
}
|
||||
|
||||
req_failure = txn_request->add_failure();
|
||||
req_failure->set_allocated_request_delete_range(del_request.release());
|
||||
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::setup_compare_and_delete_operation(std::string const& key) {
|
||||
std::unique_ptr<DeleteRangeRequest> del_request(new DeleteRangeRequest());
|
||||
del_request->set_key(key);
|
||||
del_request->set_prev_kv(true);
|
||||
RequestOp* req_success = txn_request->add_success();
|
||||
req_success->set_allocated_request_delete_range(del_request.release());
|
||||
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::setup_put(std::string const &key, std::string const &value) {
|
||||
std::unique_ptr<PutRequest> put_request(new PutRequest());
|
||||
put_request->set_key(key);
|
||||
put_request->set_value(value);
|
||||
put_request->set_prev_kv(false);
|
||||
RequestOp* req_success = txn_request->add_success();
|
||||
req_success->set_allocated_request_put(put_request.release());
|
||||
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::setup_delete(std::string const &key) {
|
||||
std::unique_ptr<DeleteRangeRequest> del_request(new DeleteRangeRequest());
|
||||
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::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_value(value);
|
||||
put_request->set_prev_kv(prev_kv);
|
||||
put_request->set_lease(leaseid);
|
||||
}
|
||||
|
||||
etcdv3::Transaction::~Transaction() {
|
||||
void etcdv3::Transaction::add_success_delete(std::string const& key,
|
||||
std::string const& range_end,
|
||||
bool const recursive,
|
||||
const bool prev_kv) {
|
||||
auto succ = txn_request->add_success();
|
||||
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_value(value);
|
||||
put_request->set_prev_kv(prev_kv);
|
||||
put_request->set_lease(leaseid);
|
||||
}
|
||||
|
||||
void etcdv3::Transaction::add_failure_delete(std::string const& key,
|
||||
std::string const& range_end,
|
||||
bool const recursive,
|
||||
const bool prev_kv) {
|
||||
auto fail = txn_request->add_failure();
|
||||
auto del_request = fail->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_failure_txn(
|
||||
const std::shared_ptr<Transaction> txn) {
|
||||
auto fail = txn_request->add_failure();
|
||||
auto txn_request = fail->mutable_request_txn();
|
||||
txn_request->CopyFrom(*txn->txn_request);
|
||||
}
|
||||
|
||||
void etcdv3::Transaction::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) {
|
||||
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,
|
||||
std::string const& create_key,
|
||||
std::string const& value,
|
||||
int64_t const leaseid) {
|
||||
this->add_compare_value(key, CompareResult::NOT_EQUAL, prev_value);
|
||||
this->add_success_put(create_key, value, leaseid);
|
||||
this->add_failure_range(key);
|
||||
}
|
||||
|
||||
void etcdv3::Transaction::setup_compare_and_swap(std::string const& key,
|
||||
std::string const& prev_value,
|
||||
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,
|
||||
std::string const& prev_value,
|
||||
std::string const& value,
|
||||
int64_t const leaseid) {
|
||||
this->add_compare_value(key, CompareResult::NOT_EQUAL, prev_value);
|
||||
this->add_success_put(key, value, leaseid);
|
||||
this->add_failure_range(key);
|
||||
}
|
||||
|
||||
void etcdv3::Transaction::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) {
|
||||
this->add_compare_value(key, CompareResult::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_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) {
|
||||
this->add_success_delete(key, "", false,
|
||||
true /* for backwards compatibility */);
|
||||
}
|
||||
|
||||
void etcdv3::Transaction::setup_delete(std::string const& key,
|
||||
std::string const& range_end,
|
||||
const bool recursive) {
|
||||
this->add_success_delete(key, range_end, recursive,
|
||||
true /* for backwards compatibility */);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,74 +1,86 @@
|
|||
#include "etcd/v3/V3Response.hpp"
|
||||
#include "etcd/v3/Member.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;
|
||||
}
|
||||
|
||||
int 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;
|
||||
}
|
||||
|
||||
void etcdv3::V3Response::set_action(std::string action)
|
||||
{
|
||||
void etcdv3::V3Response::set_action(std::string 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;
|
||||
}
|
||||
|
||||
std::vector<etcdv3::KeyValue> const & etcdv3::V3Response::get_prev_values() const
|
||||
{
|
||||
std::vector<etcdv3::KeyValue> const& etcdv3::V3Response::get_prev_values()
|
||||
const {
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
return compact_revision;
|
||||
}
|
||||
|
||||
void etcdv3::V3Response::set_lock_key(std::string const &key) {
|
||||
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) {
|
||||
this->lock_key = key;
|
||||
}
|
||||
|
||||
std::string const & etcdv3::V3Response::get_lock_key() const {
|
||||
std::string const& etcdv3::V3Response::get_lock_key() const {
|
||||
return this->lock_key;
|
||||
}
|
||||
|
||||
std::vector<mvccpb::Event> const & etcdv3::V3Response::get_events() const {
|
||||
void etcdv3::V3Response::set_name(std::string const& name) {
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
std::string const& etcdv3::V3Response::get_name() const { return this->name; }
|
||||
|
||||
std::vector<mvccpb::Event> const& etcdv3::V3Response::get_events() const {
|
||||
return this->events;
|
||||
}
|
||||
|
||||
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_raft_term() const { return this->raft_term; }
|
||||
|
||||
std::vector<int64_t> const& etcdv3::V3Response::get_leases() const {
|
||||
return this->leases;
|
||||
}
|
||||
|
||||
std::vector<etcdv3::Member> const& etcdv3::V3Response::get_members() const {
|
||||
return this->members;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,72 @@
|
|||
#include "etcd/v3/action_constants.hpp"
|
||||
|
||||
char const * etcdv3::CREATE_ACTION = "create";
|
||||
char const * etcdv3::COMPARESWAP_ACTION = "compareAndSwap";
|
||||
char const * etcdv3::UPDATE_ACTION = "update";
|
||||
char const * etcdv3::SET_ACTION = "set";
|
||||
char const * etcdv3::GET_ACTION = "get";
|
||||
char const * etcdv3::DELETE_ACTION = "delete";
|
||||
char const * etcdv3::COMPAREDELETE_ACTION = "compareAndDelete";
|
||||
char const * etcdv3::LOCK_ACTION = "lock";
|
||||
char const * etcdv3::UNLOCK_ACTION = "unlock";
|
||||
char const * etcdv3::TXN_ACTION = "txn";
|
||||
char const* etcdv3::CREATE_ACTION = "create";
|
||||
char const* etcdv3::COMPARESWAP_ACTION = "compareAndSwap";
|
||||
char const* etcdv3::UPDATE_ACTION = "update";
|
||||
char const* etcdv3::SET_ACTION = "set";
|
||||
char const* etcdv3::GET_ACTION = "get";
|
||||
char const* etcdv3::PUT_ACTION = "set"; // alias
|
||||
char const* etcdv3::DELETE_ACTION = "delete";
|
||||
char const* etcdv3::COMPAREDELETE_ACTION = "compareAndDelete";
|
||||
char const* etcdv3::LOCK_ACTION = "lock";
|
||||
char const* etcdv3::UNLOCK_ACTION = "unlock";
|
||||
char const* etcdv3::TXN_ACTION = "txn";
|
||||
char const* etcdv3::WATCH_ACTION = "watch";
|
||||
|
||||
char const* etcdv3::LEASEGRANT = "leasegrant";
|
||||
char const* etcdv3::LEASEREVOKE = "leaserevoke";
|
||||
char const* etcdv3::LEASEKEEPALIVE = "leasekeepalive";
|
||||
char const* etcdv3::LEASETIMETOLIVE = "leasetimetolive";
|
||||
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::PROCLAIM_ACTION = "preclaim";
|
||||
char const* etcdv3::LEADER_ACTION = "leader";
|
||||
char const* etcdv3::OBSERVE_ACTION = "obverse";
|
||||
char const* etcdv3::RESIGN_ACTION = "resign";
|
||||
|
||||
// see: noPrefixEnd in etcd, however c++ doesn't allows naive '\0' inside
|
||||
// a string, thus we use std::string(1, '\x00') as the constructor.
|
||||
std::string const etcdv3::NUL = std::string(1, '\x00');
|
||||
|
||||
char const* etcdv3::KEEPALIVE_CREATE = "keepalive create";
|
||||
char const* etcdv3::KEEPALIVE_WRITE = "keepalive write";
|
||||
char const* etcdv3::KEEPALIVE_READ = "keepalive read";
|
||||
char const* etcdv3::KEEPALIVE_DONE = "keepalive done";
|
||||
char const* etcdv3::KEEPALIVE_FINISH = "keepalive finish";
|
||||
|
||||
char const* etcdv3::WATCH_CREATE = "watch create";
|
||||
char const* etcdv3::WATCH_WRITE = "watch write";
|
||||
char const* etcdv3::WATCH_WRITE_CANCEL = "watch write cancel";
|
||||
char const* etcdv3::WATCH_WRITES_DONE = "watch writes done";
|
||||
char const* etcdv3::WATCH_FINISH = "watch finish";
|
||||
|
||||
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_CANCELLED = 1;
|
||||
const int etcdv3::ERROR_GRPC_UNKNOWN = 2;
|
||||
const int etcdv3::ERROR_GRPC_INVALID_ARGUMENT = 3;
|
||||
const int etcdv3::ERROR_GRPC_DEADLINE_EXCEEDED = 4;
|
||||
const int etcdv3::ERROR_GRPC_NOT_FOUND = 5;
|
||||
const int etcdv3::ERROR_GRPC_ALREADY_EXISTS = 6;
|
||||
const int etcdv3::ERROR_GRPC_PERMISSION_DENIED = 7;
|
||||
const int etcdv3::ERROR_GRPC_UNAUTHENTICATED = 16;
|
||||
const int etcdv3::ERROR_GRPC_RESOURCE_EXHAUSTED = 8;
|
||||
const int etcdv3::ERROR_GRPC_FAILED_PRECONDITION = 9;
|
||||
const int etcdv3::ERROR_GRPC_ABORTED = 10;
|
||||
const int etcdv3::ERROR_GRPC_OUT_OF_RANGE = 11;
|
||||
const int etcdv3::ERROR_GRPC_UNIMPLEMENTED = 12;
|
||||
const int etcdv3::ERROR_GRPC_INTERNAL = 13;
|
||||
const int etcdv3::ERROR_GRPC_UNAVAILABLE = 14;
|
||||
const int etcdv3::ERROR_GRPC_DATA_LOSS = 15;
|
||||
|
||||
const int etcdv3::ERROR_KEY_NOT_FOUND = 100;
|
||||
const int etcdv3::ERROR_COMPARE_FAILED = 101;
|
||||
const int etcdv3::ERROR_KEY_ALREADY_EXISTS = 105;
|
||||
const int etcdv3::ERROR_ACTION_CANCELLED = 106;
|
||||
|
|
|
|||
|
|
@ -5,45 +5,50 @@
|
|||
|
||||
#include "etcd/Client.hpp"
|
||||
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
TEST_CASE("setup with auth")
|
||||
{
|
||||
etcd::Client *etcd = etcd::Client::WithUser("http://127.0.0.1:2379", "root", "root");
|
||||
TEST_CASE("setup with auth") {
|
||||
etcd::Client* etcd = etcd::Client::WithUser(etcd_url, "root", "root");
|
||||
etcd->rmdir("/test", true).wait();
|
||||
}
|
||||
|
||||
TEST_CASE("add a new key after authenticate")
|
||||
{
|
||||
etcd::Client *etcd = etcd::Client::WithUser("http://127.0.0.1:2379", "root", "root");
|
||||
TEST_CASE("add a new key after authenticate") {
|
||||
etcd::Client* etcd = etcd::Client::WithUser(etcd_url, "root", "root");
|
||||
etcd->rmdir("/test", true).wait();
|
||||
etcd::Response resp = etcd->add("/test/key1", "42").get();
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK("create" == resp.action());
|
||||
etcd::Value const & val = resp.value();
|
||||
etcd::Value const& val = resp.value();
|
||||
CHECK("42" == val.as_string());
|
||||
CHECK("/test/key1" == val.key());
|
||||
CHECK(!val.is_dir());
|
||||
CHECK(0 < val.created_index());
|
||||
CHECK(0 < val.modified_index());
|
||||
CHECK(1 == val.version());
|
||||
CHECK(0 < resp.index());
|
||||
CHECK(105 == etcd->add("/test/key1", "43").get().error_code()); // Key already exists
|
||||
CHECK(105 == etcd->add("/test/key1", "42").get().error_code()); // Key already exists
|
||||
CHECK("Key already exists" == etcd->add("/test/key1", "42").get().error_message());
|
||||
CHECK(
|
||||
etcd::ERROR_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-cpp-apiv3: key already exists" ==
|
||||
etcd->add("/test/key1", "42").get().error_message());
|
||||
}
|
||||
|
||||
TEST_CASE("read a value from etcd")
|
||||
{
|
||||
etcd::Client *etcd = etcd::Client::WithUser("http://127.0.0.1:2379", "root", "root");
|
||||
TEST_CASE("read a value from etcd") {
|
||||
etcd::Client* etcd = etcd::Client::WithUser(etcd_url, "root", "root");
|
||||
etcd::Response resp = etcd->get("/test/key1").get();
|
||||
CHECK("get" == resp.action());
|
||||
REQUIRE(resp.is_ok());
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK("42" == resp.value().as_string());
|
||||
CHECK("" == etcd->get("/test").get().value().as_string()); // key points to a directory
|
||||
CHECK("" == etcd->get("/test").get().value().as_string()); // key points to a
|
||||
// directory
|
||||
}
|
||||
|
||||
TEST_CASE("cleanup")
|
||||
{
|
||||
etcd::Client *etcd = etcd::Client::WithUser("http://127.0.0.1:2379", "root", "root");
|
||||
TEST_CASE("cleanup") {
|
||||
etcd::Client* etcd = etcd::Client::WithUser(etcd_url, "root", "root");
|
||||
REQUIRE(0 == etcd->rmdir("/test", true).get().error_code());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,34 @@ add_custom_target(check ${CMAKE_COMMAND} -E env CTEST_OUTPUT_ON_FAILURE=1
|
|||
${CMAKE_CTEST_COMMAND} -C $<CONFIG> --verbose
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
|
||||
add_custom_target(etcd_tests)
|
||||
add_dependencies(check etcd_tests)
|
||||
|
||||
file(GLOB TEST_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
|
||||
foreach(testfile ${TEST_FILES})
|
||||
string(REGEX MATCH "^(.*)\\.[^.]*$" dummy ${testfile})
|
||||
set(test_name ${CMAKE_MATCH_1})
|
||||
message(STATUS "Found unit_test - " ${test_name})
|
||||
add_executable(${test_name} ${CMAKE_CURRENT_SOURCE_DIR}/${testfile})
|
||||
if(BUILD_ETCD_TESTS)
|
||||
add_executable(${test_name} ${CMAKE_CURRENT_SOURCE_DIR}/${testfile})
|
||||
else()
|
||||
add_executable(${test_name} EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/${testfile})
|
||||
endif()
|
||||
use_cxx(${test_name})
|
||||
set_exceptions(${test_name})
|
||||
add_test(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}>)
|
||||
|
||||
set_property(TARGET ${test_name} PROPERTY CXX_STANDARD 11)
|
||||
target_include_directories(${test_name} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen)
|
||||
target_include_directories(${test_name} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen/proto)
|
||||
target_link_libraries(${test_name} etcd-cpp-api)
|
||||
target_link_libraries(${test_name} PRIVATE etcd-cpp-api)
|
||||
|
||||
add_dependencies(check ${test_name})
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.13)
|
||||
target_link_libraries(${test_name} PRIVATE -Wl,--no-as-needed -lSegFault -Wl,--as-needed)
|
||||
else()
|
||||
target_link_options(${test_name} PRIVATE -Wl,--no-as-needed -lSegFault -Wl,--as-needed)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_dependencies(etcd_tests ${test_name})
|
||||
endforeach()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "etcd/Client.hpp"
|
||||
#include "etcd/KeepAlive.hpp"
|
||||
#include "etcd/Response.hpp"
|
||||
#include "etcd/SyncClient.hpp"
|
||||
#include "etcd/Value.hpp"
|
||||
|
||||
static std::string etcd_uri = etcdv3::detail::resolve_etcd_endpoints(
|
||||
"http://127.0.0.1:2379,http://127.0.0.1:2479,http://127.0.0.1:2579");
|
||||
|
||||
TEST_CASE("campaign and leadership using keepalive") {
|
||||
etcd::Client etcd(etcd_uri);
|
||||
auto keepalive = etcd.leasekeepalive(5).get();
|
||||
auto lease_id = keepalive->Lease();
|
||||
|
||||
std::cout << lease_id << std::endl;
|
||||
std::string value = std::string("192.168.1.6:1880");
|
||||
auto resp1 = etcd.campaign("/leader", lease_id, value).get();
|
||||
if (0 == resp1.error_code()) {
|
||||
std::cout << "became leader: " << resp1.index() << std::endl;
|
||||
} else {
|
||||
std::cout << "error code: " << resp1.error_code()
|
||||
<< "error message: " << resp1.error_message() << std::endl;
|
||||
assert(false);
|
||||
}
|
||||
|
||||
std::cout << "finish campaign" << std::endl;
|
||||
|
||||
auto resp2 = etcd.leader("/leader").get();
|
||||
CHECK(0 == resp2.error_code());
|
||||
CHECK(value == resp2.value().as_string());
|
||||
CHECK(resp1.value().key() == resp2.value().key());
|
||||
std::cout << resp2.value().key() << std::endl;
|
||||
|
||||
std::cout << "finish leader" << std::endl;
|
||||
|
||||
auto resp3 = etcd.resign("/leader", resp1.value().lease(),
|
||||
resp1.value().key(), resp1.value().created_index())
|
||||
.get();
|
||||
CHECK(0 == resp3.error_code());
|
||||
|
||||
std::cout << "finish resign" << std::endl;
|
||||
}
|
||||
|
||||
TEST_CASE("concurrent campaign with grpc timeout") {
|
||||
std::string value1 = std::string("192.168.1.6:1880");
|
||||
std::string value2 = std::string("192.168.1.6:1890");
|
||||
|
||||
auto fn1 = [&]() {
|
||||
etcd::Client etcd(etcd_uri);
|
||||
auto keepalive = etcd.leasekeepalive(5).get();
|
||||
auto lease_id = keepalive->Lease();
|
||||
auto resp1 = etcd.campaign("/leader", lease_id, value1).get();
|
||||
CHECK(0 == resp1.error_code());
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
|
||||
// resign
|
||||
auto resp2 = etcd.resign("/leader", resp1.value().lease(),
|
||||
resp1.value().key(), resp1.value().created_index())
|
||||
.get();
|
||||
CHECK(0 == resp2.error_code());
|
||||
};
|
||||
|
||||
auto fn2 = [&]() {
|
||||
etcd::Client etcd(etcd_uri);
|
||||
auto keepalive = etcd.leasekeepalive(5).get();
|
||||
auto lease_id = keepalive->Lease();
|
||||
|
||||
// set client timeout
|
||||
etcd.set_grpc_timeout(std::chrono::seconds(3));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
auto resp1 = etcd.campaign("/leader", lease_id, value2).get();
|
||||
std::cout << resp1.error_code() << resp1.error_message() << std::endl;
|
||||
CHECK(0 != resp1.error_code());
|
||||
|
||||
// wait until success
|
||||
while (true) {
|
||||
resp1 = etcd.campaign("/leader", lease_id, value2).get();
|
||||
if (resp1.error_code() == 0) {
|
||||
// check value
|
||||
auto resp2 = etcd.leader("/leader").get();
|
||||
CHECK(0 == resp2.error_code());
|
||||
CHECK(value2 == resp2.value().as_string());
|
||||
CHECK(resp1.value().key() == resp2.value().key());
|
||||
|
||||
// break the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto resp2 = etcd.resign("/leader", resp1.value().lease(),
|
||||
resp1.value().key(), resp1.value().created_index())
|
||||
.get();
|
||||
CHECK(0 == resp2.error_code());
|
||||
};
|
||||
|
||||
std::thread t1(fn1);
|
||||
std::thread t2(fn2);
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "etcd/Client.hpp"
|
||||
#include "etcd/KeepAlive.hpp"
|
||||
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
TEST_CASE("setup") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.rmdir("/test", true).wait();
|
||||
}
|
||||
|
||||
TEST_CASE("campaign and resign") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
|
||||
auto keepalive = etcd.leasekeepalive(60).get();
|
||||
auto lease_id = keepalive->Lease();
|
||||
|
||||
// campaign
|
||||
auto resp1 = etcd.campaign("test", lease_id, "xxxx").get();
|
||||
REQUIRE(0 == resp1.error_code());
|
||||
|
||||
// leader
|
||||
{
|
||||
auto resp2 = etcd.leader("test").get();
|
||||
REQUIRE(0 == resp2.error_code());
|
||||
REQUIRE(resp1.value().key() == resp2.value().key());
|
||||
REQUIRE("xxxx" == resp2.value().as_string());
|
||||
}
|
||||
|
||||
// proclaim
|
||||
auto resp3 = etcd.proclaim("test", lease_id, resp1.value().key(),
|
||||
resp1.value().created_index(), "tttt")
|
||||
.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" == 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());
|
||||
}
|
||||
|
||||
TEST_CASE("campaign and observe") {
|
||||
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.rmdir("/test", true).get();
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
#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());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#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());
|
||||
}
|
||||
|
|
@ -5,38 +5,46 @@
|
|||
|
||||
#include "etcd/SyncClient.hpp"
|
||||
|
||||
static std::string etcd_uri("http://127.0.0.1:2379");
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
TEST_CASE("sync operations")
|
||||
{
|
||||
etcd::SyncClient etcd(etcd_uri);
|
||||
TEST_CASE("sync operations") {
|
||||
etcd::SyncClient etcd(etcd_url);
|
||||
etcd.rmdir("/test", true);
|
||||
|
||||
etcd::Response res;
|
||||
int64_t index;
|
||||
|
||||
// add
|
||||
CHECK(0 == etcd.add("/test/key1", "42").error_code());
|
||||
CHECK(105 == etcd.add("/test/key1", "42").error_code()); // Key already exists
|
||||
CHECK(etcd::ERROR_KEY_ALREADY_EXISTS ==
|
||||
etcd.add("/test/key1", "41").error_code()); // Key already exists
|
||||
CHECK("42" == etcd.get("/test/key1").value().as_string());
|
||||
|
||||
// modify
|
||||
CHECK(0 == etcd.modify("/test/key1", "43").error_code());
|
||||
CHECK(100 == etcd.modify("/test/key2", "43").error_code()); // Key not found
|
||||
CHECK("43" == etcd.modify("/test/key1", "42").prev_value().as_string());
|
||||
CHECK(etcd::ERROR_KEY_NOT_FOUND ==
|
||||
etcd.modify("/test/key2", "43").error_code()); // Key not found
|
||||
res = etcd.modify("/test/key1", "42");
|
||||
CHECK(0 == res.error_code());
|
||||
CHECK("43" == res.prev_value().as_string());
|
||||
|
||||
// set
|
||||
CHECK(0 == etcd.set("/test/key1", "43").error_code()); // overwrite
|
||||
CHECK(0 == etcd.set("/test/key2", "43").error_code()); // create new
|
||||
CHECK(0 == etcd.set("/test/key1", "43").error_code()); // overwrite
|
||||
CHECK(0 == etcd.set("/test/key2", "43").error_code()); // create new
|
||||
CHECK("43" == etcd.set("/test/key2", "44").prev_value().as_string());
|
||||
CHECK("" == etcd.set("/test/key3", "44").prev_value().as_string());
|
||||
CHECK("" == etcd.set("/test/key3", "44").prev_value().as_string());
|
||||
|
||||
// get
|
||||
CHECK("43" == etcd.get("/test/key1").value().as_string());
|
||||
CHECK("44" == etcd.get("/test/key2").value().as_string());
|
||||
CHECK("44" == etcd.get("/test/key3").value().as_string());
|
||||
CHECK(100 == etcd.get("/test/key4").error_code()); // key not found
|
||||
CHECK(etcd::ERROR_KEY_NOT_FOUND ==
|
||||
etcd.get("/test/key4").error_code()); // key not found
|
||||
|
||||
// rm
|
||||
CHECK(3 == etcd.ls("/test").keys().size());
|
||||
CHECK(0 == etcd.rm("/test/key1").error_code());
|
||||
CHECK(0 == etcd.rm("/test/key1").error_code());
|
||||
CHECK(2 == etcd.ls("/test").keys().size());
|
||||
|
||||
// ls
|
||||
|
|
@ -45,73 +53,87 @@ TEST_CASE("sync operations")
|
|||
etcd.set("/test/new_dir/key2", "value2");
|
||||
CHECK(2 == etcd.ls("/test/new_dir").keys().size());
|
||||
|
||||
// keys
|
||||
etcd.set("/test/new_dir/key1", "value1");
|
||||
etcd.set("/test/new_dir/key2", "value2");
|
||||
CHECK(2 == etcd.keys("/test/new_dir").keys().size());
|
||||
|
||||
// rmdir
|
||||
CHECK(100 == etcd.rmdir("/test/new_dir").error_code()); // key not found
|
||||
CHECK(etcd::ERROR_KEY_NOT_FOUND ==
|
||||
etcd.rmdir("/test/new_dir").error_code()); // key not found
|
||||
CHECK(0 == etcd.rmdir("/test/new_dir", true).error_code());
|
||||
|
||||
// compare and swap
|
||||
etcd.set("/test/key1", "42");
|
||||
int index = etcd.modify_if("/test/key1", "43", "42").index();
|
||||
CHECK(101 == etcd.modify_if("/test/key1", "44", "42").error_code());
|
||||
index = etcd.modify_if("/test/key1", "43", "42").index();
|
||||
CHECK(etcd::ERROR_COMPARE_FAILED ==
|
||||
etcd.modify_if("/test/key1", "44", "42").error_code());
|
||||
REQUIRE(etcd.modify_if("/test/key1", "44", index).is_ok());
|
||||
CHECK(101 == etcd.modify_if("/test/key1", "45", index).error_code());
|
||||
CHECK(etcd::ERROR_COMPARE_FAILED ==
|
||||
etcd.modify_if("/test/key1", "45", index).error_code());
|
||||
|
||||
// atomic compare-and-delete based on prevValue
|
||||
etcd.set("/test/key1", "42");
|
||||
CHECK(101 == etcd.rm_if("/test/key1", "43").error_code());
|
||||
CHECK(0 == etcd.rm_if("/test/key1", "42").error_code());
|
||||
CHECK(etcd::ERROR_COMPARE_FAILED ==
|
||||
etcd.rm_if("/test/key1", "43").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
|
||||
index = etcd.set("/test/key1", "42").index();
|
||||
CHECK(101 == etcd.rm_if("/test/key1", index - 1).error_code());
|
||||
CHECK(0 == etcd.rm_if("/test/key1", index).error_code());
|
||||
CHECK(etcd::ERROR_COMPARE_FAILED ==
|
||||
etcd.rm_if("/test/key1", index - 1).error_code());
|
||||
res = etcd.rm_if("/test/key1", index);
|
||||
CHECK(
|
||||
(0 == res.error_code() || etcd::ERROR_KEY_NOT_FOUND == res.error_code()));
|
||||
|
||||
//leasegrant
|
||||
etcd::Response res = etcd.leasegrant(60);
|
||||
// leasegrant
|
||||
res = etcd.leasegrant(60);
|
||||
REQUIRE(res.is_ok());
|
||||
CHECK(60 == res.value().ttl());
|
||||
CHECK(0 < res.value().lease());
|
||||
CHECK(0 < res.value().lease());
|
||||
int64_t leaseid = res.value().lease();
|
||||
|
||||
//add with lease
|
||||
// add with lease
|
||||
res = etcd.add("/test/key1111", "43", leaseid);
|
||||
REQUIRE(0 == res.error_code()); // overwrite
|
||||
REQUIRE(0 == res.error_code()); // overwrite
|
||||
CHECK("create" == res.action());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
|
||||
//set with lease
|
||||
// set with lease
|
||||
res = etcd.set("/test/key1", "43", leaseid);
|
||||
REQUIRE(0 == res.error_code());
|
||||
REQUIRE(0 == res.error_code());
|
||||
CHECK("set" == res.action());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
res = etcd.get("/test/key1");
|
||||
CHECK(leaseid == res.value().lease());
|
||||
|
||||
//modify with lease
|
||||
// modify with lease
|
||||
res = etcd.modify("/test/key1", "44", leaseid);
|
||||
REQUIRE(0 == res.error_code());
|
||||
REQUIRE(0 == res.error_code());
|
||||
CHECK("update" == res.action());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
CHECK("44" == res.value().as_string());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
CHECK("44" == res.value().as_string());
|
||||
|
||||
res = etcd.modify_if("/test/key1", "45", "44", leaseid);
|
||||
index = res.index();
|
||||
REQUIRE(res.is_ok());
|
||||
CHECK("compareAndSwap" == res.action());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
CHECK("45" == res.value().as_string());
|
||||
|
||||
res = etcd.modify_if("/test/key1", "44", index, leaseid);
|
||||
index = res.index();
|
||||
REQUIRE(res.is_ok());
|
||||
CHECK("compareAndSwap" == res.action());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
CHECK("44" == res.value().as_string());
|
||||
|
||||
REQUIRE(0 == etcd.rmdir("/test", true).error_code());
|
||||
}
|
||||
|
||||
TEST_CASE("wait for a value change")
|
||||
{
|
||||
etcd::SyncClient etcd(etcd_uri);
|
||||
TEST_CASE("wait for a value change") {
|
||||
etcd::SyncClient etcd(etcd_url);
|
||||
etcd.set("/test/key1", "42");
|
||||
|
||||
std::thread watch_thrd([&]() {
|
||||
|
|
@ -127,9 +149,8 @@ TEST_CASE("wait for a value change")
|
|||
REQUIRE(0 == etcd.rmdir("/test", true).error_code());
|
||||
}
|
||||
|
||||
TEST_CASE("wait for a directory change")
|
||||
{
|
||||
etcd::SyncClient etcd(etcd_uri);
|
||||
TEST_CASE("wait for a directory change") {
|
||||
etcd::SyncClient etcd(etcd_url);
|
||||
|
||||
std::thread watch_thrd1([&]() {
|
||||
etcd::Response res = etcd.watch("/test", true);
|
||||
|
|
@ -154,11 +175,10 @@ TEST_CASE("wait for a directory change")
|
|||
REQUIRE(0 == etcd.rmdir("/test", true).error_code());
|
||||
}
|
||||
|
||||
TEST_CASE("watch changes in the past")
|
||||
{
|
||||
etcd::SyncClient etcd(etcd_uri);
|
||||
TEST_CASE("watch changes in the past") {
|
||||
etcd::SyncClient etcd(etcd_url);
|
||||
|
||||
int index = etcd.set("/test/key1", "42").index();
|
||||
int64_t index = etcd.set("/test/key1", "42").index();
|
||||
|
||||
etcd.set("/test/key1", "43");
|
||||
etcd.set("/test/key1", "44");
|
||||
|
|
@ -181,7 +201,7 @@ TEST_CASE("watch changes in the past")
|
|||
|
||||
// TEST_CASE("request cancellation")
|
||||
// {
|
||||
// etcd::Client etcd(etcd_uri);
|
||||
// etcd::Client etcd(etcd_url);
|
||||
// etcd.set("/test/key1", "42").wait();
|
||||
|
||||
// pplx::task<etcd::Response> res = etcd.watch("/test/key1");
|
||||
|
|
|
|||
455
tst/EtcdTest.cpp
455
tst/EtcdTest.cpp
|
|
@ -7,89 +7,105 @@
|
|||
|
||||
#include "etcd/Client.hpp"
|
||||
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
TEST_CASE("setup")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("setup") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.rmdir("/test", true).wait();
|
||||
}
|
||||
|
||||
TEST_CASE("add a new key")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("add a new key") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.rmdir("/test", true).wait();
|
||||
etcd::Response resp = etcd.add("/test/key1", "42").get();
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK("create" == resp.action());
|
||||
etcd::Value const & val = resp.value();
|
||||
etcd::Value const& val = resp.value();
|
||||
CHECK("42" == val.as_string());
|
||||
CHECK("/test/key1" == val.key());
|
||||
CHECK(!val.is_dir());
|
||||
CHECK(0 < val.created_index());
|
||||
CHECK(0 < val.modified_index());
|
||||
CHECK(1 == val.version());
|
||||
CHECK(0 < resp.index());
|
||||
CHECK(105 == etcd.add("/test/key1", "43").get().error_code()); // Key already exists
|
||||
CHECK(105 == etcd.add("/test/key1", "42").get().error_code()); // Key already exists
|
||||
CHECK("Key already exists" == etcd.add("/test/key1", "42").get().error_message());
|
||||
CHECK(etcd::ERROR_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-cpp-apiv3: key already exists" ==
|
||||
etcd.add("/test/key1", "42").get().error_message());
|
||||
}
|
||||
|
||||
TEST_CASE("read a value from etcd")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("read a value from etcd") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd::Response resp = etcd.get("/test/key1").get();
|
||||
CHECK("get" == resp.action());
|
||||
REQUIRE(resp.is_ok());
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK("42" == resp.value().as_string());
|
||||
CHECK("" == etcd.get("/test").get().value().as_string()); // key points to a directory
|
||||
CHECK("" == etcd.get("/test").get().value().as_string()); // key points to a
|
||||
// directory
|
||||
}
|
||||
|
||||
TEST_CASE("simplified read")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("simplified read") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
CHECK("42" == etcd.get("/test/key1").get().value().as_string());
|
||||
CHECK(100 == etcd.get("/test/key2").get().error_code()); // Key not found
|
||||
CHECK("" == etcd.get("/test/key2").get().value().as_string()); // Key not found
|
||||
CHECK(etcd::ERROR_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
|
||||
}
|
||||
|
||||
TEST_CASE("modify a key")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd::Response resp = etcd.modify("/test/key1", "43").get();
|
||||
REQUIRE(0 == resp.error_code()); // overwrite
|
||||
TEST_CASE("modify a key") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
|
||||
// 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
|
||||
CHECK("update" == resp.action());
|
||||
CHECK(100 == etcd.modify("/test/key2", "43").get().error_code()); // Key not found
|
||||
CHECK(etcd::ERROR_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 previous
|
||||
resp = etcd.get("/test/key1", revision).get();
|
||||
REQUIRE(resp.is_ok());
|
||||
CHECK("42" == resp.value().as_string());
|
||||
}
|
||||
|
||||
TEST_CASE("set a key")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("set a key") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd::Response resp = etcd.set("/test/key1", "43").get();
|
||||
REQUIRE(0 == resp.error_code()); // overwrite
|
||||
REQUIRE(0 == resp.error_code()); // overwrite
|
||||
CHECK("set" == resp.action());
|
||||
CHECK(0 == etcd.set("/test/key2", "43").get().error_code()); // create new
|
||||
CHECK(0 == etcd.set("/test/key2", "43").get().error_code()); // create new
|
||||
CHECK("43" == etcd.set("/test/key2", "44").get().prev_value().as_string());
|
||||
CHECK("" == etcd.set("/test/key3", "44").get().prev_value().as_string());
|
||||
CHECK(0 == etcd.set("/test", "42").get().error_code()); // Not a file
|
||||
CHECK("" == etcd.set("/test/key3", "44").get().prev_value().as_string());
|
||||
CHECK(0 == etcd.set("/test", "42").get().error_code()); // Not a file
|
||||
|
||||
//set with ttl
|
||||
resp = etcd.set("/test/key1", "50", 10).get();
|
||||
REQUIRE(0 == resp.error_code()); // overwrite
|
||||
// set with ttl
|
||||
resp = etcd.set("/test/key1", "50").get();
|
||||
REQUIRE(0 == resp.error_code()); // overwrite
|
||||
CHECK("set" == resp.action());
|
||||
CHECK("43" == resp.prev_value().as_string());
|
||||
resp = etcd.get("/test/key1").get();
|
||||
CHECK("50" == resp.value().as_string());
|
||||
CHECK( 0 < resp.value().lease());
|
||||
CHECK(0 == resp.value().lease());
|
||||
}
|
||||
|
||||
TEST_CASE("atomic compare-and-swap")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("atomic compare-and-swap") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.set("/test/key1", "42").wait();
|
||||
|
||||
// modify success
|
||||
etcd::Response res = etcd.modify_if("/test/key1", "43", "42").get();
|
||||
int index = res.index();
|
||||
REQUIRE(res.is_ok());
|
||||
CHECK("compareAndSwap" == res.action());
|
||||
CHECK("43" == res.value().as_string());
|
||||
|
|
@ -97,48 +113,53 @@ TEST_CASE("atomic compare-and-swap")
|
|||
// modify fails the second time
|
||||
res = etcd.modify_if("/test/key1", "44", "42").get();
|
||||
CHECK(!res.is_ok());
|
||||
CHECK(101 == res.error_code());
|
||||
CHECK("Compare failed" == res.error_message());
|
||||
CHECK(etcd::ERROR_COMPARE_FAILED == res.error_code());
|
||||
CHECK("etcd-cpp-apiv3: compare failed" == res.error_message());
|
||||
|
||||
// modify fails the second time
|
||||
// modify fails on non-existing keys
|
||||
res = etcd.modify_if("/test/key222", "44", "42").get();
|
||||
CHECK(!res.is_ok());
|
||||
CHECK(100 == res.error_code());
|
||||
CHECK("Key not found" == res.error_message());
|
||||
CHECK(etcd::ERROR_COMPARE_FAILED == res.error_code());
|
||||
CHECK("etcd-cpp-apiv3: compare failed" == res.error_message());
|
||||
}
|
||||
|
||||
TEST_CASE("delete a value")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("delete a value") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd::Response resp = etcd.rm("/test/key11111").get();
|
||||
CHECK(!resp.is_ok());
|
||||
CHECK(100 == resp.error_code());
|
||||
CHECK("Key not found" == resp.error_message());
|
||||
CHECK(etcd::ERROR_KEY_NOT_FOUND == resp.error_code());
|
||||
CHECK("etcd-cpp-apiv3: key not found" == resp.error_message());
|
||||
|
||||
int64_t index = etcd.get("/test/key1").get().index();
|
||||
int64_t create_index = etcd.get("/test/key1").get().value().created_index();
|
||||
int64_t modify_index = etcd.get("/test/key1").get().value().modified_index();
|
||||
int64_t version = etcd.get("/test/key1").get().value().version();
|
||||
|
||||
int head_index = etcd.head().get().index();
|
||||
CHECK(index == head_index);
|
||||
|
||||
int index = etcd.get("/test/key1").get().index();
|
||||
int create_index = etcd.get("/test/key1").get().value().created_index();
|
||||
int modify_index = etcd.get("/test/key1").get().value().modified_index();
|
||||
resp = etcd.rm("/test/key1").get();
|
||||
CHECK("43" == resp.prev_value().as_string());
|
||||
CHECK( "/test/key1" == resp.prev_value().key());
|
||||
CHECK( create_index == resp.prev_value().created_index());
|
||||
CHECK( modify_index == resp.prev_value().modified_index());
|
||||
CHECK("/test/key1" == resp.prev_value().key());
|
||||
CHECK(create_index == resp.prev_value().created_index());
|
||||
CHECK(modify_index == resp.prev_value().modified_index());
|
||||
CHECK(version == resp.prev_value().version());
|
||||
CHECK("delete" == resp.action());
|
||||
CHECK( modify_index == resp.value().modified_index());
|
||||
CHECK( create_index == resp.value().created_index());
|
||||
CHECK("" == resp.value().as_string());
|
||||
CHECK( "/test/key1" == resp.value().key());
|
||||
CHECK(create_index == resp.value().created_index());
|
||||
CHECK(modify_index == resp.value().modified_index());
|
||||
CHECK(version == resp.value().version());
|
||||
CHECK("43" == resp.value().as_string());
|
||||
CHECK("/test/key1" == resp.value().key());
|
||||
}
|
||||
|
||||
TEST_CASE("atomic compare-and-delete based on prevValue")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("atomic compare-and-delete based on prevValue") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.set("/test/key1", "42").wait();
|
||||
|
||||
etcd::Response res = etcd.rm_if("/test/key1", "43").get();
|
||||
CHECK(!res.is_ok());
|
||||
CHECK(101 == res.error_code());
|
||||
CHECK("Compare failed" == res.error_message());
|
||||
CHECK(etcd::ERROR_COMPARE_FAILED == res.error_code());
|
||||
CHECK("etcd-cpp-apiv3: compare failed" == res.error_message());
|
||||
|
||||
res = etcd.rm_if("/test/key1", "42").get();
|
||||
REQUIRE(res.is_ok());
|
||||
|
|
@ -146,15 +167,14 @@ TEST_CASE("atomic compare-and-delete based on prevValue")
|
|||
CHECK("42" == res.prev_value().as_string());
|
||||
}
|
||||
|
||||
TEST_CASE("atomic compare-and-delete based on prevIndex")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
int index = etcd.set("/test/key1", "42").get().index();
|
||||
TEST_CASE("atomic compare-and-delete based on prevIndex") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
int64_t index = etcd.set("/test/key1", "42").get().index();
|
||||
|
||||
etcd::Response res = etcd.rm_if("/test/key1", index - 1).get();
|
||||
CHECK(!res.is_ok());
|
||||
CHECK(101 == res.error_code());
|
||||
CHECK("Compare failed" == res.error_message());
|
||||
CHECK(etcd::ERROR_COMPARE_FAILED == res.error_code());
|
||||
CHECK("etcd-cpp-apiv3: compare failed" == res.error_message());
|
||||
|
||||
res = etcd.rm_if("/test/key1", index).get();
|
||||
REQUIRE(res.is_ok());
|
||||
|
|
@ -162,15 +182,13 @@ TEST_CASE("atomic compare-and-delete based on prevIndex")
|
|||
CHECK("42" == res.prev_value().as_string());
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("deep atomic compare-and-swap")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("deep atomic compare-and-swap") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.set("/test/key1", "42").wait();
|
||||
|
||||
// modify success
|
||||
etcd::Response res = etcd.modify_if("/test/key1", "43", "42").get();
|
||||
int index = res.index();
|
||||
int64_t index = res.index();
|
||||
REQUIRE(res.is_ok());
|
||||
CHECK("compareAndSwap" == res.action());
|
||||
CHECK("43" == res.value().as_string());
|
||||
|
|
@ -178,8 +196,8 @@ TEST_CASE("deep atomic compare-and-swap")
|
|||
// modify fails the second time
|
||||
res = etcd.modify_if("/test/key1", "44", "42").get();
|
||||
CHECK(!res.is_ok());
|
||||
CHECK(101 == res.error_code());
|
||||
CHECK("Compare failed" == res.error_message());
|
||||
CHECK(etcd::ERROR_COMPARE_FAILED == res.error_code());
|
||||
CHECK("etcd-cpp-apiv3: compare failed" == res.error_message());
|
||||
|
||||
// succes with the correct index
|
||||
res = etcd.modify_if("/test/key1", "44", index).get();
|
||||
|
|
@ -190,13 +208,65 @@ TEST_CASE("deep atomic compare-and-swap")
|
|||
// index changes so second modify fails
|
||||
res = etcd.modify_if("/test/key1", "45", index).get();
|
||||
CHECK(!res.is_ok());
|
||||
CHECK(101 == res.error_code());
|
||||
CHECK("Compare failed" == res.error_message());
|
||||
CHECK(etcd::ERROR_COMPARE_FAILED == res.error_code());
|
||||
CHECK("etcd-cpp-apiv3: compare failed" == res.error_message());
|
||||
}
|
||||
|
||||
TEST_CASE("list a directory")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("using binary keys and values, raw char pointer doesn't work") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.rmdir("/test", true).wait();
|
||||
{
|
||||
etcd::Response resp = etcd.put("/test/key1\0xyz", "42\0foo").get();
|
||||
REQUIRE(resp.is_ok());
|
||||
}
|
||||
{
|
||||
// should not exist
|
||||
etcd::Response resp = etcd.get(std::string("/test/key1\0xyz", 14)).get();
|
||||
CHECK(!resp.is_ok());
|
||||
CHECK(etcd::ERROR_KEY_NOT_FOUND == resp.error_code());
|
||||
}
|
||||
{
|
||||
// should exist
|
||||
etcd::Response resp = etcd.get(std::string("/test/key1", 10)).get();
|
||||
REQUIRE(resp.is_ok());
|
||||
CHECK(std::string("42") == resp.value().as_string());
|
||||
CHECK(std::string("42\0foo", 6) != resp.value().as_string());
|
||||
}
|
||||
{
|
||||
// should exist, but different value
|
||||
etcd::Response resp = etcd.get("/test/key1\0xyz").get();
|
||||
REQUIRE(resp.is_ok());
|
||||
CHECK(std::string("42") == resp.value().as_string());
|
||||
CHECK(std::string("42\0foo", 6) != resp.value().as_string());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("using binary keys and values, std::string is ok for \\0") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.rmdir("/test", true).wait();
|
||||
{
|
||||
etcd::Response resp =
|
||||
etcd.put(std::string("/test/key1\0xyz", 14), std::string("42\0foo", 6))
|
||||
.get();
|
||||
REQUIRE(resp.is_ok());
|
||||
}
|
||||
{
|
||||
// should not exist
|
||||
etcd::Response resp = etcd.get("/test/key1").get();
|
||||
CHECK(!resp.is_ok());
|
||||
CHECK(etcd::ERROR_KEY_NOT_FOUND == resp.error_code());
|
||||
}
|
||||
{
|
||||
// should exist
|
||||
etcd::Response resp = etcd.get(std::string("/test/key1\0xyz", 14)).get();
|
||||
REQUIRE(resp.is_ok());
|
||||
CHECK(std::string("42") != resp.value().as_string());
|
||||
CHECK(std::string("42\0foo", 6) == resp.value().as_string());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("list a directory") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
CHECK(0 == etcd.ls("/test/new_dir").get().keys().size());
|
||||
|
||||
etcd.set("/test/new_dir/key1", "value1").wait();
|
||||
|
|
@ -223,12 +293,16 @@ TEST_CASE("list a directory")
|
|||
|
||||
CHECK(0 == etcd.ls("/test/new_dir/key1").get().error_code());
|
||||
|
||||
// list with empty prefix
|
||||
resp = etcd.ls("").get();
|
||||
CHECK(0 == resp.error_code());
|
||||
CHECK(0 < resp.keys().size());
|
||||
|
||||
CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok());
|
||||
}
|
||||
|
||||
TEST_CASE("list by range")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("list by range") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
CHECK(0 == etcd.ls("/test/new_dir").get().keys().size());
|
||||
|
||||
etcd.set("/test/new_dir/key0", "value0").wait();
|
||||
|
|
@ -237,13 +311,15 @@ TEST_CASE("list by range")
|
|||
etcd.set("/test/new_dir/key3", "value3").wait();
|
||||
etcd.set("/test/new_dir/key4", "value4").wait();
|
||||
|
||||
etcd::Response resp1 = etcd.ls("/test/new_dir/key1", "/test/new_dir/key3").get();
|
||||
etcd::Response resp1 =
|
||||
etcd.ls("/test/new_dir/key1", "/test/new_dir/key3").get();
|
||||
REQUIRE(resp1.is_ok());
|
||||
CHECK("get" == resp1.action());
|
||||
REQUIRE(2 == resp1.keys().size());
|
||||
REQUIRE(2 == resp1.values().size());
|
||||
|
||||
etcd::Response resp2 = etcd.ls("/test/new_dir/key1", "/test/new_dir/key4").get();
|
||||
etcd::Response resp2 =
|
||||
etcd.ls("/test/new_dir/key1", "/test/new_dir/key4").get();
|
||||
REQUIRE(resp2.is_ok());
|
||||
CHECK("get" == resp2.action());
|
||||
REQUIRE(3 == resp2.keys().size());
|
||||
|
|
@ -255,7 +331,10 @@ TEST_CASE("list by range")
|
|||
REQUIRE(4 == resp3.keys().size());
|
||||
REQUIRE(4 == resp3.values().size());
|
||||
|
||||
etcd::Response resp4 = etcd.ls("/test/new_dir/key1", etcdv3::detail::string_plus_one("/test/new_dir/key")).get();
|
||||
etcd::Response resp4 =
|
||||
etcd.ls("/test/new_dir/key1",
|
||||
etcdv3::detail::string_plus_one("/test/new_dir/key"))
|
||||
.get();
|
||||
REQUIRE(resp4.is_ok());
|
||||
CHECK("get" == resp4.action());
|
||||
REQUIRE(4 == resp4.keys().size());
|
||||
|
|
@ -266,19 +345,47 @@ TEST_CASE("list by range")
|
|||
CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok());
|
||||
}
|
||||
|
||||
TEST_CASE("delete a directory")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("list by range, w/o values") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
CHECK(0 == etcd.ls("/test/new_dir").get().keys().size());
|
||||
|
||||
etcd.set("/test/new_dir/key0", "value0").wait();
|
||||
etcd.set("/test/new_dir/key1", "value1").wait();
|
||||
etcd.set("/test/new_dir/key2", "value2").wait();
|
||||
etcd.set("/test/new_dir/key3", "value3").wait();
|
||||
etcd.set("/test/new_dir/key4", "value4").wait();
|
||||
|
||||
etcd::Response resp1 =
|
||||
etcd.ls("/test/new_dir/key1", "/test/new_dir/key2").get();
|
||||
REQUIRE(resp1.is_ok());
|
||||
CHECK("get" == resp1.action());
|
||||
REQUIRE(1 == resp1.keys().size());
|
||||
REQUIRE(1 == resp1.values().size());
|
||||
REQUIRE(resp1.values()[0].as_string() == "value1");
|
||||
|
||||
etcd::Response resp2 =
|
||||
etcd.keys("/test/new_dir/key1", "/test/new_dir/key2").get();
|
||||
REQUIRE(resp1.is_ok());
|
||||
CHECK("get" == resp2.action());
|
||||
REQUIRE(1 == resp2.keys().size());
|
||||
REQUIRE(1 == resp2.values().size());
|
||||
REQUIRE(resp2.values()[0].as_string() == "");
|
||||
|
||||
CHECK(etcd.rmdir("/test/new_dir", true).get().is_ok());
|
||||
}
|
||||
|
||||
TEST_CASE("delete a directory") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
|
||||
etcd.set("/test/new_dir/key1", "value1").wait();
|
||||
etcd.set("/test/new_dir/key2", "value2").wait();
|
||||
etcd.set("/test/new_dir/key3", "value3").wait();
|
||||
|
||||
CHECK(100 == etcd.rmdir("/test/new_dir").get().error_code()); // key not found
|
||||
CHECK(etcd::ERROR_KEY_NOT_FOUND ==
|
||||
etcd.rmdir("/test/new_dir").get().error_code()); // key not found
|
||||
etcd::Response resp = etcd.ls("/test/new_dir").get();
|
||||
|
||||
resp = etcd.rmdir("/test/new_dir", true).get();
|
||||
int index = resp.index();
|
||||
CHECK("delete" == resp.action());
|
||||
REQUIRE(3 == resp.keys().size());
|
||||
CHECK("/test/new_dir/key1" == resp.key(0));
|
||||
|
|
@ -286,23 +393,35 @@ TEST_CASE("delete a directory")
|
|||
CHECK("value1" == resp.value(0).as_string());
|
||||
CHECK("value2" == resp.value(1).as_string());
|
||||
|
||||
|
||||
resp = etcd.rmdir("/test/dirnotfound", true).get();
|
||||
CHECK(!resp.is_ok());
|
||||
CHECK(100 == resp.error_code());
|
||||
CHECK("Key not found" == resp.error_message());
|
||||
CHECK(etcd::ERROR_KEY_NOT_FOUND == resp.error_code());
|
||||
CHECK("etcd-cpp-apiv3: key not found" == resp.error_message());
|
||||
|
||||
resp = etcd.rmdir("/test/new_dir", false).get();
|
||||
CHECK(!resp.is_ok());
|
||||
CHECK(100 == resp.error_code());
|
||||
CHECK("Key not found" == resp.error_message());
|
||||
CHECK(etcd::ERROR_KEY_NOT_FOUND == resp.error_code());
|
||||
CHECK("etcd-cpp-apiv3: key not found" == resp.error_message());
|
||||
}
|
||||
|
||||
TEST_CASE("delete by range")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("delete all keys with rmdir(\"\", true)") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.rmdir("", true).wait();
|
||||
|
||||
CHECK(100 == etcd.rmdir("/test/new_dir").get().error_code()); // key not found
|
||||
etcd.set("/test/new_dir/key1", "value1").wait();
|
||||
etcd.set("/test/new_dir/key2", "value2").wait();
|
||||
etcd.set("/test/new_dir/key3", "value3").wait();
|
||||
|
||||
auto resp = etcd.rmdir("", true).get();
|
||||
CHECK(resp.is_ok());
|
||||
CHECK(resp.values().size() == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("delete by range") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
|
||||
CHECK(etcd::ERROR_KEY_NOT_FOUND ==
|
||||
etcd.rmdir("/test/new_dir").get().error_code()); // key not found
|
||||
etcd::Response resp = etcd.ls("/test/new_dir").get();
|
||||
|
||||
etcd.set("/test/new_dir/key1", "value1").wait();
|
||||
|
|
@ -319,9 +438,8 @@ TEST_CASE("delete by range")
|
|||
CHECK("value2" == resp.value(1).as_string());
|
||||
}
|
||||
|
||||
TEST_CASE("wait for a value change")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("wait for a value change") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.set("/test/key1", "42").wait();
|
||||
|
||||
pplx::task<etcd::Response> res = etcd.watch("/test/key1");
|
||||
|
|
@ -337,9 +455,8 @@ TEST_CASE("wait for a value change")
|
|||
CHECK("42" == res.get().prev_value().as_string());
|
||||
}
|
||||
|
||||
TEST_CASE("wait for a directory change")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("wait for a directory change") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
|
||||
pplx::task<etcd::Response> res = etcd.watch("/test", true);
|
||||
CHECK(!res.is_done());
|
||||
|
|
@ -364,16 +481,18 @@ TEST_CASE("wait for a directory change")
|
|||
CHECK("45" == res2.get().value().as_string());
|
||||
}
|
||||
|
||||
TEST_CASE("watch changes in the past")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("watch changes in the past") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
|
||||
int index = etcd.set("/test/key1", "42").get().index();
|
||||
int64_t index = etcd.set("/test/key1", "42").get().index();
|
||||
|
||||
etcd.set("/test/key1", "43").wait();
|
||||
etcd.set("/test/key1", "44").wait();
|
||||
etcd.set("/test/key1", "45").wait();
|
||||
|
||||
int64_t head_index = etcd.head().get().index();
|
||||
CHECK(index + 3 == head_index);
|
||||
|
||||
etcd::Response res = etcd.watch("/test/key1", ++index).get();
|
||||
CHECK("set" == res.action());
|
||||
CHECK("43" == res.value().as_string());
|
||||
|
|
@ -388,17 +507,19 @@ TEST_CASE("watch changes in the past")
|
|||
CHECK("45" == res.value().as_string());
|
||||
}
|
||||
|
||||
TEST_CASE("watch range changes in the past")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("watch range changes in the past") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
|
||||
int index = etcd.set("/test/key1", "42").get().index();
|
||||
int64_t index = etcd.set("/test/key1", "42").get().index();
|
||||
|
||||
etcd.set("/test/key1", "43").wait();
|
||||
etcd.set("/test/key2", "44").wait();
|
||||
etcd.set("/test/key3", "45").wait();
|
||||
etcd.set("/test/key4", "45").wait();
|
||||
|
||||
int64_t head_index = etcd.head().get().index();
|
||||
CHECK(index + 4 == head_index);
|
||||
|
||||
etcd::Response res;
|
||||
|
||||
res = etcd.watch("/test/key1", "/test/key4", index).get();
|
||||
|
|
@ -412,98 +533,120 @@ TEST_CASE("watch range changes in the past")
|
|||
}
|
||||
|
||||
TEST_CASE("watch multiple keys and use promise") {
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
etcd::Client etcd(etcd_url);
|
||||
|
||||
int 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();
|
||||
|
||||
pplx::task<size_t> res = etcd.watch("/test", start_index, true)
|
||||
.then([](pplx::task<etcd::Response> const &resp_task) -> size_t {
|
||||
auto const &resp = resp_task.get();
|
||||
return resp.events().size();
|
||||
});
|
||||
pplx::task<size_t> res =
|
||||
etcd.watch("/test", start_index, true)
|
||||
.then([](pplx::task<etcd::Response> const& resp_task) -> size_t {
|
||||
auto const& resp = resp_task.get();
|
||||
return resp.events().size();
|
||||
});
|
||||
size_t event_size = res.get();
|
||||
CHECK(2 == event_size);
|
||||
}
|
||||
|
||||
TEST_CASE("lease grant")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("lease grant") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd::Response res = etcd.leasegrant(60).get();
|
||||
REQUIRE(res.is_ok());
|
||||
CHECK(60 == res.value().ttl());
|
||||
CHECK(0 < res.value().lease());
|
||||
CHECK(0 < res.value().lease());
|
||||
int64_t leaseid = res.value().lease();
|
||||
|
||||
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(leaseid == res.value().lease());
|
||||
res = etcd.get("/test/key1").get();
|
||||
REQUIRE(0 == res.error_code()); // overwrite
|
||||
CHECK(leaseid == res.value().lease());
|
||||
|
||||
//change leaseid
|
||||
// change with lease id
|
||||
res = etcd.leasegrant(10).get();
|
||||
leaseid = res.value().lease();
|
||||
res = etcd.set("/test/key1", "43", leaseid).get();
|
||||
REQUIRE(0 == res.error_code()); // overwrite
|
||||
REQUIRE(0 == res.error_code()); // overwrite
|
||||
CHECK("set" == res.action());
|
||||
res = etcd.get("/test/key1").get();
|
||||
REQUIRE(0 == res.error_code()); // overwrite
|
||||
CHECK(leaseid == res.value().lease());
|
||||
|
||||
//failure to attach lease id
|
||||
res = etcd.set("/test/key1", "43", leaseid+1).get();
|
||||
// failure to attach lease id
|
||||
res = etcd.set("/test/key1", "43", leaseid + 1).get();
|
||||
REQUIRE(!res.is_ok());
|
||||
REQUIRE(5 == res.error_code());
|
||||
REQUIRE(5 == res.error_code());
|
||||
CHECK("etcdserver: requested lease not found" == res.error_message());
|
||||
|
||||
res = etcd.modify("/test/key1", "44", leaseid).get();
|
||||
REQUIRE(0 == res.error_code()); // overwrite
|
||||
REQUIRE(0 == res.error_code()); // overwrite
|
||||
CHECK("update" == res.action());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
CHECK("44" == res.value().as_string());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
CHECK("44" == res.value().as_string());
|
||||
|
||||
//failure to attach lease id
|
||||
res = etcd.modify("/test/key1", "45", leaseid+1).get();
|
||||
// failure to attach invalid lease id
|
||||
res = etcd.modify("/test/key1", "45", leaseid + 1).get();
|
||||
REQUIRE(!res.is_ok());
|
||||
REQUIRE(5 == res.error_code());
|
||||
REQUIRE(5 == res.error_code());
|
||||
CHECK("etcdserver: requested lease not found" == res.error_message());
|
||||
|
||||
res = etcd.modify_if("/test/key1", "45", "44", leaseid).get();
|
||||
int index = res.index();
|
||||
int64_t index = res.index();
|
||||
REQUIRE(res.is_ok());
|
||||
CHECK("compareAndSwap" == res.action());
|
||||
CHECK("45" == res.value().as_string());
|
||||
|
||||
//failure to attach lease id
|
||||
res = etcd.modify_if("/test/key1", "46", "45", leaseid+1).get();
|
||||
// failure to attach invalid lease id
|
||||
res = etcd.modify_if("/test/key1", "46", "45", leaseid + 1).get();
|
||||
REQUIRE(!res.is_ok());
|
||||
REQUIRE(5 == res.error_code());
|
||||
REQUIRE(5 == res.error_code());
|
||||
CHECK("etcdserver: requested lease not found" == res.error_message());
|
||||
|
||||
// succes with the correct index
|
||||
// succes with the correct index & lease id
|
||||
res = etcd.modify_if("/test/key1", "44", index, leaseid).get();
|
||||
index = res.index();
|
||||
REQUIRE(res.is_ok());
|
||||
CHECK("compareAndSwap" == res.action());
|
||||
CHECK("44" == res.value().as_string());
|
||||
|
||||
res = etcd.modify_if("/test/key1", "44", index, leaseid+1).get();
|
||||
res = etcd.modify_if("/test/key1", "44", index, leaseid + 1).get();
|
||||
REQUIRE(!res.is_ok());
|
||||
REQUIRE(5 == res.error_code());
|
||||
REQUIRE(5 == res.error_code());
|
||||
CHECK("etcdserver: requested lease not found" == res.error_message());
|
||||
|
||||
res = etcd.add("/test/key11111", "43", leaseid).get();
|
||||
REQUIRE(0 == res.error_code());
|
||||
REQUIRE(0 == res.error_code());
|
||||
CHECK("create" == res.action());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
CHECK(leaseid == res.value().lease());
|
||||
|
||||
//failure to attach lease id
|
||||
res = etcd.set("/test/key11111", "43", leaseid+1).get();
|
||||
// failure to attach invalid lease id
|
||||
res = etcd.set("/test/key11111", "43", leaseid + 1).get();
|
||||
REQUIRE(!res.is_ok());
|
||||
REQUIRE(5 == res.error_code());
|
||||
REQUIRE(5 == res.error_code());
|
||||
CHECK("etcdserver: requested lease not found" == res.error_message());
|
||||
}
|
||||
|
||||
TEST_CASE("cleanup")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("lease list") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd::Response res = etcd.leasegrant(60).get();
|
||||
REQUIRE(res.is_ok());
|
||||
CHECK(60 == res.value().ttl());
|
||||
CHECK(0 < res.value().lease());
|
||||
int64_t leaseid = res.value().lease();
|
||||
|
||||
etcd::Response leasesresp = etcd.leases().get();
|
||||
if (leasesresp.is_ok()) {
|
||||
REQUIRE(leasesresp.is_ok());
|
||||
auto const& leases = leasesresp.leases();
|
||||
REQUIRE(leases.size() > 0);
|
||||
CHECK(std::find(leases.begin(), leases.end(), leaseid) != leases.end());
|
||||
} else {
|
||||
REQUIRE(leasesresp.error_code() == etcdv3::ERROR_GRPC_UNIMPLEMENTED);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("cleanup") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "etcd/Client.hpp"
|
||||
#include "etcd/KeepAlive.hpp"
|
||||
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
TEST_CASE("fork: set in child and get from self") {
|
||||
pid_t pid = fork();
|
||||
REQUIRE(pid >= 0);
|
||||
|
||||
if (pid == 0) {
|
||||
// child
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.set("/test/fork-key1", "fork: abcdefgh").wait();
|
||||
std::cout << "child: set finished ..." << std::endl;
|
||||
} else {
|
||||
// self
|
||||
etcd::Client etcd(etcd_url);
|
||||
size_t check = 0;
|
||||
while (check < 10) {
|
||||
auto resp = etcd.get("/test/fork-key1").get();
|
||||
if (!resp.is_ok()) {
|
||||
check += 1;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
continue;
|
||||
} else {
|
||||
CHECK(resp.value().as_string() == "fork: abcdefgh");
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::cout << "self: get finished ..." << std::endl;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "etcd/Client.hpp"
|
||||
#include "etcd/KeepAlive.hpp"
|
||||
#include "etcd/Response.hpp"
|
||||
#include "etcd/SyncClient.hpp"
|
||||
#include "etcd/Value.hpp"
|
||||
|
||||
static std::string etcd_uri =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
TEST_CASE("keepalive revoke and check if alive") {
|
||||
etcd::Client etcd(etcd_uri);
|
||||
|
||||
// create a lease with 30 seconds TTL
|
||||
auto keepalive = etcd.leasekeepalive(30).get();
|
||||
auto lease_id = keepalive->Lease();
|
||||
|
||||
// revoke the lease before it reaches its TTL
|
||||
etcd.leaserevoke(lease_id).wait();
|
||||
|
||||
// retrieves its TTL again, and it is now -1
|
||||
auto response = etcd.leasetimetolive(lease_id).get();
|
||||
REQUIRE(response.value().ttl() == -1);
|
||||
|
||||
// shorter than the TLL, or no sleep
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
// expect keep_alive->Check() to throw exception
|
||||
#ifndef _ETCD_NO_EXCEPTIONS
|
||||
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();
|
||||
}
|
||||
141
tst/LockTest.cpp
141
tst/LockTest.cpp
|
|
@ -10,10 +10,11 @@
|
|||
#include "etcd/Client.hpp"
|
||||
#include "etcd/KeepAlive.hpp"
|
||||
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
TEST_CASE("lock and unlock")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("lock and unlock") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
|
||||
// lock
|
||||
etcd::Response resp1 = etcd.lock("/test/abcd").get();
|
||||
|
|
@ -28,9 +29,8 @@ TEST_CASE("lock and unlock")
|
|||
REQUIRE(0 == resp2.error_code());
|
||||
}
|
||||
|
||||
TEST_CASE("double lock will fail")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("double lock will fail") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
|
||||
// lock
|
||||
etcd::Response resp1 = etcd.lock("/test/abcd").get();
|
||||
|
|
@ -41,7 +41,7 @@ TEST_CASE("double lock will fail")
|
|||
bool first_lock_release = false;
|
||||
std::string lock_key = resp1.lock_key();
|
||||
|
||||
auto second_lock_thr = std::thread([&](){
|
||||
auto second_lock_thr = std::thread([&]() {
|
||||
// lock again
|
||||
etcd::Response resp2 = etcd.lock("/test/abcd").get();
|
||||
CHECK("lock" == resp2.action());
|
||||
|
|
@ -61,11 +61,13 @@ TEST_CASE("double lock will fail")
|
|||
REQUIRE(0 == resp3.error_code());
|
||||
|
||||
// create a duration
|
||||
first_lock_release = true;
|
||||
// using a duration longer than default lease TTL for lock (see: DEFAULT_LEASE_TTL_FOR_LOCK)
|
||||
// using a duration longer than default lease TTL for lock (see:
|
||||
// DEFAULT_LEASE_TTL_FOR_LOCK)
|
||||
std::this_thread::sleep_for(std::chrono::seconds(15));
|
||||
|
||||
// unlock the first lock
|
||||
first_lock_release = true;
|
||||
|
||||
etcd::Response resp4 = etcd.unlock(lock_key).get();
|
||||
CHECK("unlock" == resp4.action());
|
||||
REQUIRE(resp4.is_ok());
|
||||
|
|
@ -82,22 +84,54 @@ TEST_CASE("double lock will fail")
|
|||
REQUIRE(0 == resp5.error_code());
|
||||
}
|
||||
|
||||
TEST_CASE("lock using lease")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("lock could be timeout") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
|
||||
// setup the timeout
|
||||
etcd.set_grpc_timeout(std::chrono::seconds(5));
|
||||
|
||||
// lock
|
||||
etcd::Response resp1 = etcd.lock("/test/abcd").get();
|
||||
CHECK("lock" == resp1.action());
|
||||
REQUIRE(resp1.is_ok());
|
||||
REQUIRE(0 == resp1.error_code());
|
||||
|
||||
auto lock_in_another_thread = std::thread([&]() {
|
||||
// lock again
|
||||
etcd::Response resp2 = etcd.lock("/test/abcd").get();
|
||||
CHECK("lock" == resp2.action());
|
||||
REQUIRE(resp2.is_grpc_timeout());
|
||||
});
|
||||
|
||||
lock_in_another_thread.join();
|
||||
|
||||
// cleanup: unlock the second lock
|
||||
etcd::Response resp5 = etcd.unlock(resp1.lock_key()).get();
|
||||
CHECK("unlock" == resp5.action());
|
||||
REQUIRE(resp5.is_ok());
|
||||
REQUIRE(0 == resp5.error_code());
|
||||
}
|
||||
|
||||
TEST_CASE("lock using lease") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
|
||||
bool failed = false;
|
||||
|
||||
std::function<void (std::exception_ptr)> handler = [&failed](std::exception_ptr eptr) {
|
||||
try {
|
||||
if (eptr) {
|
||||
#ifndef _ETCD_NO_EXCEPTIONS
|
||||
std::function<void(std::exception_ptr)> handler =
|
||||
[&failed](std::exception_ptr eptr) {
|
||||
try {
|
||||
if (eptr) {
|
||||
std::rethrow_exception(eptr);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Caught exception \"" << e.what() << "\"\n";
|
||||
failed = true;
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
std::cerr << "Caught exception \"" << e.what() << "\"\n";
|
||||
failed = true;
|
||||
}
|
||||
};
|
||||
};
|
||||
#else
|
||||
std::function<void(std::exception_ptr)> handler;
|
||||
#endif
|
||||
|
||||
// with handler
|
||||
{
|
||||
|
|
@ -166,24 +200,61 @@ TEST_CASE("lock using lease")
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("concurrent lock & unlock")
|
||||
{
|
||||
etcd::Client etcd("http://127.0.0.1:2379");
|
||||
TEST_CASE("concurrent lock & unlock") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
std::string const lock_key = "/test/test_key";
|
||||
|
||||
constexpr size_t trials = 128;
|
||||
constexpr size_t trials = 192;
|
||||
|
||||
std::function<void(std::string const &, const size_t)> locker = [&etcd](std::string const &key, const size_t index) {
|
||||
std::cout << "start lock for " << index << std::endl;
|
||||
auto resp = etcd.lock(key).get();
|
||||
std::cout << "lock for " << index << " is ok, start sleep: ..." << resp.error_message() << std::endl;
|
||||
REQUIRE(resp.is_ok());
|
||||
std::srand(index);
|
||||
size_t time_to_sleep = 1;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(time_to_sleep));
|
||||
REQUIRE(etcd.unlock(resp.lock_key()).get().is_ok());
|
||||
std::cout << "thread " << index << " been unlocked" << std::endl;
|
||||
};
|
||||
std::function<void(std::string const&, const size_t)> locker =
|
||||
[&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();
|
||||
std::cout << "lock for " << index << " is ok, starts sleeping: ..."
|
||||
<< resp.error_message() << std::endl
|
||||
<< std::flush;
|
||||
REQUIRE(resp.is_ok());
|
||||
std::srand(index);
|
||||
size_t time_to_sleep = 1;
|
||||
std::this_thread::sleep_for(std::chrono::seconds(time_to_sleep));
|
||||
std::cout << "lock for " << index << " resumes from sleep: ..."
|
||||
<< resp.error_message() << std::endl
|
||||
<< std::flush;
|
||||
REQUIRE(etcd.unlock(resp.lock_key()).get().is_ok());
|
||||
std::cout << "thread " << index << " been unlocked" << std::endl
|
||||
<< std::flush;
|
||||
};
|
||||
|
||||
std::vector<std::thread> locks(trials);
|
||||
for (size_t index = 0; index < trials; ++index) {
|
||||
locks[index] = std::thread(locker, lock_key, index);
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < trials; ++index) {
|
||||
locks[index].join();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("concurrent lock & unlock with a put in between") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
std::string const lock_key = "/test/test_key";
|
||||
|
||||
constexpr size_t trials = 128;
|
||||
|
||||
std::function<void(std::string const&, const size_t)> locker =
|
||||
[&etcd](std::string const& key, const size_t index) {
|
||||
std::cout << "start lock for " << index << std::endl;
|
||||
auto resp = etcd.lock(key, true).get();
|
||||
std::cout << "lock for " << index << " is ok, start put and unlock: ..."
|
||||
<< resp.error_message() << std::endl;
|
||||
REQUIRE(resp.is_ok());
|
||||
auto put_resp =
|
||||
etcd.put("/test/test_put", "hello" + std::to_string(index)).get();
|
||||
REQUIRE(put_resp.is_ok());
|
||||
REQUIRE(etcd.unlock(resp.lock_key()).get().is_ok());
|
||||
std::cout << "thread " << index << " been unlocked" << std::endl;
|
||||
};
|
||||
|
||||
std::vector<std::thread> locks(trials);
|
||||
for (size_t index = 0; index < trials; ++index) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
// See also: https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/issues/86
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
|
||||
#include "etcd/Client.hpp"
|
||||
#include "etcd/KeepAlive.hpp"
|
||||
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
class DistributedLock {
|
||||
public:
|
||||
DistributedLock(const std::string& lock_name, uint timeout = 0);
|
||||
~DistributedLock() noexcept;
|
||||
inline bool lock_acquired() { return _acquired; }
|
||||
|
||||
private:
|
||||
bool _acquired = false;
|
||||
std::string _lock_key;
|
||||
std::unique_ptr<::etcd::Client> _etcd_client;
|
||||
};
|
||||
|
||||
DistributedLock::DistributedLock(const std::string& lock_name, uint timeout) {
|
||||
_etcd_client = std::unique_ptr<etcd::Client>(new etcd::Client(etcd_url));
|
||||
|
||||
if (timeout == 0) {
|
||||
etcd::Response resp = _etcd_client->lock(lock_name).get();
|
||||
if (resp.is_ok()) {
|
||||
_lock_key = resp.lock_key();
|
||||
_acquired = true;
|
||||
}
|
||||
} else {
|
||||
std::future<etcd::Response> future = std::async(std::launch::async, [&]() {
|
||||
etcd::Response resp = _etcd_client->lock(lock_name).get();
|
||||
return resp;
|
||||
});
|
||||
|
||||
std::future_status status = future.wait_for(std::chrono::seconds(timeout));
|
||||
if (status == std::future_status::ready) {
|
||||
auto resp = future.get();
|
||||
if (resp.is_ok()) {
|
||||
_lock_key = resp.lock_key();
|
||||
_acquired = true;
|
||||
}
|
||||
} else if (status == std::future_status::timeout) {
|
||||
std::cerr << "failed to acquire distributed because of lock timeout"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cerr << "failed to acquire distributed lock" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DistributedLock::~DistributedLock() noexcept {
|
||||
if (!_acquired) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto resp = _etcd_client->unlock(_lock_key).get();
|
||||
if (!resp.is_ok()) {
|
||||
std::cout << resp.error_code() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int i = 0, t = 0;
|
||||
while (t < 100 /* update this value to make it run for longer */) {
|
||||
{
|
||||
DistributedLock lock(std::to_string(i), 0);
|
||||
if (!lock.lock_acquired()) {
|
||||
std::cerr << "failed to acquire lock" << std::endl;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
++i;
|
||||
++t;
|
||||
if (i == 10) {
|
||||
i = 0;
|
||||
}
|
||||
if (t % 10 == 0) {
|
||||
std::cout << "round: i = " << i << ", t = " << t << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
|
||||
#include "etcd/Client.hpp"
|
||||
#include "etcd/KeepAlive.hpp"
|
||||
#include "etcd/Watcher.hpp"
|
||||
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
static std::atomic_int watcher_called;
|
||||
|
||||
void print_response(etcd::Response const& resp) { watcher_called.fetch_add(1); }
|
||||
|
||||
/**
|
||||
* @brief emulate the behavior of creating watcher many times:
|
||||
*
|
||||
* 1. create a watcher
|
||||
* 2. change a value
|
||||
* 3. cancel the watcher
|
||||
*/
|
||||
void watch_once(etcd::Client& client, std::unique_ptr<etcd::Watcher>& watcher,
|
||||
const size_t round) {
|
||||
const std::string my_prefix = "/test";
|
||||
const std::string my_key = my_prefix + "/foo";
|
||||
watcher.reset(new etcd::Watcher(client, my_prefix, print_response, true));
|
||||
|
||||
int k = watcher_called.load();
|
||||
client.set(my_key, "bar-" + std::to_string(round)).wait();
|
||||
while (true) {
|
||||
if (watcher_called.load() > k) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
// cancel the watcher
|
||||
watcher->Cancel();
|
||||
}
|
||||
|
||||
TEST_CASE("watch shouldn't leak memory") {
|
||||
watcher_called.store(0);
|
||||
|
||||
// issue some changes to see if the watcher works
|
||||
etcd::Client client(etcd_url);
|
||||
std::unique_ptr<etcd::Watcher> watcher;
|
||||
for (int round = 0;
|
||||
round < 10 /* update this value to make it run for longer */; ++round) {
|
||||
if (round % 50 == 0) {
|
||||
std::cout << "starting round " << round << std::endl;
|
||||
}
|
||||
watch_once(client, watcher, round);
|
||||
}
|
||||
|
||||
std::cout << "watcher been called for " << watcher_called.load() << " times"
|
||||
<< std::endl;
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "etcd/Client.hpp"
|
||||
#include "etcd/SyncClient.hpp"
|
||||
#include "etcd/Watcher.hpp"
|
||||
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
static int watcher_called = 0;
|
||||
|
||||
void print_response(etcd::Response const& resp) {
|
||||
++watcher_called;
|
||||
std::cout << "print response called" << std::endl;
|
||||
if (resp.error_code()) {
|
||||
std::cout << resp.error_code() << ": " << resp.error_message() << std::endl;
|
||||
} else {
|
||||
std::cout << resp.action() << " " << resp.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;
|
||||
for (auto const& ev : resp.events()) {
|
||||
std::cout << "Value change in events: "
|
||||
<< static_cast<int>(ev.event_type())
|
||||
<< ", prev kv = " << ev.prev_kv().key() << " -> "
|
||||
<< ev.prev_kv().as_string() << ", kv = " << ev.kv().key()
|
||||
<< " -> " << ev.kv().as_string() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wait_for_connection(std::string endpoints) {
|
||||
// wait until the client connects to etcd server
|
||||
while (true) {
|
||||
#ifndef _ETCD_NO_EXCEPTIONS
|
||||
try {
|
||||
etcd::Client client(endpoints);
|
||||
if (client.head().get().is_ok()) {
|
||||
break;
|
||||
}
|
||||
} catch (...) {
|
||||
// pass
|
||||
}
|
||||
#else
|
||||
etcd::Client client(endpoints);
|
||||
if (client.head().get().is_ok()) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
void initialize_watcher(const std::string& endpoints, const std::string& prefix,
|
||||
std::function<void(etcd::Response)> callback,
|
||||
std::shared_ptr<etcd::Watcher>& watcher) {
|
||||
// wait until the endpoints turn to be available
|
||||
wait_for_connection(endpoints);
|
||||
etcd::Client client(endpoints);
|
||||
|
||||
// Check if the failed one has been cancelled first
|
||||
if (watcher && watcher->Cancelled()) {
|
||||
std::cout << "watcher's reconnect loop been cancelled" << std::endl;
|
||||
return;
|
||||
}
|
||||
watcher.reset(new etcd::Watcher(client, prefix, callback, true));
|
||||
|
||||
// Note that lambda requires `mutable`qualifier.
|
||||
watcher->Wait(
|
||||
[endpoints, prefix, callback,
|
||||
/* By reference for renewing */ &watcher](bool cancelled) mutable {
|
||||
if (cancelled) {
|
||||
std::cout << "watcher's reconnect loop stopped as been cancelled"
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
initialize_watcher(endpoints, prefix, callback, watcher);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("watch should can be re-established") {
|
||||
const std::string my_prefix = "/test";
|
||||
|
||||
// the watcher initialized in this way will auto re-connect to etcd
|
||||
std::shared_ptr<etcd::Watcher> watcher;
|
||||
initialize_watcher(etcd_url, my_prefix, print_response, watcher);
|
||||
|
||||
// issue some changes to see if the watcher works
|
||||
for (int round = 0; round < 100000; ++round) {
|
||||
#ifndef _ETCD_NO_EXCEPTIONS
|
||||
try {
|
||||
etcd::Client client(etcd_url);
|
||||
auto response =
|
||||
client.set(my_prefix + "/foo", "bar-" + std::to_string(round)).get();
|
||||
} catch (...) {
|
||||
// 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));
|
||||
}
|
||||
|
||||
// cancel the worker
|
||||
watcher->Cancel();
|
||||
|
||||
// the watcher has been cancelled and shouldn't work anymore
|
||||
for (int round = 10; round < 20; ++round) {
|
||||
#ifndef _ETCD_NO_EXCEPTIONS
|
||||
try {
|
||||
etcd::Client client(etcd_url);
|
||||
auto response =
|
||||
client.set(my_prefix + "/foo", "bar-" + std::to_string(round)).get();
|
||||
} catch (...) {
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -9,44 +9,50 @@ static std::string ca = "security-config/certs/ca.crt";
|
|||
static std::string cert = "security-config/certs/etcd0.example.com.crt";
|
||||
static std::string key = "security-config/private/etcd0.example.com.key";
|
||||
|
||||
TEST_CASE("setup with auth")
|
||||
{
|
||||
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379", ca, cert, key);
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("https://127.0.0.1:2379");
|
||||
|
||||
TEST_CASE("setup with auth") {
|
||||
etcd::Client* etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key);
|
||||
etcd->rmdir("/test", true).wait();
|
||||
}
|
||||
|
||||
TEST_CASE("add a new key after authenticate")
|
||||
{
|
||||
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379", ca, cert, key);
|
||||
TEST_CASE("add a new key after authenticate") {
|
||||
etcd::Client* etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key);
|
||||
etcd->rmdir("/test", true).wait();
|
||||
etcd::Response resp = etcd->add("/test/key1", "42").get();
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK("create" == resp.action());
|
||||
etcd::Value const & val = resp.value();
|
||||
etcd::Value const& val = resp.value();
|
||||
CHECK("42" == val.as_string());
|
||||
CHECK("/test/key1" == val.key());
|
||||
CHECK(!val.is_dir());
|
||||
CHECK(0 < val.created_index());
|
||||
CHECK(0 < val.modified_index());
|
||||
CHECK(1 == val.version());
|
||||
CHECK(0 < resp.index());
|
||||
CHECK(105 == etcd->add("/test/key1", "43").get().error_code()); // Key already exists
|
||||
CHECK(105 == etcd->add("/test/key1", "42").get().error_code()); // Key already exists
|
||||
CHECK("Key already exists" == etcd->add("/test/key1", "42").get().error_message());
|
||||
CHECK(
|
||||
etcd::ERROR_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-cpp-apiv3: key already exists" ==
|
||||
etcd->add("/test/key1", "42").get().error_message());
|
||||
}
|
||||
|
||||
TEST_CASE("read a value from etcd")
|
||||
{
|
||||
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379", ca, cert, key);
|
||||
TEST_CASE("read a value from etcd") {
|
||||
etcd::Client* etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key);
|
||||
etcd::Response resp = etcd->get("/test/key1").get();
|
||||
CHECK("get" == resp.action());
|
||||
REQUIRE(resp.is_ok());
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK("42" == resp.value().as_string());
|
||||
CHECK("" == etcd->get("/test").get().value().as_string()); // key points to a directory
|
||||
CHECK("" == etcd->get("/test").get().value().as_string()); // key points to a
|
||||
// directory
|
||||
}
|
||||
|
||||
TEST_CASE("cleanup")
|
||||
{
|
||||
etcd::Client *etcd = etcd::Client::WithSSL("https://127.0.0.1:2379", ca, cert, key);
|
||||
TEST_CASE("cleanup") {
|
||||
etcd::Client* etcd = etcd::Client::WithSSL(etcd_url, ca, cert, key);
|
||||
REQUIRE(0 == etcd->rmdir("/test", true).get().error_code());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "etcd/Client.hpp"
|
||||
#include "etcd/v3/Transaction.hpp"
|
||||
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
TEST_CASE("setup") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.rmdir("/test", true).wait();
|
||||
}
|
||||
|
||||
TEST_CASE("add a new key") {
|
||||
etcd::Client etcd(etcd_url);
|
||||
etcd.rmdir("/test", true).wait();
|
||||
|
||||
// do some put using txn
|
||||
{
|
||||
etcdv3::Transaction txn;
|
||||
txn.setup_put("/test/x1", "1");
|
||||
txn.setup_put("/test/x2", "2");
|
||||
txn.setup_put("/test/x3", "3");
|
||||
etcd::Response resp = etcd.txn(txn).get();
|
||||
REQUIRE(0 == resp.error_code());
|
||||
}
|
||||
|
||||
// check the value
|
||||
{
|
||||
etcd::Response resp = etcd.get("/test/x1").get();
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK(resp.value().as_string() == "1");
|
||||
|
||||
resp = etcd.get("/test/x2").get();
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK(resp.value().as_string() == "2");
|
||||
|
||||
resp = etcd.get("/test/x3").get();
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK(resp.value().as_string() == "3");
|
||||
}
|
||||
|
||||
// do some put and delete using txn
|
||||
{
|
||||
etcdv3::Transaction txn;
|
||||
|
||||
// setup the conditions
|
||||
txn.add_compare_value("/test/x1", "1");
|
||||
txn.add_compare_value("/test/x2", "2");
|
||||
|
||||
txn.setup_put("/test/x1", "111");
|
||||
txn.setup_delete("/test/x2");
|
||||
txn.setup_delete("/test/x3");
|
||||
txn.setup_put("/test/x4", "4");
|
||||
etcd::Response resp = etcd.txn(txn).get();
|
||||
REQUIRE(0 == resp.error_code());
|
||||
}
|
||||
|
||||
// check the value
|
||||
{
|
||||
etcd::Response resp = etcd.get("/test/x1").get();
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK(resp.value().as_string() == "111");
|
||||
|
||||
resp = etcd.get("/test/x2").get();
|
||||
REQUIRE(etcdv3::ERROR_KEY_NOT_FOUND == resp.error_code());
|
||||
|
||||
resp = etcd.get("/test/x3").get();
|
||||
REQUIRE(etcdv3::ERROR_KEY_NOT_FOUND == resp.error_code());
|
||||
|
||||
resp = etcd.get("/test/x4").get();
|
||||
REQUIRE(0 == resp.error_code());
|
||||
CHECK(resp.value().as_string() == "4");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("fetch & add") {
|
||||
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);
|
||||
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
|
||||
}
|
||||
|
|
@ -4,116 +4,231 @@
|
|||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "etcd/Watcher.hpp"
|
||||
#include "etcd/SyncClient.hpp"
|
||||
#include "etcd/Watcher.hpp"
|
||||
|
||||
static const std::string etcd_url =
|
||||
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
|
||||
|
||||
static std::string etcd_uri("http://127.0.0.1:2379");
|
||||
static int watcher_called = 0;
|
||||
|
||||
void printResponse(etcd::Response const & resp)
|
||||
{
|
||||
++watcher_called;
|
||||
std::cout << "print response called" << std::endl;
|
||||
void printResponse(etcd::Response const& resp) {
|
||||
if (resp.error_code()) {
|
||||
std::cout << resp.error_code() << ": " << resp.error_message() << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << resp.action() << " " << resp.value().as_string() << std::endl;
|
||||
std::cout << "Previous value: " << resp.prev_value().as_string() << std::endl;
|
||||
std::cout << "Watcher " << resp.watch_id() << " fails with "
|
||||
<< resp.error_code() << ": " << resp.error_message() << std::endl;
|
||||
} else {
|
||||
std::cout << "Watcher " << resp.watch_id() << " responses with "
|
||||
<< resp.action() << " " << resp.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;
|
||||
for (auto const& ev : resp.events()) {
|
||||
if (ev.prev_kv().key().find("/leader") == 0 ||
|
||||
ev.kv().key().find("/leader") == 0) {
|
||||
return;
|
||||
}
|
||||
std::cout << "Value change in events: "
|
||||
<< static_cast<int>(ev.event_type())
|
||||
<< ", prev kv = " << ev.prev_kv().key() << " -> "
|
||||
<< ev.prev_kv().as_string() << ", kv = " << ev.kv().key()
|
||||
<< " -> " << ev.kv().as_string() << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "print response called" << std::endl;
|
||||
++watcher_called;
|
||||
}
|
||||
|
||||
TEST_CASE("create watcher with cancel")
|
||||
{
|
||||
|
||||
etcd::SyncClient etcd(etcd_uri);
|
||||
TEST_CASE("create watcher") {
|
||||
etcd::SyncClient etcd(etcd_url);
|
||||
etcd.rmdir("/test", true);
|
||||
|
||||
watcher_called = 0;
|
||||
etcd::Watcher watcher(etcd_uri, "/test", printResponse, true);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
{
|
||||
etcd::Watcher watcher(etcd_url, "/test", printResponse, true);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
etcd.set("/test/key", "42");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
etcd.set("/test/key", "43");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
}
|
||||
CHECK(2 == watcher_called);
|
||||
etcd.rmdir("/test", true);
|
||||
}
|
||||
|
||||
TEST_CASE("watch with correct prefix") {
|
||||
etcd::SyncClient etcd(etcd_url);
|
||||
etcd.rmdir("/test", true);
|
||||
|
||||
watcher_called = 0;
|
||||
etcd::Watcher watcher(etcd_url, "/test/key_prefix", printResponse, true);
|
||||
|
||||
{
|
||||
etcd.set("/test/key1", "42");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
CHECK(0 == watcher_called);
|
||||
}
|
||||
{
|
||||
etcd.set("/test/key_prefix", "42");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
CHECK(1 == watcher_called);
|
||||
}
|
||||
{
|
||||
etcd.set("/test/key_prefix1", "42");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
CHECK(2 == watcher_called);
|
||||
}
|
||||
{
|
||||
etcd.set("/test/key_prefiy", "42");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
CHECK(2 == watcher_called);
|
||||
}
|
||||
{
|
||||
etcd.set("/test/key1", "42");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
CHECK(2 == watcher_called);
|
||||
}
|
||||
|
||||
etcd.rmdir("/test", true);
|
||||
}
|
||||
|
||||
TEST_CASE("create watcher with cancel") {
|
||||
etcd::SyncClient etcd(etcd_url);
|
||||
etcd.rmdir("/test", true);
|
||||
|
||||
watcher_called = 0;
|
||||
etcd::Watcher watcher(etcd_url, "/test", printResponse, true);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
etcd.set("/test/key", "42");
|
||||
etcd.set("/test/key", "43");
|
||||
etcd.rm("/test/key");
|
||||
etcd.set("/test/key", "44");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
CHECK(4 == watcher_called);
|
||||
watcher.Cancel();
|
||||
etcd.set("/test/key", "50");
|
||||
etcd.set("/test/key", "51");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
CHECK(4 == watcher_called);
|
||||
|
||||
etcd.rmdir("/test", true);
|
||||
}
|
||||
|
||||
TEST_CASE("create watcher on ranges with cancel")
|
||||
{
|
||||
etcd::SyncClient etcd(etcd_uri);
|
||||
TEST_CASE("create watcher on ranges with cancel") {
|
||||
etcd::SyncClient etcd(etcd_url);
|
||||
etcd.rmdir("/test", true);
|
||||
|
||||
watcher_called = 0;
|
||||
etcd::Watcher watcher(etcd_uri, "/test/key1", "/test/key5", printResponse);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
etcd::Watcher watcher(etcd_url, "/test/key1", "/test/key5", printResponse);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
etcd.set("/test/key1", "42");
|
||||
etcd.set("/test/key2", "43");
|
||||
etcd.rm("/test/key1");
|
||||
etcd.set("/test/key5", "44");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
CHECK(3 == watcher_called);
|
||||
watcher.Cancel();
|
||||
etcd.set("/test/key3", "50");
|
||||
etcd.set("/test/key4", "51");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
CHECK(3 == watcher_called);
|
||||
|
||||
etcd.rmdir("/test", true);
|
||||
}
|
||||
|
||||
TEST_CASE("create watcher")
|
||||
{
|
||||
etcd::SyncClient etcd(etcd_uri);
|
||||
etcd.rmdir("/test", true);
|
||||
|
||||
watcher_called = 0;
|
||||
{
|
||||
etcd::Watcher watcher(etcd_uri, "/test", printResponse, true);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
etcd.set("/test/key", "42");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
etcd.set("/test/key", "43");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
}
|
||||
CHECK(2 == watcher_called);
|
||||
etcd.rmdir("/test", true).error_code();
|
||||
}
|
||||
|
||||
TEST_CASE("watch should exit normally")
|
||||
{
|
||||
// cancal immediately after start watch.
|
||||
etcd::Watcher watcher(etcd_uri, "/test", printResponse, true);
|
||||
TEST_CASE("watch should exit normally") {
|
||||
// cancel immediately after start watch.
|
||||
etcd::Watcher watcher(etcd_url, "/test", printResponse, true);
|
||||
watcher.Cancel();
|
||||
}
|
||||
|
||||
TEST_CASE("watch should can be cancelled repeatedly")
|
||||
{
|
||||
// cancal immediately after start watch.
|
||||
etcd::Watcher watcher(etcd_uri, "/test", printResponse, true);
|
||||
TEST_CASE("watch should can be cancelled repeatedly") {
|
||||
etcd::Watcher watcher(etcd_url, "/test", printResponse, true);
|
||||
std::vector<std::thread> threads(10);
|
||||
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) {
|
||||
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")
|
||||
// {
|
||||
// etcd::Client etcd(etcd_uri);
|
||||
// etcd::Client etcd(etcd_url);
|
||||
// etcd.set("/test/key1", "42").wait();
|
||||
|
||||
// pplx::task<etcd::Response> res = etcd.watch("/test/key1");
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
Source: etcd-cpp-apiv3
|
||||
Version: etcd-cpp-apiv3-7e280ec
|
||||
Homepage: https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3
|
||||
Description: The etcd-cpp-apiv3 is a C++ API for etcd's v3 client API, i.e., ETCDCTL_API=3.
|
||||
Build-Depends: boost-system, boost-thread, boost-random,
|
||||
cpprestsdk, grpc, openssl, protobuf
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO etcd-cpp-apiv3/etcd-cpp-apiv3
|
||||
REF 7e280ec8a49aaf26976d72a4080f2e2c6756f2b7
|
||||
SHA512 1e1d525f79840731102e6400ff2582807e0626b58fa60726e49bea6ca51868a6b8bf3a34c7bf1d1266e156daa1e741bbfa2acce42b54680a23426bc00192dc6d
|
||||
HEAD_REF master
|
||||
)
|
||||
|
||||
vcpkg_configure_cmake(
|
||||
SOURCE_PATH ${SOURCE_PATH}
|
||||
PREFER_NINJA
|
||||
OPTIONS
|
||||
-DBUILD_ETCD_TESTS=OFF
|
||||
)
|
||||
set(VCPKG_POLICY_DLLS_WITHOUT_LIBS enabled)
|
||||
set(VCPKG_POLICY_DLLS_WITHOUT_EXPORTS enabled)
|
||||
|
||||
vcpkg_install_cmake()
|
||||
|
||||
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)
|
||||
|
||||
vcpkg_copy_pdbs()
|
||||
|
||||
# Handle copyright
|
||||
file(INSTALL ${SOURCE_PATH}/LICENSE.txt DESTINATION ${CURRENT_PACKAGES_DIR}/share/etcd-cpp-apiv3 RENAME copyright)
|
||||
Loading…
Reference in New Issue