Enhance tests and improve documents about about build & install.
Should fixes #2, #4. Signed-off-by: Tao He <linzhu.ht@alibaba-inc.com>
This commit is contained in:
parent
e24f3c0de9
commit
65271bb92d
|
|
@ -86,7 +86,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
/usr/local/bin/etcd &
|
/usr/local/bin/etcd &
|
||||||
./bin/etcd_test
|
CTEST_OUTPUT_ON_FAILURE=1 make test
|
||||||
|
|
||||||
# note: no need to clean up on CI env
|
# note: no need to clean up on CI env
|
||||||
|
|
||||||
|
|
|
||||||
202
README.md
202
README.md
|
|
@ -1,51 +1,38 @@
|
||||||
etcd-cpp-api is a C++ API for [etcd]
|
etcd-cpp-apiv3
|
||||||
|
==============
|
||||||
|
|
||||||
|
The _etcd-cpp-apiv3_ is a C++ API for [etcd](https://etcd.io/)'s v3 client API,
|
||||||
|
i.e., `ETCDCTL_API=3`.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
1. Build Boost Library(http://www.boost.org/doc/libs/1_61_0/more/getting_started/unix-variants.html)
|
|
||||||
* You can use:
|
|
||||||
```
|
|
||||||
./bootstrap.sh --with-libraries=system,thread,locale,random,chrono,regex,filesystem
|
|
||||||
./b2 install
|
|
||||||
```
|
|
||||||
* The above command will only compile those libraries indicated, thus it takes less time to build boost
|
|
||||||
|
|
||||||
2. Build Casablanca:
|
1. boost
|
||||||
* Instructions on how to build is here: https://github.com/Microsoft/cpprestsdk/wiki
|
2. protobuf
|
||||||
* For Linux environment you can omit libboost-all-dev during apt-get install, if you already have boost library installed.
|
3. gRPC
|
||||||
|
4. [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:
|
||||||
|
|
||||||
3. Install Protobuf:
|
git clone https://github.com/microsoft/cpprestsdk.git
|
||||||
* https://github.com/google/protobuf/blob/master/src/README.md
|
cd cpprestsdk
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. -DCPPREST_EXCLUDE_WEBSOCKETS=ON
|
||||||
|
make -j && make install
|
||||||
|
|
||||||
4. Install Protoc for C++:
|
## Build and install
|
||||||
* https://github.com/grpc/grpc/blob/release-0_14/INSTALL.md
|
|
||||||
|
|
||||||
|
The _etcd-cpp-apiv3_ library could be easily built and installed using cmake, after all above
|
||||||
|
dependencies have been successfully installed:
|
||||||
|
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake ..
|
||||||
|
make -j
|
||||||
|
|
||||||
## Compatible etcd version
|
## Compatible etcd version
|
||||||
Build master branch of etcd in github. As of writing, current releases of etcd does not yet support prev_value.
|
|
||||||
Only the master branch supports it.
|
|
||||||
https://github.com/coreos/etcd/blob/master/Documentation/dl_build.md (see: Build the latest version)
|
|
||||||
|
|
||||||
## Updates from etcdv2 cpp client to etcdv3 cpp client
|
The _etcd-cpp-apiv3_ should work well with etcd > 3.0. Feel free to issue an issue to us on
|
||||||
See "handling directory nodes" section
|
Github when you encounter problems when working with etcd 3.x releases.
|
||||||
|
|
||||||
## compiling .proto
|
## Usage
|
||||||
Proto files are stored in /proto. The proto files defined the interface of etcdv3.
|
|
||||||
|
|
||||||
You can compile it like this(if you are running this command inside /proto folder):
|
|
||||||
```
|
|
||||||
protoc -I . --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ./rpc.proto
|
|
||||||
protoc -I . --cpp_out=. ./*.proto
|
|
||||||
```
|
|
||||||
|
|
||||||
Protofiles for etcdv3 can be found here:
|
|
||||||
* https://github.com/coreos/etcd/tree/master/auth/authpb
|
|
||||||
* https://github.com/coreos/etcd/tree/master/etcdserver/etcdserverpb
|
|
||||||
* https://github.com/coreos/etcd/tree/master/mvcc/mvccpb
|
|
||||||
|
|
||||||
Note: Protofiles in the project is not sync to the protofiles in etcd master branch. If you
|
|
||||||
want to update the protofiles in this project, you need to manually update it.
|
|
||||||
|
|
||||||
## generic notes
|
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
etcd::Client etcd("http://127.0.0.1:4001");
|
etcd::Client etcd("http://127.0.0.1:4001");
|
||||||
|
|
@ -64,7 +51,7 @@ also returns the ```etcd::Response``` object.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
etcd::Client etcd("http://127.0.0.1:4001");
|
etcd::Client etcd("http://127.0.0.1:4001");
|
||||||
pplx::task<etcd::Response> response_task = etcd.get("/test/key1").get();
|
pplx::task<etcd::Response> response_task = etcd.get("/test/key1");
|
||||||
// ... do something else
|
// ... do something else
|
||||||
etcd::Response response = response_task.get();
|
etcd::Response response = response_task.get();
|
||||||
std::cout << response.value().as_string();
|
std::cout << response.value().as_string();
|
||||||
|
|
@ -113,7 +100,7 @@ this case since the respose has been already arrived (we are inside the callback
|
||||||
|
|
||||||
## etcd operations
|
## etcd operations
|
||||||
|
|
||||||
### reading a value
|
### 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 clinent 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
|
key to be read. If the read operation is successful then the value of the key can be acquired with
|
||||||
|
|
@ -149,7 +136,7 @@ the ```created_index()``` and the ```modofied_index()``` methods.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### modifying a value
|
### Modifying a value
|
||||||
|
|
||||||
Setting the value of a key can be done with the ```set()``` method of the client. You simply pass
|
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
|
the key and the value as string parameters and you are done. The newly set value object can be asked
|
||||||
|
|
@ -192,7 +179,7 @@ some specific conditions.
|
||||||
the previous value equals with old_index. If the indices does not match returns with "Compare
|
the previous value equals with old_index. If the indices does not match returns with "Compare
|
||||||
failed" error (code 101)
|
failed" error (code 101)
|
||||||
|
|
||||||
### deleting a value
|
### 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.
|
should point to an existing value. There are conditional variations for deletion too.
|
||||||
|
|
@ -204,57 +191,67 @@ should point to an existing value. There are conditional variations for deletion
|
||||||
the previous value equals with old_index. If the indices does not match returns with "Compare
|
the previous value equals with old_index. If the indices does not match returns with "Compare
|
||||||
failed" error (code 101)
|
failed" error (code 101)
|
||||||
|
|
||||||
### handling directory nodes
|
### Handling directory nodes
|
||||||
Directory nodes are not supported anymore in etcdv3.
|
|
||||||
|
|
||||||
However, ls and rmdir will list/delete keys defined by the prefix. mkdir method is removed since
|
Directory nodes are not supported anymore in etcdv3. However, ls and rmdir will list/delete
|
||||||
etcdv3 treats everything as keys.
|
keys defined by the prefix. mkdir method is removed since etcdv3 treats everything as keys.
|
||||||
|
|
||||||
1. Creating a directory:
|
1. Creating a directory:
|
||||||
Creating a directory is not supported anymore in etcdv3 cpp client. Users should remove the
|
|
||||||
API from their code.
|
Creating a directory is not supported anymore in etcdv3 cpp client. Users should remove the
|
||||||
|
API from their code.
|
||||||
|
|
||||||
2. Listing a directory:
|
2. Listing a directory:
|
||||||
Listing directory in etcd3 cpp client will return all keys that matched the given prefix recursively.
|
|
||||||
|
|
||||||
```c++
|
Listing directory in etcd3 cpp client will return all keys that matched the given prefix
|
||||||
etcd.set("/test/key1", "value1").wait();
|
recursively.
|
||||||
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();
|
```c++
|
||||||
```
|
etcd.set("/test/key1", "value1").wait();
|
||||||
resp.key() will have the following values:
|
etcd.set("/test/key2", "value2").wait();
|
||||||
/test/key1
|
etcd.set("/test/key3", "value3").wait();
|
||||||
/test/key2
|
etcd.set("/test/subdir/foo", "foo").wait();
|
||||||
/test/key3
|
|
||||||
/test/subdir/foo
|
|
||||||
|
|
||||||
|
etcd::Response resp = etcd.ls("/test/new_dir").get();
|
||||||
|
```
|
||||||
|
|
||||||
Note: Regarding the returned keys when listing a directory:
|
```resp.key()``` will have the following values:
|
||||||
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
|
/test/key1
|
||||||
returns with the i-th element of the values vector, so ```response.values()[i] ==
|
/test/key2
|
||||||
response.value(i)```.
|
/test/key3
|
||||||
|
/test/subdir/foo
|
||||||
|
```
|
||||||
|
|
||||||
```c++
|
Note: Regarding the returned keys when listing a directory:
|
||||||
etcd::Client etcd("http://127.0.0.1:4001");
|
|
||||||
etcd::Response resp = etcd.ls("/test/new_dir").get();
|
+ In etcdv3 cpp client, resp.key(0) will return "/test/new_dir/key1" since everything is
|
||||||
for (int i = 0; i < resp.keys().size(); ++i)
|
treated as keys in etcdv3.
|
||||||
{
|
+ While in etcdv2 cpp client it will return "key1" and "/test/new_dir" directory should
|
||||||
std::cout << resp.keys(i);
|
be created first before you can set "key1".
|
||||||
std::cout << " = " << resp.value(i).as_string() << std::endl;
|
|
||||||
}
|
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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
3. Removing directory:
|
3. Removing directory:
|
||||||
|
|
||||||
If you want the delete recursively then you have to pass a second ```true``` parameter
|
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
|
to rmdir and supply a key. This key will be treated as a prefix. All keys that match the
|
||||||
be deleted. All deleted keys will be placed in response.values() and response.keys(). This parameter defaults to ```false```.
|
prefix will be deleted. All deleted keys will be placed in ```response.values()``` and
|
||||||
|
```response.keys()```. This parameter defaults to ```false```.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
etcd::Client etcd("http://127.0.0.1:4001");
|
etcd::Client etcd("http://127.0.0.1:4001");
|
||||||
|
|
@ -267,12 +264,12 @@ be deleted. All deleted keys will be placed in response.values() and response.ke
|
||||||
std::cout << resp.keys(i);
|
std::cout << resp.keys(i);
|
||||||
std::cout << " = " << resp.value(i).as_string() << std::endl;
|
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.
|
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.
|
The key supplied will NOT be treated as a prefix and will be treated as a normal key name.
|
||||||
|
|
||||||
### watching for changes
|
### 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
|
simply does not deliver a response object until the watched value changes in any way (modified or
|
||||||
|
|
@ -283,12 +280,15 @@ value and ```response.prev_value()``` will contain the previous value. In case o
|
||||||
```response.action()``` will return "delete", ```response.value()``` will be empty and should not be
|
```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.
|
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.
|
As mentioned in the section "handling directory nodes", directory nodes are not supported anymore
|
||||||
However it is still possible to watch a whole "directory subtree", or more specifically a set of keys that match the
|
in etcdv3.
|
||||||
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
|
However it is still possible to watch a whole "directory subtree", or more specifically a set of
|
||||||
handy to determine what key is actually changed. Since this can be a long lasting operation you have to be prepared that is
|
keys that match the prefix, for changes with passing ```true``` to the second ```recursive```
|
||||||
terminated by an exception and you have to restart the watch operation.
|
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.
|
||||||
|
|
||||||
The watch also accepts an index parameter that specifies what is the first change we are interested
|
The watch also accepts an index parameter that specifies what is the first change we are interested
|
||||||
about. Since etcd stores the last couple of modifications with this feature you can ensure that your
|
about. Since etcd stores the last couple of modifications with this feature you can ensure that your
|
||||||
|
|
@ -314,44 +314,48 @@ void watch_for_changes()
|
||||||
```
|
```
|
||||||
|
|
||||||
At first glance it seems that ```watch_for_changes()``` calls itself on every value change but in
|
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
|
fact it just sends the asynchron 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
|
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.
|
||||||
|
|
||||||
|
|
||||||
### requesting for lease
|
### Requesting for lease
|
||||||
|
|
||||||
Users can request for lease which is governed by a time-to-live(TTL) value given by the user.
|
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().
|
Moreover, user can attached the lease to a key(s) by indicating the lease id in ```add()```,
|
||||||
Also the ttl will that was granted by etcd server will be indicated in ttl().
|
```set()```, ```modify()``` and ```modify_if()```. Also the ttl will that was granted by etcd
|
||||||
|
server will be indicated in ```ttl()```.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
etcd::Client etcd("http://127.0.0.1:4001");
|
etcd::Client etcd("http://127.0.0.1:4001");
|
||||||
etcd::Response resp = etcd.leasegrant(60).get();
|
etcd::Response resp = etcd.leasegrant(60).get();
|
||||||
etcd.set("/test/key2", "bar", resp.value().lease());
|
etcd.set("/test/key2", "bar", resp.value().lease());
|
||||||
std::cout <<"ttl" << resp.value().ttl();
|
std::cout <<"ttl" << resp.value().ttl();
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Watcher Class
|
### 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.
|
Users can watch a key indefinitely or until user cancels the watch. This can be done by
|
||||||
Watch stream will be cancelled either by user implicitly calling Cancel() or when watcher class is destroyed.
|
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.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
etcd::Watcher watcher("http://127.0.0.1:2379", "/test", printResponse);
|
etcd::Watcher watcher("http://127.0.0.1:2379", "/test", printResponse);
|
||||||
etcd.set("/test/key", "42"); /* print response will be called */
|
etcd.set("/test/key", "42"); /* print response will be called */
|
||||||
etcd.set("/test/key", "43"); /* print response will be called */
|
etcd.set("/test/key", "43"); /* print response will be called */
|
||||||
watcher.Cancel();
|
watcher.Cancel();
|
||||||
etcd.set("/test/key", "43"); /* print response will NOT be called, since watch is already cancelled */
|
etcd.set("/test/key", "43"); /* print response will NOT be called,
|
||||||
|
since watch is already cancelled */
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
||||||
1. Cancellation of asynchronous calls(except for watch)
|
1. Cancellation of asynchronous calls(except for watch)
|
||||||
2. LeaseKeepAlive
|
2. LeaseKeepAlive
|
||||||
|
3. Authentication
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the BSD-3-Clause license - see the [LICENSE](https://github.com/nokia/etcd-cpp-apiv3/blob/master/LICENSE.txt).
|
This project is licensed under the BSD-3-Clause license - see the [LICENSE](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/blob/master/LICENSE.txt).
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,22 @@
|
||||||
find_path(CATCH_INCLUDE_DIR NAMES catch.hpp PATHS ${PROJECT_SOURCE_DIR})
|
find_path(CATCH_INCLUDE_DIR NAMES catch.hpp PATHS ${PROJECT_SOURCE_DIR})
|
||||||
include_directories(${CATCH_INCLUDE_DIR})
|
include_directories(${CATCH_INCLUDE_DIR})
|
||||||
|
|
||||||
add_executable(etcd_test
|
add_custom_target(check ${CMAKE_COMMAND} -E env CTEST_OUTPUT_ON_FAILURE=1
|
||||||
EtcdTest.cpp
|
${CMAKE_CTEST_COMMAND} -C $<CONFIG> --verbose
|
||||||
EtcdSyncTest.cpp
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||||
WatcherTest.cpp
|
|
||||||
LockTest.cpp
|
|
||||||
)
|
|
||||||
set_property(TARGET etcd_test PROPERTY CXX_STANDARD 11)
|
|
||||||
target_include_directories(etcd_test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen)
|
|
||||||
target_include_directories(etcd_test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../proto/gen/proto)
|
|
||||||
|
|
||||||
target_link_libraries(etcd_test etcd-cpp-api)
|
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})
|
||||||
|
add_test(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}>)
|
||||||
|
|
||||||
add_test(etcd_test etcd_test)
|
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)
|
||||||
|
|
||||||
|
add_dependencies(check ${test_name})
|
||||||
|
endforeach()
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
#include "etcd/SyncClient.hpp"
|
#include "etcd/SyncClient.hpp"
|
||||||
|
|
@ -24,25 +25,26 @@ TEST_CASE("sync operations")
|
||||||
CHECK(0 == etcd.set("/test/key2", "43").error_code()); // create new
|
CHECK(0 == etcd.set("/test/key2", "43").error_code()); // create new
|
||||||
CHECK("43" == etcd.set("/test/key2", "44").prev_value().as_string());
|
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());
|
||||||
//CHECK(102 == etcd.set("/test", "42").error_code()); // Not a file
|
|
||||||
|
// 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
|
||||||
|
|
||||||
// rm
|
// rm
|
||||||
CHECK(3 == etcd.ls("/test").keys().size());
|
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());
|
CHECK(2 == etcd.ls("/test").keys().size());
|
||||||
|
|
||||||
// mkdir
|
|
||||||
//CHECK(etcd.mkdir("/test/new_dir").value().is_dir());
|
|
||||||
|
|
||||||
// ls
|
// ls
|
||||||
CHECK(0 == etcd.ls("/test/new_dir").keys().size());
|
CHECK(0 == etcd.ls("/test/new_dir").keys().size());
|
||||||
etcd.set("/test/new_dir/key1", "value1");
|
etcd.set("/test/new_dir/key1", "value1");
|
||||||
etcd.set("/test/new_dir/key2", "value2");
|
etcd.set("/test/new_dir/key2", "value2");
|
||||||
//etcd.mkdir("/test/new_dir/sub_dir");
|
|
||||||
CHECK(2 == etcd.ls("/test/new_dir").keys().size());
|
CHECK(2 == etcd.ls("/test/new_dir").keys().size());
|
||||||
|
|
||||||
// rmdir
|
// rmdir
|
||||||
//CHECK(108 == etcd.rmdir("/test/new_dir").error_code()); // Directory not empty
|
CHECK(100 == etcd.rmdir("/test/new_dir").error_code()); // key not found
|
||||||
CHECK(0 == etcd.rmdir("/test/new_dir", true).error_code());
|
CHECK(0 == etcd.rmdir("/test/new_dir", true).error_code());
|
||||||
|
|
||||||
// compare and swap
|
// compare and swap
|
||||||
|
|
@ -102,64 +104,74 @@ TEST_CASE("sync operations")
|
||||||
CHECK(leaseid == res.value().lease());
|
CHECK(leaseid == res.value().lease());
|
||||||
CHECK("44" == res.value().as_string());
|
CHECK("44" == res.value().as_string());
|
||||||
|
|
||||||
// TEST_CASE("wait for a value change")
|
REQUIRE(0 == etcd.rmdir("/test", true).error_code());
|
||||||
// {
|
}
|
||||||
// etcd::Client etcd(etcd_uri);
|
|
||||||
// etcd.set("/test/key1", "42").wait();
|
|
||||||
|
|
||||||
// pplx::task<etcd::Response> res = etcd.watch("/test/key1");
|
TEST_CASE("wait for a value change")
|
||||||
// CHECK(!res.is_done());
|
{
|
||||||
|
etcd::Client etcd(etcd_uri);
|
||||||
|
etcd.set("/test/key1", "42").wait();
|
||||||
|
|
||||||
// etcd.set("/test/key1", "43").get();
|
pplx::task<etcd::Response> res = etcd.watch("/test/key1");
|
||||||
// sleep(1);
|
CHECK(!res.is_done());
|
||||||
|
|
||||||
// REQUIRE(res.is_done());
|
etcd.set("/test/key1", "43").get();
|
||||||
// REQUIRE("set" == res.get().action());
|
sleep(1);
|
||||||
// CHECK("43" == res.get().value().as_string());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TEST_CASE("wait for a directory change")
|
REQUIRE(res.is_done());
|
||||||
// {
|
REQUIRE("set" == res.get().action());
|
||||||
// etcd::Client etcd(etcd_uri);
|
CHECK("43" == res.get().value().as_string());
|
||||||
|
|
||||||
// pplx::task<etcd::Response> res = etcd.watch("/test", true);
|
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
|
||||||
|
}
|
||||||
|
|
||||||
// etcd.add("/test/key4", "44").wait();
|
TEST_CASE("wait for a directory change")
|
||||||
// REQUIRE(res.is_done());
|
{
|
||||||
// CHECK("create" == res.get().action());
|
etcd::Client etcd(etcd_uri);
|
||||||
// CHECK("44" == res.get().value().as_string());
|
|
||||||
|
|
||||||
// pplx::task<etcd::Response> res2 = etcd.watch("/test", true);
|
pplx::task<etcd::Response> res = etcd.watch("/test", true);
|
||||||
|
|
||||||
// etcd.set("/test/key4", "45").wait();
|
etcd.add("/test/key4", "44").wait();
|
||||||
// sleep(1);
|
sleep(1);
|
||||||
// REQUIRE(res2.is_done());
|
REQUIRE(res.is_done());
|
||||||
// CHECK("set" == res2.get().action());
|
CHECK("create" == res.get().action());
|
||||||
// CHECK("45" == res2.get().value().as_string());
|
CHECK("44" == res.get().value().as_string());
|
||||||
// }
|
|
||||||
|
|
||||||
// TEST_CASE("watch changes in the past")
|
pplx::task<etcd::Response> res2 = etcd.watch("/test", true);
|
||||||
// {
|
|
||||||
// etcd::Client etcd(etcd_uri);
|
|
||||||
|
|
||||||
// int index = etcd.set("/test/key1", "42").get().index();
|
etcd.set("/test/key4", "45").wait();
|
||||||
|
sleep(1);
|
||||||
|
REQUIRE(res2.is_done());
|
||||||
|
CHECK("set" == res2.get().action());
|
||||||
|
CHECK("45" == res2.get().value().as_string());
|
||||||
|
|
||||||
// etcd.set("/test/key1", "43").wait();
|
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
|
||||||
// etcd.set("/test/key1", "44").wait();
|
}
|
||||||
// etcd.set("/test/key1", "45").wait();
|
|
||||||
|
|
||||||
// etcd::Response res = etcd.watch("/test/key1", ++index).get();
|
TEST_CASE("watch changes in the past")
|
||||||
// CHECK("set" == res.action());
|
{
|
||||||
// CHECK("43" == res.value().as_string());
|
etcd::Client etcd(etcd_uri);
|
||||||
|
|
||||||
// res = etcd.watch("/test/key1", ++index).get();
|
int index = etcd.set("/test/key1", "42").get().index();
|
||||||
// CHECK("set" == res.action());
|
|
||||||
// CHECK("44" == res.value().as_string());
|
|
||||||
|
|
||||||
// res = etcd.watch("/test", ++index, true).get();
|
etcd.set("/test/key1", "43").wait();
|
||||||
// CHECK("set" == res.action());
|
etcd.set("/test/key1", "44").wait();
|
||||||
// CHECK("45" == res.value().as_string());
|
etcd.set("/test/key1", "45").wait();
|
||||||
// }
|
|
||||||
|
etcd::Response res = etcd.watch("/test/key1", ++index).get();
|
||||||
|
CHECK("set" == res.action());
|
||||||
|
CHECK("43" == res.value().as_string());
|
||||||
|
|
||||||
|
res = etcd.watch("/test/key1", ++index).get();
|
||||||
|
CHECK("set" == res.action());
|
||||||
|
CHECK("44" == res.value().as_string());
|
||||||
|
|
||||||
|
res = etcd.watch("/test", ++index, true).get();
|
||||||
|
CHECK("set" == res.action());
|
||||||
|
CHECK("45" == res.value().as_string());
|
||||||
|
|
||||||
|
REQUIRE(0 == etcd.rmdir("/test", true).get().error_code());
|
||||||
|
}
|
||||||
|
|
||||||
// TEST_CASE("request cancellation")
|
// TEST_CASE("request cancellation")
|
||||||
// {
|
// {
|
||||||
|
|
@ -173,19 +185,13 @@ TEST_CASE("sync operations")
|
||||||
|
|
||||||
// sleep(1);
|
// sleep(1);
|
||||||
// REQUIRE(res.is_done());
|
// REQUIRE(res.is_done());
|
||||||
// try
|
// try {
|
||||||
// {
|
|
||||||
// res.wait();
|
// res.wait();
|
||||||
// }
|
// }
|
||||||
// catch(pplx::task_canceled const & ex)
|
// catch(pplx::task_canceled const & ex) {
|
||||||
// {
|
|
||||||
// std::cout << "pplx::task_canceled: " << ex.what() << "\n";
|
// std::cout << "pplx::task_canceled: " << ex.what() << "\n";
|
||||||
// }
|
// }
|
||||||
// catch(std::exception const & ex)
|
// catch(std::exception const & ex) {
|
||||||
// {
|
|
||||||
// std::cout << "std::exception: " << ex.what() << "\n";
|
// std::cout << "std::exception: " << ex.what() << "\n";
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
REQUIRE(0 == etcd.rmdir("/test", true).error_code());
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#define CATCH_CONFIG_MAIN
|
#define CATCH_CONFIG_MAIN
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "etcd/Client.hpp"
|
#include "etcd/Client.hpp"
|
||||||
|
|
@ -11,10 +12,8 @@ TEST_CASE("setup")
|
||||||
etcd.rmdir("/test", true).wait();
|
etcd.rmdir("/test", true).wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("add a new key")
|
TEST_CASE("add a new key")
|
||||||
{
|
{
|
||||||
|
|
||||||
etcd::Client etcd("http://127.0.0.1:2379");
|
etcd::Client etcd("http://127.0.0.1:2379");
|
||||||
etcd.rmdir("/test", true).wait();
|
etcd.rmdir("/test", true).wait();
|
||||||
etcd::Response resp = etcd.add("/test/key1", "42").get();
|
etcd::Response resp = etcd.add("/test/key1", "42").get();
|
||||||
|
|
@ -32,7 +31,6 @@ TEST_CASE("add a new key")
|
||||||
CHECK("Key already exists" == etcd.add("/test/key1", "42").get().error_message());
|
CHECK("Key already exists" == etcd.add("/test/key1", "42").get().error_message());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("read a value from etcd")
|
TEST_CASE("read a value from etcd")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379");
|
etcd::Client etcd("http://127.0.0.1:2379");
|
||||||
|
|
@ -41,11 +39,9 @@ TEST_CASE("read a value from etcd")
|
||||||
REQUIRE(resp.is_ok());
|
REQUIRE(resp.is_ok());
|
||||||
REQUIRE(0 == resp.error_code());
|
REQUIRE(0 == resp.error_code());
|
||||||
CHECK("42" == resp.value().as_string());
|
CHECK("42" == resp.value().as_string());
|
||||||
|
|
||||||
CHECK("" == etcd.get("/test").get().value().as_string()); // key points to a directory
|
CHECK("" == etcd.get("/test").get().value().as_string()); // key points to a directory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("simplified read")
|
TEST_CASE("simplified read")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379");
|
etcd::Client etcd("http://127.0.0.1:2379");
|
||||||
|
|
@ -54,8 +50,6 @@ TEST_CASE("simplified read")
|
||||||
CHECK("" == etcd.get("/test/key2").get().value().as_string()); // Key not found
|
CHECK("" == etcd.get("/test/key2").get().value().as_string()); // Key not found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("modify a key")
|
TEST_CASE("modify a key")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379");
|
etcd::Client etcd("http://127.0.0.1:2379");
|
||||||
|
|
@ -66,7 +60,6 @@ TEST_CASE("modify a key")
|
||||||
CHECK("43" == etcd.modify("/test/key1", "42").get().prev_value().as_string());
|
CHECK("43" == etcd.modify("/test/key1", "42").get().prev_value().as_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("set a key")
|
TEST_CASE("set a key")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379");
|
etcd::Client etcd("http://127.0.0.1:2379");
|
||||||
|
|
@ -110,10 +103,8 @@ TEST_CASE("atomic compare-and-swap")
|
||||||
CHECK(!res.is_ok());
|
CHECK(!res.is_ok());
|
||||||
CHECK(100 == res.error_code());
|
CHECK(100 == res.error_code());
|
||||||
CHECK("Key not found" == res.error_message());
|
CHECK("Key not found" == res.error_message());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("delete a value")
|
TEST_CASE("delete a value")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379");
|
etcd::Client etcd("http://127.0.0.1:2379");
|
||||||
|
|
@ -188,7 +179,6 @@ TEST_CASE("deep atomic compare-and-swap")
|
||||||
CHECK(101 == res.error_code());
|
CHECK(101 == res.error_code());
|
||||||
CHECK("Compare failed" == res.error_message());
|
CHECK("Compare failed" == res.error_message());
|
||||||
|
|
||||||
|
|
||||||
// succes with the correct index
|
// succes with the correct index
|
||||||
res = etcd.modify_if("/test/key1", "44", index).get();
|
res = etcd.modify_if("/test/key1", "44", index).get();
|
||||||
REQUIRE(res.is_ok());
|
REQUIRE(res.is_ok());
|
||||||
|
|
@ -200,21 +190,8 @@ TEST_CASE("deep atomic compare-and-swap")
|
||||||
CHECK(!res.is_ok());
|
CHECK(!res.is_ok());
|
||||||
CHECK(101 == res.error_code());
|
CHECK(101 == res.error_code());
|
||||||
CHECK("Compare failed" == res.error_message());
|
CHECK("Compare failed" == res.error_message());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//skip this test case
|
|
||||||
/*
|
|
||||||
TEST_CASE("create a directory")
|
|
||||||
{
|
|
||||||
etcd::Client etcd("http://127.0.0.1:4001");
|
|
||||||
etcd::Response resp = etcd.mkdir("/test/new_dir").get();
|
|
||||||
CHECK("set" == resp.action());
|
|
||||||
CHECK(resp.value().is_dir());
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
TEST_CASE("list a directory")
|
TEST_CASE("list a directory")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379");
|
etcd::Client etcd("http://127.0.0.1:2379");
|
||||||
|
|
@ -249,7 +226,7 @@ TEST_CASE("delete a directory")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379");
|
etcd::Client etcd("http://127.0.0.1:2379");
|
||||||
|
|
||||||
//CHECK(108 == etcd.rmdir("/test/new_dir").get().error_code()); // Directory not empty
|
CHECK(100 == etcd.rmdir("/test/new_dir").get().error_code()); // key not found
|
||||||
etcd::Response resp = etcd.ls("/test/new_dir").get();
|
etcd::Response resp = etcd.ls("/test/new_dir").get();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include <atomic>
|
#define CATCH_CONFIG_MAIN
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
|
@ -23,7 +25,6 @@ TEST_CASE("lock and unlock")
|
||||||
REQUIRE(0 == resp2.error_code());
|
REQUIRE(0 == resp2.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("double lock will fail")
|
TEST_CASE("double lock will fail")
|
||||||
{
|
{
|
||||||
etcd::Client etcd("http://127.0.0.1:2379");
|
etcd::Client etcd("http://127.0.0.1:2379");
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
#include "etcd/Watcher.hpp"
|
#include "etcd/Watcher.hpp"
|
||||||
|
|
@ -10,8 +11,9 @@ void printResponse(etcd::Response const & resp)
|
||||||
{
|
{
|
||||||
++watcher_called;
|
++watcher_called;
|
||||||
std::cout << "print response called" << std::endl;
|
std::cout << "print response called" << std::endl;
|
||||||
if (resp.error_code())
|
if (resp.error_code()) {
|
||||||
std::cout << resp.error_code() << ": " << resp.error_message() << std::endl;
|
std::cout << resp.error_code() << ": " << resp.error_message() << std::endl;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cout << resp.action() << " " << resp.value().as_string() << std::endl;
|
std::cout << resp.action() << " " << resp.value().as_string() << std::endl;
|
||||||
|
|
@ -46,7 +48,6 @@ TEST_CASE("create watcher with cancel")
|
||||||
|
|
||||||
TEST_CASE("create watcher")
|
TEST_CASE("create watcher")
|
||||||
{
|
{
|
||||||
|
|
||||||
etcd::SyncClient etcd(etcd_uri);
|
etcd::SyncClient etcd(etcd_uri);
|
||||||
etcd.rmdir("/test", true);
|
etcd.rmdir("/test", true);
|
||||||
|
|
||||||
|
|
@ -74,17 +75,13 @@ TEST_CASE("create watcher")
|
||||||
|
|
||||||
// sleep(1);
|
// sleep(1);
|
||||||
// REQUIRE(res.is_done());
|
// REQUIRE(res.is_done());
|
||||||
// try
|
// try {
|
||||||
// {
|
|
||||||
// res.wait();
|
// res.wait();
|
||||||
// }
|
// }
|
||||||
// catch(pplx::task_canceled const & ex)
|
// catch(pplx::task_canceled const & ex) {
|
||||||
// {
|
|
||||||
// std::cout << "pplx::task_canceled: " << ex.what() << "\n";
|
// std::cout << "pplx::task_canceled: " << ex.what() << "\n";
|
||||||
// }
|
// }
|
||||||
// catch(std::exception const & ex)
|
// catch(std::exception const & ex) {
|
||||||
// {
|
|
||||||
// std::cout << "std::exception: " << ex.what() << "\n";
|
// std::cout << "std::exception: " << ex.what() << "\n";
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue