{"id":13651720,"url":"https://github.com/WeAreFarmGeek/diplomat","last_synced_at":"2025-04-22T22:31:58.163Z","repository":{"id":16185777,"uuid":"18932342","full_name":"WeAreFarmGeek/diplomat","owner":"WeAreFarmGeek","description":"A HTTP Ruby API for Consul","archived":false,"fork":false,"pushed_at":"2022-11-11T10:38:54.000Z","size":565,"stargazers_count":369,"open_issues_count":12,"forks_count":115,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-08T09:12:41.083Z","etag":null,"topics":["api","api-client","cluster","consul","consul-cluster","data-center","diplomat","distributed","distributed-computing","distributed-systems","key-value","lock","rails","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/WeAreFarmGeek.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-04-19T03:22:12.000Z","updated_at":"2025-01-10T17:17:10.000Z","dependencies_parsed_at":"2023-01-13T18:44:25.912Z","dependency_job_id":null,"html_url":"https://github.com/WeAreFarmGeek/diplomat","commit_stats":null,"previous_names":["johnhamelink/diplomat"],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WeAreFarmGeek%2Fdiplomat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WeAreFarmGeek%2Fdiplomat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WeAreFarmGeek%2Fdiplomat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WeAreFarmGeek%2Fdiplomat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WeAreFarmGeek","download_url":"https://codeload.github.com/WeAreFarmGeek/diplomat/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250334022,"owners_count":21413494,"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":["api","api-client","cluster","consul","consul-cluster","data-center","diplomat","distributed","distributed-computing","distributed-systems","key-value","lock","rails","ruby"],"created_at":"2024-08-02T02:00:51.802Z","updated_at":"2025-04-22T22:31:57.898Z","avatar_url":"https://github.com/WeAreFarmGeek.png","language":"Ruby","funding_links":[],"categories":["Projects"],"sub_categories":["Programming Language Clients"],"readme":"# Diplomat\n[![Build Status](https://github.com/WeAreFarmGeek/diplomat/workflows/Ruby/badge.svg?branch=master)](https://github.com/WeAreFarmGeek/diplomat/actions?query=branch%3Amaster)\n[![Gem Version](https://badge.fury.io/rb/diplomat.svg)](https://rubygems.org/gems/diplomat) [![Gem](https://img.shields.io/gem/dt/diplomat.svg)](https://rubygems.org/gems/diplomat) [![Code Climate](https://codeclimate.com/github/johnhamelink/diplomat.svg)](https://codeclimate.com/github/WeAreFarmGeek/diplomat) [![Inline docs](http://inch-ci.org/github/wearefarmgeek/diplomat.svg?branch=master)](http://inch-ci.org/github/wearefarmgeek/diplomat)\n### A HTTP Ruby API for [Consul](http://www.consul.io/)\n\n![Diplomacy Board Game](http://i.imgur.com/Nkuy4b7.jpg)\n\n\n## FAQ\n\n#### What's Diplomat for?\n\nDiplomat allows any ruby application to interact with [Consul's](http://www.consul.io/) distributed key value store, and also receive information about services currently available in the Consul cluster.\n\n#### Does it work in rails?\n\nYup! In fact, we're using it in all of our rails production apps instead of any previous case where it'd be right to use environment variables according to [12Factor configuration principals](http://12factor.net/config). This gives us the ability to scale up without making any changes to the actual project codebase, and to move applications around the cluster with ease.\n\nHere's what a production database.yml file might look like:\n\n```erb\n\u003c% if Rails.env.production? %\u003e\nproduction:\n  adapter:            postgresql\n  encoding:           unicode\n  host:               \u003c%= Diplomat::Service.get('postgres').Address %\u003e\n  database:           \u003c%= Diplomat::Kv.get('project/db/name') %\u003e\n  pool:               5\n  username:           \u003c%= Diplomat::Kv.get('project/db/user') %\u003e\n  password:           \u003c%= Diplomat::Kv.get('project/db/pass') %\u003e\n  port:               \u003c%= Diplomat::Service.get('postgres').ServicePort %\u003e\n\u003c% end %\u003e\n```\n\n#### Why would I use Consul over ZooKeeper, Doozerd, etcd, Nagios, Sensu, SmartStack, SkyDNS, Chef, Puppet, Ansible, etc?\n\n[Read up what makes Consul different here](http://www.consul.io/intro/vs/index.html)\n\n#### How do I install Consul?\n\n[See here](http://www.consul.io/intro/). I managed to roll it out on my production machines with the help of [Ansible](http://www.ansible.com/) in one working day.\n\n### Which versions of Ruby does Diplomat support? Where did my ruby 1.9 compatibility go?\n\nCheck out [GitHub Actions](https://github.com/WeAreFarmGeek/diplomat/blob/master/.github/workflows/ruby.yml) to see which versions of ruby we currently test when we're making builds.\n\nWe've dropped ruby 1.9 support. You can still depend on Diplomat by directly using the `ruby-1.9-compatible` branch on github, although be advised it's not actively maintained anymore.\n\n### ERB templating\n\nIt is possible to inject diplomat data into `.erb` files (such as in chef), but you could also have a look at\n[consul-templaterb](https://github.com/criteo/consul-templaterb/) that is highly optimized for ERB templating\nwith very hi parallelism and good optimized performance for large clusters.\n\n## Usage\n\n[The most up to date place to read about the API is here.](http://rubydoc.info/github/WeAreFarmGeek/diplomat)\n\nHere's a few examples of how diplomat works:\n\n### Key Values\n\n#### Setting\n\nSetting the value of a key is easy as pie:\n\n```ruby\nfoo = Diplomat::Kv.put('foo', 'bar')\n# =\u003e \"bar\"\n```\n\n#### Getting\n\nGetting the value of a key is just as simple:\n\n```ruby\nfoo = Diplomat::Kv.get('foo')\n# =\u003e \"bar\"\n```\n\nOr retrieve a value from another datacenter:\n\n```ruby\nfoo = Diplomat::Kv.get('foo', :dc =\u003e 'dc-west')\n# =\u003e \"baz\"\n```\n\nYou can also retrieve values recursively:\n\n```ruby\nDiplomat::Kv.put('foo/a', 'lorem')\nDiplomat::Kv.put('foo/b', 'ipsum')\nDiplomat::Kv.put('foo/c', 'dolor')\n\nDiplomat::Kv.get('foo/', recurse: true)\n# =\u003e [{:key=\u003e\"foo/a\", :value=\u003e\"lorem\"}, {:key=\u003e\"foo/b\", :value=\u003e\"ipsum\"}, {:key=\u003e\"foo/c\", :value=\u003e\"dolor\"}]\n```\n\nYou can also use `get_all` to retrieve values recursively with a consistent return type:\n\n```ruby\nDiplomat::Kv.put('foo/a', 'lorem')\nDiplomat::Kv.put('foo/b', 'ipsum')\nDiplomat::Kv.put('foo/c', 'dolor')\n\nDiplomat::Kv.get('foo/', recurse: true)\n# =\u003e [{:key=\u003e\"foo/a\", :value=\u003e\"lorem\"}, {:key=\u003e\"foo/b\", :value=\u003e\"ipsum\"}, {:key=\u003e\"foo/c\", :value=\u003e\"dolor\"}]\nDiplomat::Kv.get_all('foo/')\n# =\u003e [{:key=\u003e\"foo/a\", :value=\u003e\"lorem\"}, {:key=\u003e\"foo/b\", :value=\u003e\"ipsum\"}, {:key=\u003e\"foo/c\", :value=\u003e\"dolor\"}]\n\nDiplomat::Kv.put('bar/a', 'lorem')\n\nDiplomat::Kv.get('bar/', recurse: true)\n# =\u003e \"lorem\"\nDiplomat::Kv.get_all('bar/')\n# =\u003e [{:key=\u003e\"bar/a\", :value=\u003e\"lorem\"}]\n```\n\n\nOr list all available keys:\n\n```ruby\nDiplomat::Kv.get('/', :keys =\u003e true) # =\u003e ['foo/a', 'foo/b']\n```\nYou can convert the consul data to a ruby hash\n\n```ruby\nDiplomat::Kv.put('foo/a', 'lorem')\nDiplomat::Kv.put('foo/b', 'ipsum')\nDiplomat::Kv.put('foo/c', 'dolor')\n\nDiplomat::Kv.get('foo/', recurse: true, convert_to_hash: true)\n# =\u003e {\"foo\"=\u003e{\"a\"=\u003e\"lorem\", \"b\"=\u003e\"ipsum\", \"c\"=\u003e\"dolor\"}}\n```\n\n### Nodes\n\n#### Getting\n\nLook up a node:\n\n```ruby\nfoo_service = Diplomat::Node.get('foo')\n# =\u003e {\"Node\"=\u003e{\"Node\"=\u003e\"foobar\", \"Address\"=\u003e\"10.1.10.12\"}, \"Services\"=\u003e{\"consul\"=\u003e{\"ID\"=\u003e\"consul\", \"Service\"=\u003e\"consul\", \"Tags\"=\u003enil, \"Port\"=\u003e8300}, \"redis\"=\u003e{\"ID\"=\u003e\"redis\", \"Service\"=\u003e\"redis\", \"Tags\"=\u003e[\"v1\"], \"Port\"=\u003e8000}}}\n```\n\nGet all nodes:\n\n```ruby\nnodes = Diplomat::Node.get_all\n# =\u003e [#\u003cOpenStruct Address=\"10.1.10.12\", Node=\"foo\"\u003e, #\u003cOpenStruct Address=\"10.1.10.13\", Node=\"bar\"\u003e]\n```\n\nGet all nodes for a particular datacenter\n\n```ruby\nnodes = Diplomat::Node.get_all({ :dc =\u003e 'My_Datacenter' })\n# =\u003e [#\u003cOpenStruct Address=\"10.1.10.12\", Node=\"foo\"\u003e, #\u003cOpenStruct Address=\"10.1.10.13\", Node=\"bar\"\u003e]\n```\n\nRegister a node:\n\n```ruby\nDiplomat::Node.register({ :Node =\u003e \"app1\", :Address =\u003e \"10.0.0.2\" })\n# =\u003e true\n```\n\nDe-register a node:\n\n```ruby\nDiplomat::Node.deregister({ :Node =\u003e \"app1\", :Address =\u003e \"10.0.0.2\" })\n# =\u003e true\n```\n\n### Services\n\n#### Getting\n\nLooking up a service is easy as pie:\n\n```ruby\nfoo_service = Diplomat::Service.get('foo')\n# =\u003e #\u003cOpenStruct Node=\"hotel\", Address=\"1.2.3.4\", ServiceID=\"hotel_foo\", ServiceName=\"foo\", ServiceTags=[\"foo\"], ServicePort=5432\u003e\n```\nOr if you have multiple nodes per service:\n\n```ruby\nfoo_service = Diplomat::Service.get('foo', :all)\n# =\u003e [#\u003cOpenStruct Node=\"hotel\", Address=\"1.2.3.4\", ServiceID=\"hotel_foo\", ServiceName=\"foo\", ServiceTags=[\"foo\"], ServicePort=5432\u003e,#\u003cOpenStruct Node=\"indigo\", Address=\"1.2.3.5\", ServiceID=\"indigo_foo\", ServiceName=\"foo\", ServiceTags=[\"foo\"], ServicePort=5432\u003e]\n```\n\nOr if you want to find services for a particular datacenter\n\n```ruby\nfoo_service = Diplomat::Service.get('foo', :all, { :dc =\u003e 'My_Datacenter'})\n# =\u003e [#\u003cOpenStruct Node=\"hotel\", Address=\"1.2.3.4\", ServiceID=\"hotel_foo\", ServiceName=\"foo\", ServiceTags=[\"foo\"], ServicePort=5432\u003e,#\u003cOpenStruct Node=\"indigo\", Address=\"1.2.3.5\", ServiceID=\"indigo_foo\", ServiceName=\"foo\", ServiceTags=[\"foo\"], ServicePort=5432\u003e]\n```\n\nOr if you want to filter services\n\n```ruby\nfoo_service = Diplomat::Service.get('foo', :all, { :filter =\u003e 'ServiceID == \"indigo_foo\"'})\n# =\u003e [#\u003cOpenStruct Node=\"indigo\", Address=\"1.2.3.5\", ServiceID=\"indigo_foo\", ServiceName=\"foo\", ServiceTags=[\"foo\"], ServicePort=5432\u003e]\n```\n\nIf you wish to list all the services on consul:\n\n```ruby\nservices = Diplomat::Service.get_all\n# =\u003e #\u003cOpenStruct consul=[], foo=[], bar=[]\u003e\n```\n\nIf you wish to list all the services for a specific datacenter:\n\n```ruby\nservices = Diplomat::Service.get_all({ :dc =\u003e 'My_Datacenter' })\n# =\u003e #\u003cOpenStruct consul=[], foo=[], bar=[]\u003e\n```\n\n### Datacenters\n\nGetting a list of datacenters is quite simple and gives you the option to extract all services out of\nall accessible datacenters if you need to.\n\n```ruby\ndatacenters = Diplomat::Datacenter.get()\n# =\u003e [\"DC1\", \"DC2\"]\n```\n\n### Sessions\n\nCreating a session:\n\n```ruby\nsessionid = Diplomat::Session.create({:Node =\u003e \"server1\", :Name =\u003e \"my-lock\"})\n# =\u003e \"fc5ca01a-c317-39ea-05e8-221da00d3a12\"\n```\nOr destroying a session:\n\n```ruby\nDiplomat::Session.destroy(\"fc5ca01a-c317-39ea-05e8-221da00d3a12\")\n```\n\nRenew a session:\n```ruby\nDiplomat::Session.renew(sessionid)\n```\n\nList sessions:\n```ruby\nDiplomat::Session.list.each {|session| puts \"#{session[\"ID\"]} #{session[\"Name\"]}\"}\n```\n\n### Locks\n\nAcquire a lock:\n\n```ruby\nsessionid = Diplomat::Session.create({:Node =\u003e \"server1\", :Name =\u003e \"my-lock\"})\nlock_acquired = Diplomat::Lock.acquire(\"/key/to/lock\", sessionid)\n# =\u003e true\n```\nOr wait for a lock to be acquired:\n\n```ruby\nsessionid = Diplomat::Session.create({:hostname =\u003e \"server1\", :ipaddress =\u003e \"4.4.4.4\"})\nlock_acquired = Diplomat::Lock.wait_to_acquire(\"/key/to/lock\", sessionid)\n```\n\nRelease a lock:\n\n```ruby\nDiplomat::Lock.release(\"/key/to/lock\", sessionid )\n```\n\n### Events\n\nFire an event:\n\n```ruby\nDiplomat::Event.fire('do_something', 'payload')\n```\n\nList all events with a certain name received by the local agent:\n\n```ruby\nDiplomat::Event.get_all('do_something')\n```\n\nGet the latest event with a certain name received by the local agent:\n\n```ruby\nDiplomat::Event.get('do_something')\n```\n\nIterate through the events with a certain name received by the local agent:\n\n```ruby\nevents = Enumerator.new do |y|\n  ret = {token: :first}\n  while ret = begin Diplomat::Event.get('do_something', ret[:token], :reject) rescue nil end\n    y.yield(ret[:value])\n  end\nend\n\nevents.each{ |e| puts e }\n```\n\n### Status\n\nReturns information about the status of the Consul cluster.\n\nGet the raft leader for the datacenter in which the local consul agent is running\n\n```ruby\nDiplomat::Status.leader()\n```\n\nGet an array of Raft peers for the datacenter in which the agent is running\n\n```ruby\nDiplomat::Status.peers()\n```\n\n### Autopilot\n\nReturns information about the autopilot configuration of the Consul cluster\n\nGet the current autopilot configuration\n\n```ruby\nDiplomat::Autopilot.get_configuration()\n```\n\nGet the health status from autopilot\n\n```ruby\nDiplomat::Autopilot.get_health()\n```\n\n### Health\n\nRetrieve health of a node\n\n```ruby\nDiplomat::Health.node('fooNode', :dc =\u003e 'abc')\n```\n\nRetrieve health of a given check\n\n```ruby\nDiplomat::Health.checks('fooCheck', :dc =\u003e 'abc')\n```\n\nRetrieve health of a given service\n\n```ruby\nDiplomat::Health.service('fooService', :dc =\u003e 'abc')\n```\n\nRetrieve a list of anything that correspond to the state (\"any\", \"passing\", \"warning\", or \"critical\")\nYou can use filters too !\n\n```ruby\nDiplomat::Health.state(\"critical\", {:dc =\u003e 'abc', :filter =\u003e 'Node==foo'})\n```\n\nYou also have some convenience method (`any`, `passing`, `warning`, `critical`)\nThat can be filtered\n\n```ruby\nDiplomat::Health.critical({:dc =\u003e 'abc', :filter =\u003e 'ServiceName==foo'})\n```\n\n### Maintenance mode\n\nEnable maintenance mode on a host, with optional reason and DC (requires access to local agent)\n\n```ruby\nDiplomat::Maintenance.enable(true, 'doing stuff', :dc =\u003e 'abc')\n```\n\nDetermine if a host has maintenance mode enabled\n\n```ruby\nDiplomat::Maintenance.enabled('foobar')\n# =\u003e { :enabled =\u003e true, :reason =\u003e 'doing stuff' }\n```\n\n### Custom configuration\n\nYou can create a custom configuration using the following syntax:\n\n```ruby\nDiplomat.configure do |config|\n  # Set up a custom Consul URL\n  config.url = \"http://localhost:8888\"\n  # Set up a custom Faraday Middleware\n  config.middleware = MyCustomMiddleware\n  # Set extra Faraday configuration options and custom access token (ACL)\n  config.options = {ssl: {version: :TLSv1_2}, headers: {\"X-Consul-Token\" =\u003e \"xxxxxxxx-yyyy-zzzz-1111-222222222222\"}}\nend\n```\n\nThis is traditionally kept inside the `config/initializers` directory if you're using rails. The middleware allows you to customise what happens when faraday sends and receives data. This can be useful if you want to instrument your use of diplomat, for example. You can read more about Faraday's custom middleware [here](http://stackoverflow.com/a/20973008).\n\nAlternatively, configuration settings can be overriden at each method call allowing for instance to address different consul agents, with some other token.\n\n```ruby\nDiplomat::Service.get('foo', { http_addr: 'http://consu01:8500' })\nDiplomat::Service.get('foo', { http_addr: 'http://consu02:8500' })\nDiplomat::Kv.put('key/path', 'value', { http_addr: 'http://localhost:8500', dc: 'dc1', token: '111-222-333-444-555' })\n```\n\nMost common options are:\n* dc: target datacenter\n* token: identity used to perform the corresponding action\n* http_addr: to target a remote consul node\n* stale: use consistency mode that allows any server to service the read regardless of whether it is the leader\n\n### Todo\n\n-  [ ] Updating Docs with latest changes\n-  [ ] Using custom objects for response objects (instead of openStruct)\n-  [ ] PUTing and DELETEing services\n-  [x] Custom SSL Cert Middleware for faraday\n-  [x] Allowing the custom configuration of the consul url to connect to\n-  [x] Deleting Keys\n-  [x] Listing available services\n-  [x] Health\n-  [x] Members\n-  [x] Status\n-  [x] Datacenter support for services\n-  [x] Ruby 1.8 support\n-  [x] Events\n\n\n## Enjoy!\n\n![Photo Copyright \"merlinmann\" https://www.flickr.com/photos/merlin/. All rights reserved.](http://i.imgur.com/3mBwzR9.jpg)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWeAreFarmGeek%2Fdiplomat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FWeAreFarmGeek%2Fdiplomat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FWeAreFarmGeek%2Fdiplomat/lists"}