#ifndef __ETCD_SYNC_CLIENT_HPP__ #define __ETCD_SYNC_CLIENT_HPP__ #include #include #include #include #include #include #include "etcd/Response.hpp" #include "etcd/v3/action_constants.hpp" namespace etcdv3 { struct ActionParameters; class AsyncCompareAndDeleteAction; class AsyncCompareAndSwapAction; class AsyncDeleteAction; class AsyncCampaignAction; class AsyncProclaimAction; class AsyncLeaderAction; class AsyncObserveAction; class AsyncResignAction; class AsyncHeadAction; class AsyncLeaseGrantAction; class AsyncLeaseRevokeAction; class AsyncLeaseKeepAliveAction; class AsyncLeaseTimeToLiveAction; class AsyncLeaseLeasesAction; class AsyncLockAction; class AsyncUnlockAction; class AsyncPutAction; class AsyncRangeAction; class AsyncSetAction; class AsyncTxnAction; class AsyncUpdateAction; class AsyncWatchAction; enum class AtomicityType; class Transaction; namespace detail { std::string string_plus_one(std::string const &value); } } #if defined(WITH_GRPC_CHANNEL_CLASS) namespace grpc { class Channel; class ChannelArguments; } #else namespace grpc_impl { class Channel; class ChannelArguments; } #endif namespace etcd { using etcdv3::ERROR_KEY_NOT_FOUND; using etcdv3::ERROR_COMPARE_FAILED; using etcdv3::ERROR_KEY_ALREADY_EXISTS; using etcdv3::ERROR_ACTION_CANCELLED; class KeepAlive; class Watcher; class Client; /** * Client is responsible for maintaining a connection towards an etcd server. * Etcd operations can be reached via the methods of the client. */ /** * SyncClient is a wrapper around etcd::Client and provides a simplified sync interface with blocking operations. * * In case of any exceptions occur in the backend the Response value's error_message will contain the * text of the exception and the error code will be 600 * * Use this class only if performance does not matter. */ class SyncClient { private: class TokenAuthenticator; class TokenAuthenticatorDeleter { public: void operator()(TokenAuthenticator *authenticator); }; public: /** * 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, separated by ',' or ';'. * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds. */ SyncClient(std::string const & etcd_url, std::string const & 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, separated by ',' or ';'. * @param arguments user provided grpc channel arguments. */ SyncClient(std::string const & etcd_url, #if defined(WITH_GRPC_CHANNEL_CLASS) grpc::ChannelArguments const & arguments #else grpc_impl::ChannelArguments const & arguments #endif ); /** * 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, separated by ',' or ';'. * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds. */ static SyncClient *WithUrl(std::string const & etcd_url, 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, separated by ',' or ';'. * @param arguments user provided grpc channel arguments. */ static SyncClient *WithUrl(std::string const & etcd_url, #if defined(WITH_GRPC_CHANNEL_CLASS) grpc::ChannelArguments const & arguments #else grpc_impl::ChannelArguments const & arguments #endif ); /** * 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, separated by ',' or ';'. * @param username username of etcd auth * @param password password of etcd auth * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds. * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd. */ SyncClient(std::string const & etcd_url, std::string const & username, std::string const & password, int const auth_token_ttl = 300, 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, separated by ',' or ';'. * @param username username of etcd auth * @param password password of etcd auth * @param arguments user provided grpc channel arguments. * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd. * Default value should be 300. */ SyncClient(std::string const & etcd_url, std::string const & username, std::string const & password, int const auth_token_ttl, #if defined(WITH_GRPC_CHANNEL_CLASS) grpc::ChannelArguments const & arguments #else grpc_impl::ChannelArguments const & arguments #endif ); /** * 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, separated by ',' or ';'. * @param username username of etcd auth * @param password password of etcd auth * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds. * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd. */ static SyncClient *WithUser(std::string const & etcd_url, std::string const & username, std::string const & password, int const auth_token_ttl = 300, 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, separated by ',' or ';'. * @param username username of etcd auth * @param password password of etcd auth * @param arguments user provided grpc channel arguments. * @param auth_token_ttl TTL seconds for auth token, see also `--auth-token-ttl` flags of etcd. * Default value should be 300. */ static SyncClient *WithUser(std::string const & etcd_url, std::string const & username, std::string const & password, int const auth_token_ttl, #if defined(WITH_GRPC_CHANNEL_CLASS) grpc::ChannelArguments const & arguments #else grpc_impl::ChannelArguments const & arguments #endif ); /** * 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, separated 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 privkey private key file for SSL/TLS authentication, could be empty string. * @param load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds. */ SyncClient(std::string const & etcd_url, std::string const & ca, std::string const & cert, std::string const & privkey, std::string const & target_name_override, 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, separated 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 privkey private key file for SSL/TLS authentication, could be empty string. * @param arguments user provided grpc channel arguments. */ SyncClient(std::string const & etcd_url, std::string const & ca, std::string const & cert, std::string const & privkey, std::string const & target_name_override, #if defined(WITH_GRPC_CHANNEL_CLASS) grpc::ChannelArguments const & arguments #else grpc_impl::ChannelArguments const & arguments #endif ); /** * 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, separated 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 privkey 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 load_balancer is the load balance strategy, can be one of round_robin/pick_first/grpclb/xds. */ static SyncClient *WithSSL(std::string const & etcd_url, std::string const & ca, std::string const & cert = "", std::string const & privkey = "", std::string const & target_name_override = "", 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, separated 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 privkey 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 SyncClient *WithSSL(std::string const & etcd_url, #if defined(WITH_GRPC_CHANNEL_CLASS) grpc::ChannelArguments const & arguments, #else grpc_impl::ChannelArguments const & arguments, #endif std::string const & ca, std::string const & cert = "", std::string const & privkey = "", std::string const & target_name_override = ""); ~SyncClient(); /** * Get the HEAD revision of the connected etcd server. */ Response head(); /** * Get the value of specified key from the etcd server * @param key is the key to be read */ Response get(std::string const & key); /** * Sets the value of a key. The key will be modified if already exists or created * if it does not exists. * @param key is the key to be created or modified * @param value is the new value to be set */ Response set(std::string const & key, std::string const & value, int ttl = 0); /** * Sets the value of a key. The key will be modified if already exists or created * if it does not exists. * @param key is the key to be created or modified * @param value is the new value to be set * @param leaseId is the lease attached to the key */ Response set(std::string const & key, std::string const & value, int64_t leaseId); /** * Creates a new key and sets it's value. Fails if the key already exists. * @param key is the key to be created * @param value is the value to be set */ Response add(std::string const & key, std::string const & value, int ttl = 0); /** * Creates a new key and sets it's value. Fails if the key already exists. * @param key is the key to be created * @param value is the value to be set * @param leaseId is the lease attached to the key */ Response add(std::string const & key, std::string const & value, int64_t leaseId); /** * Put a new key-value pair. * @param key is the key to be put * @param value is the value to be put */ Response put(std::string const & key, std::string const & value); /** * Modifies an existing key. Fails if the key does not exists. * @param key is the key to be modified * @param value is the new value to be set */ Response modify(std::string const & key, std::string const & value, int ttl = 0); /** * Modifies an existing key. Fails if the key does not exists. * @param key is the key to be modified * @param value is the new value to be set * @param leaseId is the lease attached to the key */ Response modify(std::string const & key, std::string const & value, int64_t leaseId); /** * Modifies an existing key only if it has a specific value. Fails if the key does not exists * or the original value differs from the expected one. * @param key is the key to be modified * @param value is the new value to be set * @param old_value is the value to be replaced */ Response modify_if(std::string const & key, std::string const & value, std::string const & old_value, int ttl = 0); /** * Modifies an existing key only if it has a specific value. Fails if the key does not exists * or the original value differs from the expected one. * @param key is the key to be modified * @param value is the new value to be set * @param old_value is the value to be replaced * @param leaseId is the lease attached to the key */ Response modify_if(std::string const & key, std::string const & value, std::string const & old_value, int64_t leaseId); /** * Modifies an existing key only if it has a specific modification index value. Fails if the key * does not exists or the modification index of the previous value differs from the expected one. * @param key is the key to be modified * @param value is the new value to be set * @param old_index is the expected index of the original value */ Response modify_if(std::string const & key, std::string const & value, int64_t old_index, int ttl = 0); /** * Modifies an existing key only if it has a specific modification index value. Fails if the key * does not exists or the modification index of the previous value differs from the expected one. * @param key is the key to be modified * @param value is the new value to be set * @param old_index is the expected index of the original value * @param leaseId is the lease attached to the key */ Response modify_if(std::string const & key, std::string const & value, int64_t old_index, int64_t leaseId); /** * Removes a single key. The key has to point to a plain, non directory entry. * @param key is the key to be deleted */ Response rm(std::string const & key); /** * Removes a single key but only if it has a specific value. Fails if the key does not exists * or the its value differs from the expected one. * @param key is the key to be deleted */ Response rm_if(std::string const & key, std::string const & old_value); /** * Removes an existing key only if it has a specific modification index value. Fails if the key * does not exists or the modification index of it differs from the expected one. * @param key is the key to be deleted * @param old_index is the expected index of the existing value */ Response rm_if(std::string const & key, int64_t old_index); /** * Removes a directory node. Fails if the parent directory dos not exists or not a directory. * @param key is the directory to be created to be listed * @param recursive if true then delete a whole subtree, otherwise deletes only an empty directory. */ Response rmdir(std::string const & key, bool recursive = false); /** * Removes multiple keys between [key, range_end). * * This overload for `const char *` is to avoid const char * to bool implicit casting. * * @param key is the directory to be created to be listed * @param range_end is the end of key range to be removed. */ Response rmdir(std::string const & key, const char *range_end); /** * Removes multiple keys between [key, range_end). * * @param key is the directory to be created to be listed * @param range_end is the end of key range to be removed. */ Response rmdir(std::string const & key, std::string const &range_end); /** * Gets a directory listing of the directory identified by the key. * @param key is the key to be listed */ Response ls(std::string const & key); /** * Gets a directory listing of the directory identified by the key. * @param key is the key to be listed * @param limit is the size limit of results to be listed, we don't use default parameters * to ensure backwards binary compatibility. */ Response ls(std::string const & key, size_t const limit); /** * Gets a directory listing of the directory identified by the key and range_end, i.e., get * all keys in the range [key, range_end). * * @param key is the key to be listed * @param range_end is the end of key range to be listed */ Response ls(std::string const & key, std::string const &range_end); /** * Gets a directory listing of the directory identified by the key and range_end, i.e., get * all keys in the range [key, range_end). * * @param key is the key to be listed * @param range_end is the end of key range to be listed * @param limit is the size limit of results to be listed, we don't use default parameters * to ensure backwards binary compatibility. */ Response ls(std::string const & key, std::string const &range_end, size_t const limit); /** * Watches for changes of a key or a subtree. Please note that if you watch e.g. "/testdir" and * a new key is created, like "/testdir/newkey" then no change happened in the value of * "/testdir" so your watch will not detect this. If you want to detect addition and deletion of * directory entries then you have to do a recursive watch. * @param key is the value or directory to be watched * @param recursive if true watch a whole subtree */ Response watch(std::string const & key, bool recursive = false); /** * Watches for changes of a key or a subtree from a specific index. The index value can be in the "past". * @param key is the value or directory to be watched * @param fromIndex the first index we are interested in * @param recursive if true watch a whole subtree */ Response watch(std::string const & key, int64_t fromIndex, bool recursive = false); /** * Watches for changes of a range of keys inside [key, range_end). * * This overload for `const char *` is to avoid const char * to bool implicit casting. * * @param key is the value or directory to be watched * @param range_end is the end of key range to be removed. */ Response watch(std::string const & key, const char *range_end); /** * Watches for changes of a range of keys inside [key, range_end). * * @param key is the value or directory to be watched * @param range_end is the end of key range to be removed. */ Response watch(std::string const & key, std::string const &range_end); /** * Watches for changes of a range of keys inside [key, range_end) from a specific index. The index value * can be in the "past". * * Watches for changes of a key or a subtree from a specific index. The index value can be in the "past". * @param key is the value or directory to be watched * @param range_end is the end of key range to be removed. * @param fromIndex the first index we are interested in */ Response watch(std::string const & key, std::string const &range_end, int64_t fromIndex); /** * Grants a lease. * @param ttl is the time to live of the lease */ Response leasegrant(int ttl); /** * Grants a lease. * @param ttl is the time to live of the lease */ std::shared_ptr leasekeepalive(int ttl); /** * Revoke a lease. * @param lease_id is the id the lease */ Response leaserevoke(int64_t lease_id); /** * Get time-to-live of a lease. * @param lease_id is the id the lease */ Response leasetimetolive(int64_t lease_id); /** * List all alive leases, equivalent to `etcdctl lease list`. */ Response leases(); /** * Gains a lock at a key, using a default created lease, using the default lease (10 seconds), with * keeping alive has already been taken care of by the library. * @param key is the key to be used to request the lock. */ Response lock(std::string const &key); /** * Gains a lock at a key, using a default created lease, using the specified lease TTL (in seconds), with * keeping alive has already been taken care of by the library. * @param key is the key to be used to request the lock. * @param lease_ttl is the TTL used to create a lease for the key. */ Response lock(std::string const &key, int lease_ttl); /** * Gains a lock at a key, using a user-provided lease, the lifetime of the lease won't be taken care * of by the library. * @param key is the key to be used to request the lock. */ Response lock_with_lease(std::string const &key, int64_t lease_id); /** * Releases a lock at a key. * @param key is the lock key to release. */ Response unlock(std::string const &lock_key); /** * Execute a etcd transaction. * @param txn is the transaction object to be executed. */ Response txn(etcdv3::Transaction const &txn); /** * Campaign for the election @name@. * * @param name is the name of election that will campaign for. * @param lease_id is a user-managed (usually with a `KeepAlive`) lease id. * @param value is the value for campaign. * * @returns a leader key if succeed, consist of * * - name: the name of the election * - key: a generated election key * - created rev: the revision of the generated key * - lease: the lease id of the election leader */ Response campaign(std::string const &name, int64_t lease_id, std::string const &value); /** * Updates the value of election with a new value, with leader key returns by * @campaign@. * * @param name is the name of election * @param lease_id is the user-provided lease id for the proclamation * @param key is the generated associated key returned by @campaign@ * @param revision is the created revision of key-value returned by @campaign@ * @param value is the new value to set. */ Response proclaim(std::string const &name, int64_t lease_id, std::string const &key, int64_t revision, std::string const &value); /** * Get the current leader proclamation. * * @param name is the names of election. * * @returns current election key and value. */ Response leader(std::string const &name); /** * An observer that will cancel the associated election::observe request * when being destruct. */ class Observer { public: ~Observer(); // wait a at least one response from the observer. Response WaitOnce(); private: std::shared_ptr action = nullptr; friend class SyncClient; }; /** * Observe the leader change. * * @param name is the names of election to watch. * * @returns an observer that holds that action and will cancel the request when being destructed. */ std::unique_ptr observe(std::string const &name); /** * Updates the value of election with a new value, with leader key returns by * @campaign@. * * @param name is the name of election * @param lease_id is the user-provided lease id for the proclamation * @param key is the generated associated key returned by @campaign@ * @param revision is the created revision of key-value returned by @campaign@ */ Response resign(std::string const &name, int64_t lease_id, std::string const &key, int64_t revision); private: // TODO: use std::unique_ptr<> std::shared_ptr head_internal(); std::shared_ptr get_internal(std::string const & key); std::shared_ptr set_internal(std::string const & key, std::string const & value, int64_t leaseId); std::shared_ptr add_internal(std::string const & key, std::string const & value, int64_t leaseId); std::shared_ptr put_internal(std::string const & key, std::string const & value); std::shared_ptr modify_internal(std::string const & key, std::string const & value, int64_t leaseId); std::shared_ptr modify_if_internal(std::string const & key, std::string const & value, int64_t old_index, std::string const & old_value, int64_t leaseId, etcdv3::AtomicityType const & atomicity_type); std::shared_ptr rm_internal(std::string const & key); std::shared_ptr rm_if_internal(std::string const & key, int64_t old_index, const std::string & old_value, etcdv3::AtomicityType const & atomicity_type); std::shared_ptr rmdir_internal(std::string const & key, bool recursive = false); std::shared_ptr rmdir_internal(std::string const & key, std::string const &range_end); std::shared_ptr ls_internal(std::string const & key, size_t const limit); std::shared_ptr ls_internal(std::string const & key, std::string const &range_end, size_t const limit); std::shared_ptr watch_internal(std::string const & key, int64_t fromIndex, bool recursive = false); std::shared_ptr watch_internal(std::string const & key, std::string const &range_end, int64_t fromIndex); std::shared_ptr leaserevoke_internal(int64_t lease_id); std::shared_ptr leasetimetolive_internal(int64_t lease_id); std::shared_ptr leases_internal(); Response lock_internal(std::string const &key, std::shared_ptr const &keepalive); std::shared_ptr lock_with_lease_internal(std::string const &key, int64_t lease_id); std::shared_ptr unlock_internal(std::string const &lock_key); std::shared_ptr txn_internal(etcdv3::Transaction const &txn); std::shared_ptr campaign_internal(std::string const &name, int64_t lease_id, std::string const &value); std::shared_ptr proclaim_internal( std::string const &name, int64_t lease_id, std::string const &key, int64_t revision, std::string const &value); std::shared_ptr leader_internal(std::string const &name); std::shared_ptr resign_internal(std::string const &name, int64_t lease_id, std::string const &key, int64_t revision); public: /** * Return current auth token. */ const std::string ¤t_auth_token() const; /** * Obtain the underlying gRPC channel. */ #if defined(WITH_GRPC_CHANNEL_CLASS) std::shared_ptr grpc_channel() const; #else std::shared_ptr grpc_channel() const; #endif /** * Set a timeout value for grpc operations. */ template void set_grpc_timeout(std::chrono::duration const &timeout) { grpc_timeout = std::chrono::duration_cast(timeout); } /** * Get the current timeout value for grpc operations. */ std::chrono::microseconds get_grpc_timeout() const { return this->grpc_timeout; } private: #if defined(WITH_GRPC_CHANNEL_CLASS) std::shared_ptr channel; #else std::shared_ptr channel; #endif mutable std::unique_ptr token_authenticator; mutable std::chrono::microseconds grpc_timeout = std::chrono::microseconds::zero(); struct EtcdServerStubs; struct EtcdServerStubsDeleter { void operator()(EtcdServerStubs *stubs); }; std::unique_ptr stubs; std::mutex mutex_for_keepalives; std::map leases_for_locks; std::map> keep_alive_for_locks; friend class KeepAlive; friend class Watcher; friend class Client; }; } #endif