diff --git a/README.md b/README.md index 0f02588a..f8e2ee4e 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,23 @@ If you set your `reporter_type` to `"etcd"` you should also set these parameters * `etcd_port`: port to connect to etcd. * `etcd_path`: the path where the registration will be created; nerve will create a node with a 30s ttl that is the registration as a child of this path, and then update it every few seconds +#### Serf Reporter #### + +You can use Nerve together with [Serf](https://www.serf.io/), which requires: + +* a Serf agent running on the same machine as Nerve +* Setting your `reporter_type` to `"serf"` +* `serf_config_dir`: a directory to place files in that is readable/writable by both Serf and Nerve. + * Nerve will place configuration files named `zzz_nerve__.json` under the directory specified with `serf_config_dir`. + * Serf will read these configurations and must be configured to run with `-config-dir=` + * Please note that it is up to you (through e.g. configuration management code) to ensure no invalid configuration files are left under `serf_config_dir` by wiping all `zzz_nerve*` files when renaming/removing services. (each nerve reporter creates/deletes files for services named ``, but renaming `` to `` on an existing machine could lead to stale configuration files remaining under `serf_config_dir`). +* Nerve must have permissions to reload the serf process. (can be specified by the `serf_reload_command` parameter - defaults to `/usr/bin/killall -HUP serf`) + * (You can for example install Serf and Nerve to run under the same user) + +Configuration management code and example Vagrant setup to use Serf together with Nerve/Synapse can be found here: + +* Ansible: https://github.com/AutomationWithAnsible/ansible-smartstack + ### Checks ### The core of nerve is a set of service checks. diff --git a/example/nerve_services/serf_service1.json b/example/nerve_services/serf_service1.json new file mode 100644 index 00000000..83450739 --- /dev/null +++ b/example/nerve_services/serf_service1.json @@ -0,0 +1,16 @@ +{ + "host": "1.2.3.4", + "port": 3000, + "reporter_type": "serf", + "check_interval": 2, + "weight": 2, + "checks": [ + { + "type": "http", + "uri": "/health", + "timeout": 0.2, + "rise": 3, + "fall": 2 + } + ] +} diff --git a/lib/nerve/reporter/serf.rb b/lib/nerve/reporter/serf.rb new file mode 100644 index 00000000..515ca80b --- /dev/null +++ b/lib/nerve/reporter/serf.rb @@ -0,0 +1,51 @@ +require 'nerve/reporter/base' + +class Nerve::Reporter + class Serf < Base + def initialize(service) + + # Set default parameters - the defaults are sane so nothing needed. + @config_dir = service['serf_config_dir'] || '/etc/serf' + @reload_command = service['serf_reload_command'] || '/usr/bin/killall -HUP serf' + @name = service['name'] + # please note that because of + # https://github.com/airbnb/smartstack-cookbook/blob/master/recipes/nerve.rb#L71 + # the name won't just be the name you gave but name_port. this allows a same + # service to be on multiple ports of a same machine. + + @data = parse_data(get_service_data(service)) + @config_file = File.join(@config_dir,"zzz_nerve_#{@name}.json") + File.unlink @config_file if File.exists? @config_file + end + + def start() + log.info "nerve: started to maintain the tag #{@name} with Serf" + end + + def report_up() + update_data(false) + end + + def report_down + File.unlink(@config_file) + reload_serf + end + + def update_data(new_data='') + @data = new_data if new_data + data = JSON.parse(@data) + tag = "#{data['host']}:#{data['port']}" + File.write(@config_file, JSON.generate({tags:{"smart:#{@name}" =>tag}})) + reload_serf + end + + def reload_serf() + system(@reload_command) + end + + def ping? + # for now return true. + return true + end + end +end diff --git a/spec/configuration_manager_spec.rb b/spec/configuration_manager_spec.rb index f218e9cc..fc50a4e2 100644 --- a/spec/configuration_manager_spec.rb +++ b/spec/configuration_manager_spec.rb @@ -24,7 +24,7 @@ expect(config_manager.config.keys()).to include('instance_id', 'services') expect(config_manager.config['services'].keys()).to contain_exactly( 'your_http_service', 'your_tcp_service', 'rabbitmq_service', - 'etcd_service1', 'zookeeper_service1' + 'etcd_service1', 'serf_service1', 'zookeeper_service1' ) end end