{"id":22220954,"url":"https://github.com/nokia/etcd-cpp-api","last_synced_at":"2025-07-27T16:30:54.413Z","repository":{"id":32418964,"uuid":"35996163","full_name":"nokia/etcd-cpp-api","owner":"nokia","description":"C++ API for etcd","archived":false,"fork":false,"pushed_at":"2020-08-03T15:09:35.000Z","size":12,"stargazers_count":21,"open_issues_count":1,"forks_count":55,"subscribers_count":68,"default_branch":"master","last_synced_at":"2024-04-14T13:07:13.208Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nokia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-05-21T06:49:46.000Z","updated_at":"2023-09-25T12:40:59.000Z","dependencies_parsed_at":"2022-09-17T23:00:18.811Z","dependency_job_id":null,"html_url":"https://github.com/nokia/etcd-cpp-api","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nokia%2Fetcd-cpp-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nokia%2Fetcd-cpp-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nokia%2Fetcd-cpp-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nokia%2Fetcd-cpp-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nokia","download_url":"https://codeload.github.com/nokia/etcd-cpp-api/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227817164,"owners_count":17824199,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-02T23:11:07.795Z","updated_at":"2024-12-02T23:11:08.467Z","avatar_url":"https://github.com/nokia.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"etcd-cpp-api is a C++ API for [etcd](https://github.com/coreos/etcd).\n\n## Requirements\n\n   * [C++ REST SDK](http://casablanca.codeplex.com/)\n   * Boost libraries\n   * [Catch](https://github.com/philsquared/Catch) for testing \n\n## generic notes\n\n```c++\n  etcd::Client etcd(\"http://127.0.0.1:4001\");\n  etcd::Response response = etcd.get(\"/test/key1\").get();\n  std::cout \u003c\u003c response.value().as_string();\n```\n\nMethods of the etcd client object are sending the corresponding HTTP requests and are returning\nimmediatelly with a ```pplx::task``` object. The task object is responsible for handling the\nreception of the HTTP response as well as parsing the JSON body of the response. All of this is done\nasynchronously in a background thread so you can continue your code to do other operations while the\ncurrent etcd operation is executing in the background or you can wait for the response with the\n```wait()``` or ```get()``` methods if a synchron behaviour is enough for your needs. These methods\nare blocking until the HTTP response arrives or some error situation happens. ```get()``` method\nalso returns the ```etcd::Response``` object.\n\n```c++\n  etcd::Client etcd(\"http://127.0.0.1:4001\");\n  pplx::task\u003cetcd::Response\u003e response_task = etcd.get(\"/test/key1\").get();\n  // ... do something else\n  etcd::Response response = response_task.get();\n  std::cout \u003c\u003c response.value().as_string();\n```\n\nThe pplx library allows to do even more. You can attach continuation ojects to the task if you do\nnot care about when the response is coming you only want to specify what to do then. This\ncan be achieved by calling the ```then``` method of the task, giving a funcion object parameter to\nit that can be used as a callback when the response is arrived and processed. The parameter of this\ncallback should be either a ```etcd::Response``` or a ```pplx::task\u003cetcd:Response\u003e```. You should\nprobably use a C++ lambda funcion here as a callback.\n\n```c++\n  etcd::Client etcd(\"http://127.0.0.1:4001\");\n  etcd.get(\"/test/key1\").then([](etcd::Response response)\n  {\n    std::cout \u003c\u003c response.value().as_string();\n  });\n\n  // ... your code can continue here without any delay\n```\n\nYour lambda function should have a parameter of type ```etcd::Response``` or\n```pplx::task\u003cetcd::Response\u003e```. In the latter case you can get the actual ```etcd::Response```\nobject with the ```get()``` function of the task. Calling get can raise exeptions so this is the way\nhow you can catch the errors generated by the REST interface. The ```get()``` call will not block in\nthis case since the respose has been already arrived (we are inside the callback).\n\n```c++\n  etcd::Client etcd(\"http://127.0.0.1:4001\");\n  etcd.get(\"/test/key1\").then([](pplx::task\u003cetcd::Response\u003e response_task)\n  {\n    try\n    {\n      etcd::Response response = response.task.get(); // can throw\n      std::cout \u003c\u003c response.value().as_string();\n    }\n    catch (std::ecxeption const \u0026 ex)\n    {\n      std::cerr \u003c\u003c ex.what();\n    }\n  });\n\n  // ... your code can continue here without any delay\n```\n\n## etcd operations\n\n### reading a value\n\nYou can read a value with the ```get``` method of the clinent instance. The only parameter is the\nkey to be read. If the read operation is successful then the value of the key can be acquired with\nthe ```value()``` method of the response. Success of the operation can be checked with the\n```is_ok()``` method of the response. In case of an error, the ```error_code()``` and\n```error_message()``` methods can be called for some further detail.\n\nPlease note that there can be two kind of error situations. There can be some problem with the\ncommunication between the client and the etcd server. In this case the ```get()``` method of the\nresponse task will throw an exception as shown above. If the communication is ok but there is some\nproblem with the content of the actual operation, like attemp to read a non-existing key then the\nresponse object will give you all the details. Let's see this in an example.\n\nThe Value object of the response also holds some extra information besides the string value of the\nkey. You can also get the index number of the creation and the last modification of this key with\nthe ```created_index()``` and the ```modofied_index()``` methods.\n\n```c++\n  etcd::Client etcd(\"http://127.0.0.1:4001\");\n  pplx::task\u003cetcd::Response\u003e response_task = etcd.get(\"/test/key1\");\n\n  try\n  {\n    etcd::Response response = response_task.get(); // can throw\n    if (response.is_ok())\n      std::cout \u003c\u003c \"successful read, value=\" \u003c\u003c response.value().as_string();\n    else\n      std::cout \u003c\u003c \"operation failed, details: \" \u003c\u003c response.error_message();\n  }\n  catch (std::ecxeption const \u0026 ex)\n  {\n    std::cerr \u003c\u003c \"communication problem, details: \" \u003c\u003c ex.what();\n  }\n```\n\n### modifying a value\n\nSetting the value of a key can be done with the ```set()``` method of the client. You simply pass\nthe key and the value as string parameters and you are done. The newly set value object can be asked\nfrom the response object exactly the same way as in case of the reading (with the ```value()```\nmethod). This way you can check for example the index value of your modification. You can also check\nwhat was the previous value that this operation was overwritten. You can do that with the\n```prev_value()``` method of the response object.\n\n```c++\n  etcd::Client etcd(\"http://127.0.0.1:4001\");\n  pplx::task\u003cetcd::Response\u003e response_task = etcd.set(\"/test/key1\", \"42\");\n\n  try\n  {\n    etcd::Response response = response_task.get();\n    if (response.is_ok())\n      std::cout \u003c\u003c \"The new value is successfully set, previous value was \"\n                \u003c\u003c response.prev_value().as_string();\n    else\n      std::cout \u003c\u003c \"operation failed, details: \" \u003c\u003c response.error_message();\n  }\n  catch (std::ecxeption const \u0026 ex)\n  {\n    std::cerr \u003c\u003c \"communication problem, details: \" \u003c\u003c ex.what();\n  }\n```\n\nThe set method creates a new leaf node if it weren't exists already or modifies an existing one.\nThere are a couple of other modification methods that are executing the write operation only upon\nsome specific conditions.\n\n   * ```add(key, value)``` creates a new value if it's key does not exists and returns a \"Key\n     already exists\" error otherwise (error code 105)\n   * ```modify(key, value)``` modifies an already existing value or returns a \"Key not found\" error\n     otherwise (error code 100)\n   * ```modify_if(key, value, old_value)``` modifies an already existing value but only if the previous\n     value equals with old_value. If the values does not match returns with \"Compare failed\" error\n     (code 101)\n   * ```modify_if(key, value, old_index)``` modifies an already existing value but only if the index of\n     the previous value equals with old_index. If the indices does not match returns with \"Compare\n     failed\" error (code 101)\n\n### deleting a value\n\nValues can be deleted with the ```rm``` method passing the key to be deleted as a parameter. The key\nshould point to an existing value. There are conditional variations for deletion too.\n\n   * ```rm_if(key, value, old_value)``` deletes an already existing value but only if the previous\n     value equals with old_value. If the values does not match returns with \"Compare failed\" error\n     (code 101)\n   * ```rm_if(key, value, old_index)``` deletes an already existing value but only if the index of\n     the previous value equals with old_index. If the indices does not match returns with \"Compare\n     failed\" error (code 101)\n\n### handling directory nodes\n\nDirectory nodes can be created, listed and deleted with the mkdir, ls and rmdir methods. For\ndirectory creation you just have to specify the full path of the new directory. Naturally the parent\nhas to exists and it has to be another directory.\n\n```c++\n  etcd::Client etcd(\"http://127.0.0.1:4001\");\n  etcd::Response resp = etcd.mkdir(\"/test\").get();\n```\n\nWhen you list a directory the response object's ```keys()``` and ```values()``` methods gives you a\nvector of directory entry names and values. The ```value()``` method with an integer parameter also\nreturns with the i-th element of the values vector, so ```response.values()[i] ==\nresponse.value(i)```. Entry names in the keys vector are relative to the parent directory. Elements\nin the values vector can be subdirectories or actual string values. To decide which one you can use\nthe ```is_dir()``` method.\n\n```c++\n  etcd::Client etcd(\"http://127.0.0.1:4001\");\n  etcd::Response resp = etcd.ls(\"/test/new_dir\").get();\n  for (int i = 0; i \u003c resp.keys().size(); ++i)\n  {\n    std::cout \u003c\u003c resp.keys(i);\n    if (resp.value(i).is_dir())\n      std::cout \u003c\u003c \"/\" \u003c\u003c std::endl;\n    else\n      std::cout \u003c\u003c \" = \" \u003c\u003c resp.value(i).as_string() \u003c\u003c std::endl;\n  }\n```\n\nDirectories can only be deleted if they are empty by default. If you want the delete recursively\nthen you have to pass a second ```true``` parameter to rmdir. This parameter defaults to ```false```.\n\n```c++\n  etcd::Client etcd(\"http://127.0.0.1:4001\");\n  etcd.rmdir(\"/test\", true).get();\n```\n\n### watching for changes\n\nWatching for a change is possible with the ```watch()``` operation of the client. The watch method\nsimply does not deliver a response object until the watched value changes in any way (modified or\ndeleted). When a change happens the returned result object will be the same as the result object of\nthe modification operation. So if the change is triggered by a value change, then\n```response.action()``` will return \"set\" or \"modify\", ```response.value()``` will hold the new\nvalue and ```response.prev_value()``` will contain the previous value. In case of a delete\n```response.action()``` will return \"delete\", ```response.value()``` will be empty and should not be\ncalled at all and ```response.prev_value()``` will contain the deleted value.\n\nIt is also possible to watch a whole directory subtree for changes with passing ```true``` to the second\n```recursive``` parameter of ```watch``` (this parameter defaults to ```false``` if omitted). In\nthis case the modified value object's ```key()``` method can be handy to determine what key is\nactually changed. Since this can be a long lasting operation you have to be prepared that is\nterminated by an exception and you have to restart the watch operation.\n\nThe watch also accepts an index parameter that specifies what is the first change we are interested\nabout. Since etcd stores the last couple of modifications with this feature you can ensure that your\nclient does not miss a single change.\n\nHere is an example how you can watch continuously for changes of one specific key.\n\n```c++\nvoid watch_for_changes()\n{\n  etcd.watch(\"/nodes\", index + 1, true).then([this](pplx::task\u003cetcd::Response\u003e resp_task)\n  {\n    try\n    {\n      etcd::Response resp = resp_task.get();\n      index = resp.index();\n      std::cout \u003c\u003c resp.action() \u003c\u003c \" \" \u003c\u003c resp.value().as_string() \u003c\u003c std::endl;\n    }\n    catch(...) {}\n    watch_for_changes();\n  });\n}\n```\n\nAt first glance it seems that ```watch_for_changes()``` calls itself on every value change but in\nfact it just sends the asynchron request, sets up a callback for the response and then returns.  The\ncallback is executed by some thread from the pplx library's thread pool and the callback (in this\ncase a small lambda function actually) will call ```watch_for_changes``` again from there.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnokia%2Fetcd-cpp-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnokia%2Fetcd-cpp-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnokia%2Fetcd-cpp-api/lists"}