Accept an `grpc::ChannelArguments` argument in client's constructors.

Resolves #103.

Signed-off-by: Tao He <sighingnow@gmail.com>
This commit is contained in:
Tao He 2021-11-09 00:03:28 +08:00
parent bfb56be151
commit a23e156886
3 changed files with 234 additions and 16 deletions

View File

@ -81,7 +81,7 @@ Github when you encounter problems when working with etcd 3.x releases.
## Usage ## Usage
```c++ ```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(); etcd::Response response = etcd.get("/test/key1").get();
std::cout << response.value().as_string(); std::cout << response.value().as_string();
``` ```
@ -96,7 +96,7 @@ are blocking until the HTTP response arrives or some error situation happens. `g
also returns the `etcd::Response` object. 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:2379");
pplx::task<etcd::Response> response_task = etcd.get("/test/key1"); 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();
@ -111,7 +111,7 @@ callback should be either a `etcd::Response` or a `pplx::task<etcd:Response>`. Y
probably use a C++ lambda function here as a callback. probably use a C++ lambda function here as a callback.
```c++ ```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) etcd.get("/test/key1").then([](etcd::Response response)
{ {
std::cout << response.value().as_string(); std::cout << response.value().as_string();
@ -127,7 +127,7 @@ how you can catch the errors generated by the REST interface. The `get()` call w
this case since the response has been already arrived (we are inside the callback). this case since the response has been already arrived (we are inside the callback).
```c++ ```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) etcd.get("/test/key1").then([](pplx::task<etcd::Response> response_task)
{ {
try try
@ -238,7 +238,7 @@ transport security using openssl.
We also provide a tool [`setup-ca.sh`](./security-config/setup-ca.sh) as a helper for development and testing. We also provide a tool [`setup-ca.sh`](./security-config/setup-ca.sh) as a helper for development and testing.
#### transport security & Multiple endpoints #### transport security & multiple endpoints
If you want to use multiple `https://` endpoints, and you are working with self-signed certificates, you If you want to use multiple `https://` endpoints, and you are working with self-signed certificates, you
may encountered errors like may encountered errors like
@ -271,6 +271,36 @@ And pass a `target_name_override` arguments to `WithSSL`,
For more discussion about this feature, see also [#87](https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3/issues/87), 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). [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).
### Reading a value ### Reading a value
You can read a value with the `get()` method of the client instance. The only parameter is the You can read a value with the `get()` method of the client instance. The only parameter is the
@ -290,7 +320,7 @@ key. You can also get the index number of the creation and the last modification
the `created_index()` and the `modified_index()` methods. the `created_index()` and the `modified_index()` methods.
```c++ ```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"); pplx::task<etcd::Response> response_task = etcd.get("/test/key1");
try try
@ -317,7 +347,7 @@ what was the previous value that this operation was overwritten. You can do that
`prev_value()` method of the response object. `prev_value()` method of the response object.
```c++ ```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.set("/test/key1", "42"); pplx::task<etcd::Response> response_task = etcd.set("/test/key1", "42");
try try
@ -407,7 +437,7 @@ keys defined by the prefix. mkdir method is removed since etcdv3 treats everythi
returns with the i-th element of the values vector, so `response.values()[i] == response.value(i)`. returns with the i-th element of the values vector, so `response.values()[i] == response.value(i)`.
```c++ ```c++
etcd::Client etcd("http://127.0.0.1:4001"); etcd::Client etcd("http://127.0.0.1:2379");
etcd::Response resp = etcd.ls("/test/new_dir").get(); etcd::Response resp = etcd.ls("/test/new_dir").get();
for (int i = 0; i < resp.keys().size(); ++i) for (int i = 0; i < resp.keys().size(); ++i)
{ {
@ -424,7 +454,7 @@ prefix will be deleted. All deleted keys will be placed in `response.values()` a
`response.keys()`. This parameter defaults to `false`. `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:2379");
etcd.set("/test/key1", "foo"); etcd.set("/test/key1", "foo");
etcd.set("/test/key2", "bar"); etcd.set("/test/key2", "bar");
etcd.set("/test/key3", "foo_bar"); etcd.set("/test/key3", "foo_bar");
@ -444,7 +474,7 @@ The key supplied will NOT be treated as a prefix and will be treated as a normal
Etcd lock has been supported as follows: Etcd lock has been supported as follows:
```c++ ```c++
etcd::Client etcd("http://127.0.0.1:4001"); etcd::Client etcd("http://127.0.0.1:2379");
etcd.lock("/test/lock"); etcd.lock("/test/lock");
``` ```
@ -454,7 +484,7 @@ the lock is unlocked.
Users can also feed their own lease directory for lock: Users can also feed their own lease directory for lock:
```c++ ```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); etcd.lock_with_lease("/test/lock", lease_id);
``` ```
@ -575,7 +605,7 @@ Moreover, user can attached the lease to a key(s) by indicating the lease id in
server will be indicated in `ttl()`. 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:2379");
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();

View File

@ -20,6 +20,10 @@ namespace etcdv3 {
} }
} }
namespace grpc {
class ChannelArguments;
}
#if defined(WITH_GRPC_CHANNEL_CLASS) #if defined(WITH_GRPC_CHANNEL_CLASS)
namespace grpc { namespace grpc {
class Channel; class Channel;
@ -56,6 +60,16 @@ namespace etcd
Client(std::string const & etcd_url, Client(std::string const & etcd_url,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
/**
* Constructs an etcd client object.
*
* @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* or multiple url, seperated by ',' or ';'.
* @param arguments user provided grpc channel arguments.
*/
Client(std::string const & etcd_url,
grpc::ChannelArguments const & arguments);
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
@ -66,6 +80,16 @@ namespace etcd
static etcd::Client *WithUrl(std::string const & etcd_url, static etcd::Client *WithUrl(std::string const & etcd_url,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
/**
* Constructs an etcd client object.
*
* @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* or multiple url, seperated by ',' or ';'.
* @param arguments user provided grpc channel arguments.
*/
static etcd::Client *WithUrl(std::string const & etcd_url,
grpc::ChannelArguments const & arguments);
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
@ -80,6 +104,20 @@ namespace etcd
std::string const & password, std::string const & password,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
/**
* Constructs an etcd client object.
*
* @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* or multiple url, seperated by ',' or ';'.
* @param username username of etcd auth
* @param password password of etcd auth
* @param arguments user provided grpc channel arguments.
*/
Client(std::string const & etcd_url,
std::string const & username,
std::string const & password,
grpc::ChannelArguments const & arguments);
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
@ -94,6 +132,20 @@ namespace etcd
std::string const & password, std::string const & password,
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
/**
* Constructs an etcd client object.
*
* @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* or multiple url, seperated by ',' or ';'.
* @param username username of etcd auth
* @param password password of etcd auth
* @param arguments user provided grpc channel arguments.
*/
static etcd::Client *WithUser(std::string const & etcd_url,
std::string const & username,
std::string const & password,
grpc::ChannelArguments const & arguments);
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
@ -111,6 +163,23 @@ namespace etcd
std::string const & target_name_override, std::string const & target_name_override,
std::string const & load_balancer); std::string const & load_balancer);
/**
* Constructs an etcd client object.
*
* @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* or multiple url, seperated by ',' or ';'.
* @param ca root CA file for SSL/TLS connection.
* @param cert cert chain file for SSL/TLS authentication, could be empty string.
* @param key private key file for SSL/TLS authentication, could be empty string.
* @param arguments user provided grpc channel arguments.
*/
Client(std::string const & etcd_url,
std::string const & ca,
std::string const & cert,
std::string const & key,
std::string const & target_name_override,
grpc::ChannelArguments const & arguments);
/** /**
* Constructs an etcd client object. * Constructs an etcd client object.
* *
@ -131,6 +200,26 @@ namespace etcd
std::string const & target_name_override = "", std::string const & target_name_override = "",
std::string const & load_balancer = "round_robin"); std::string const & load_balancer = "round_robin");
/**
* Constructs an etcd client object.
*
* @param etcd_url is the url of the etcd server to connect to, like "http://127.0.0.1:2379",
* or multiple url, seperated by ',' or ';'.
* @param ca root CA file for SSL/TLS connection.
* @param cert cert chain file for SSL/TLS authentication, could be empty string.
* @param key private key file for SSL/TLS authentication, could be empty string.
* @param target_name_override Override the target host name if you want to pass multiple address
* for load balancing with SSL, and there's no DNS. The @target_name_override@ must exist in the
* SANS of your SSL certificate.
* @param arguments user provided grpc channel arguments.
*/
static etcd::Client *WithSSL(std::string const & etcd_url,
grpc::ChannelArguments const & arguments,
std::string const & ca,
std::string const & cert = "",
std::string const & key = "",
std::string const & target_name_override = "");
/** /**
* Get the HEAD revision of the connected etcd server. * Get the HEAD revision of the connected etcd server.
*/ */

View File

@ -200,11 +200,36 @@ etcd::Client::Client(std::string const & address,
stubs->electionServiceStub = Election::NewStub(this->channel); stubs->electionServiceStub = Election::NewStub(this->channel);
} }
etcd::Client::Client(std::string const & address,
grpc::ChannelArguments const & arguments)
{
// create channels
std::string const addresses = etcd::detail::strip_and_resolve_addresses(address);
grpc::ChannelArguments grpc_args = arguments;
grpc_args.SetMaxSendMessageSize(std::numeric_limits<int>::max());
grpc_args.SetMaxReceiveMessageSize(std::numeric_limits<int>::max());
std::shared_ptr<grpc::ChannelCredentials> creds = grpc::InsecureChannelCredentials();
this->channel = grpc::CreateCustomChannel(addresses, creds, grpc_args);
// create stubs
stubs.reset(new EtcdServerStubs{});
stubs->kvServiceStub = KV::NewStub(this->channel);
stubs->watchServiceStub= Watch::NewStub(this->channel);
stubs->leaseServiceStub= Lease::NewStub(this->channel);
stubs->lockServiceStub = Lock::NewStub(this->channel);
stubs->electionServiceStub = Election::NewStub(this->channel);
}
etcd::Client *etcd::Client::WithUrl(std::string const & etcd_url, etcd::Client *etcd::Client::WithUrl(std::string const & etcd_url,
std::string const & load_balancer) { std::string const & load_balancer) {
return new etcd::Client(etcd_url, load_balancer); return new etcd::Client(etcd_url, load_balancer);
} }
etcd::Client *etcd::Client::WithUrl(std::string const & etcd_url,
grpc::ChannelArguments const & arguments) {
return new etcd::Client(etcd_url, arguments);
}
etcd::Client::Client(std::string const & address, etcd::Client::Client(std::string const & address,
std::string const & username, std::string const & username,
std::string const & password, std::string const & password,
@ -235,6 +260,35 @@ etcd::Client::Client(std::string const & address,
stubs->electionServiceStub = Election::NewStub(this->channel); stubs->electionServiceStub = Election::NewStub(this->channel);
} }
etcd::Client::Client(std::string const & address,
std::string const & username,
std::string const & password,
grpc::ChannelArguments const & arguments)
{
// create channels
std::string const addresses = etcd::detail::strip_and_resolve_addresses(address);
grpc::ChannelArguments grpc_args = arguments;
grpc_args.SetMaxSendMessageSize(std::numeric_limits<int>::max());
grpc_args.SetMaxReceiveMessageSize(std::numeric_limits<int>::max());
std::shared_ptr<grpc::ChannelCredentials> creds = grpc::InsecureChannelCredentials();
this->channel = grpc::CreateCustomChannel(addresses, creds, grpc_args);
// auth
std::string token_or_message;
if (!etcd::detail::authenticate(this->channel, username, password, token_or_message)) {
throw std::invalid_argument("Etcd authentication failed: " + token_or_message);
}
this->auth_token = token_or_message;
// setup stubs
stubs.reset(new EtcdServerStubs{});
stubs->kvServiceStub = KV::NewStub(this->channel);
stubs->watchServiceStub= Watch::NewStub(this->channel);
stubs->leaseServiceStub= Lease::NewStub(this->channel);
stubs->lockServiceStub = Lock::NewStub(this->channel);
stubs->electionServiceStub = Election::NewStub(this->channel);
}
etcd::Client *etcd::Client::WithUser(std::string const & etcd_url, etcd::Client *etcd::Client::WithUser(std::string const & etcd_url,
std::string const & username, std::string const & username,
std::string const & password, std::string const & password,
@ -242,6 +296,14 @@ etcd::Client *etcd::Client::WithUser(std::string const & etcd_url,
return new etcd::Client(etcd_url, username, password, load_balancer); return new etcd::Client(etcd_url, username, password, load_balancer);
} }
etcd::Client *etcd::Client::WithUser(std::string const & etcd_url,
std::string const & username,
std::string const & password,
grpc::ChannelArguments const & arguments) {
return new etcd::Client(etcd_url, username, password, arguments);
}
etcd::Client::Client(std::string const & address, etcd::Client::Client(std::string const & address,
std::string const & ca, std::string const & ca,
std::string const & cert, std::string const & cert,
@ -271,6 +333,34 @@ etcd::Client::Client(std::string const & address,
stubs->electionServiceStub = Election::NewStub(this->channel); stubs->electionServiceStub = Election::NewStub(this->channel);
} }
etcd::Client::Client(std::string const & address,
std::string const & ca,
std::string const & cert,
std::string const & key,
std::string const & target_name_override,
grpc::ChannelArguments const & arguments)
{
// create channels
std::string const addresses = etcd::detail::strip_and_resolve_addresses(address);
grpc::ChannelArguments grpc_args = arguments;
grpc_args.SetMaxSendMessageSize(std::numeric_limits<int>::max());
grpc_args.SetMaxReceiveMessageSize(std::numeric_limits<int>::max());
std::shared_ptr<grpc::ChannelCredentials> creds = grpc::SslCredentials(
etcd::detail::make_ssl_credentials(ca, cert, key));
if (!target_name_override.empty()) {
grpc_args.SetString(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, target_name_override);
}
this->channel = grpc::CreateCustomChannel(addresses, creds, grpc_args);
// setup stubs
stubs.reset(new EtcdServerStubs{});
stubs->kvServiceStub = KV::NewStub(this->channel);
stubs->watchServiceStub= Watch::NewStub(this->channel);
stubs->leaseServiceStub= Lease::NewStub(this->channel);
stubs->lockServiceStub = Lock::NewStub(this->channel);
stubs->electionServiceStub = Election::NewStub(this->channel);
}
etcd::Client *etcd::Client::WithSSL(std::string const & etcd_url, etcd::Client *etcd::Client::WithSSL(std::string const & etcd_url,
std::string const & ca, std::string const & ca,
std::string const & cert, std::string const & cert,
@ -280,6 +370,15 @@ etcd::Client *etcd::Client::WithSSL(std::string const & etcd_url,
return new etcd::Client(etcd_url, ca, cert, key, target_name_override, load_balancer); return new etcd::Client(etcd_url, ca, cert, key, target_name_override, load_balancer);
} }
etcd::Client *etcd::Client::WithSSL(std::string const & etcd_url,
grpc::ChannelArguments const & arguments,
std::string const & ca,
std::string const & cert,
std::string const & key,
std::string const & target_name_override) {
return new etcd::Client(etcd_url, ca, cert, key, target_name_override, arguments);
}
pplx::task<etcd::Response> etcd::Client::head() pplx::task<etcd::Response> etcd::Client::head()
{ {
etcdv3::ActionParameters params; etcdv3::ActionParameters params;