nerve 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 1.9.3
5
+
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,14 @@
1
+ # Contributing Guidelines #
2
+
3
+ Thanks for contributing to SmartStack!
4
+ If you're opening a new PR, please ask for a merge into our `pull_requests` branch -- *not* `master`.
5
+ This will allow us avoid a back-and-forth by quickly accepting your PR and then making minor changes or doing testing before merging into `master`.
6
+
7
+ ## Writing Checks ##
8
+
9
+ We welcome additional service checks into the core of nerve.
10
+ However, your checks must follow a few guidelines or they will not be accepted:
11
+
12
+ * be sure to respect timeouts; checks that do not time-out will not be accepted
13
+ * do NOT shell out; this becomes very expensive when done frequently
14
+ * use well-tested, stable, core libraries whenever possible
data/Gemfile.lock CHANGED
@@ -1,23 +1,33 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nerve (0.3.0)
5
- bunny (= 1.0.0.rc2)
4
+ nerve (0.5.0)
5
+ bunny (= 1.1.0)
6
6
  zk (~> 1.9.2)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- amq-protocol (1.8.0)
12
- bunny (1.0.0.rc2)
13
- amq-protocol (>= 1.8.0)
11
+ amq-protocol (1.9.2)
12
+ bunny (1.1.0)
13
+ amq-protocol (>= 1.9.2)
14
+ diff-lcs (1.2.5)
14
15
  little-plugger (1.1.3)
15
16
  logging (1.7.2)
16
17
  little-plugger (>= 1.1.3)
17
- zk (1.9.2)
18
+ rake (10.1.1)
19
+ rspec (2.14.1)
20
+ rspec-core (~> 2.14.0)
21
+ rspec-expectations (~> 2.14.0)
22
+ rspec-mocks (~> 2.14.0)
23
+ rspec-core (2.14.7)
24
+ rspec-expectations (2.14.4)
25
+ diff-lcs (>= 1.1.3, < 2.0)
26
+ rspec-mocks (2.14.4)
27
+ zk (1.9.3)
18
28
  logging (~> 1.7.2)
19
29
  zookeeper (~> 1.4.0)
20
- zookeeper (1.4.7)
30
+ zookeeper (1.4.8)
21
31
 
22
32
  PLATFORMS
23
33
  java
@@ -25,3 +35,5 @@ PLATFORMS
25
35
 
26
36
  DEPENDENCIES
27
37
  nerve!
38
+ rake
39
+ rspec
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://travis-ci.org/airbnb/nerve.png?branch=master)](https://travis-ci.org/airbnb/nerve)
2
+
1
3
  # Nerve
2
4
 
3
5
  Nerve is a utility for tracking the status of machines and services.
@@ -8,7 +10,7 @@ The combination of Nerve and [Synapse](https://github.com/airbnb/synapse) make s
8
10
  ## Motivation ##
9
11
 
10
12
  We already use [Synapse](https://github.com/airbnb/synapse) to discover remote services.
11
- However, those services needed boilerplate code to register themselves in [Zookeeper](zookeeper.apache.org/).
13
+ However, those services needed boilerplate code to register themselves in [Zookeeper](http://zookeeper.apache.org/).
12
14
  Nerve simplifies underlying services, enables code reuse, and allows us to create a more composable system.
13
15
  It does so by factoring out the boilerplate into it's own application, which independenly handles monitoring and reporting.
14
16
 
@@ -36,7 +38,7 @@ It is usually called `nerve.conf.json`.
36
38
  An example config file is available in `example/nerve.conf.json`.
37
39
  The config file is composed of two main sections:
38
40
 
39
- * `instance_id`: the name under which your services will be registered in zookeeper
41
+ * `instance_id`: the name nerve will submit when registering services; makes debugging easier
40
42
  * `services`: the hash (from service name to config) of the services nerve will be monitoring
41
43
  * `service_conf_dir`: path to a directory in which each json file will be interpreted as a service with the basename of the file minus the .json extension
42
44
 
@@ -46,13 +48,19 @@ Each service that nerve will be monitoring is specified in the `services` hash.
46
48
  The key is the name of the service, and the value is a configuration hash telling nerve how to monitor the service.
47
49
  The configuration contains the following options:
48
50
 
49
- * `port`: the default port for service checks; nerve will submit this the address `instance_id:port` to Zookeeper
50
- * `host`: the default host on which to make service checks; you should make this your *public* ip if you want to make sure your service is publically accessible
51
- * `zk_hosts`: a list of the zookeeper hosts comprising the [ensemble](https://zookeeper.apache.org/doc/r3.1.2/zookeeperAdmin.html#sc_zkMulitServerSetup) that nerve will submit registration to
52
- * `zk_path`: the path (or [znode](https://zookeeper.apache.org/doc/r3.1.2/zookeeperProgrammers.html#sc_zkDataModel_znodes)) where the registration will be created; nerve will create the [ephemeral node](https://zookeeper.apache.org/doc/r3.1.2/zookeeperProgrammers.html#Ephemeral+Nodes) that is the registration as a child of this path
51
+ * `host`: the default host on which to make service checks; you should make this your *public* ip to ensure your service is publically accessible
52
+ * `port`: the default port for service checks; nerve will report the `host`:`port` combo via your chosen reporter
53
+ * `reporter_type`: the mechanism used to report up/down information; depending on the reporter you choose, additional parameters may be required. Defaults to `zookeeper`
53
54
  * `check_interval`: the frequency with which service checks will be initiated; defaults to `500ms`
54
55
  * `checks`: a list of checks that nerve will perform; if all of the pass, the service will be registered; otherwise, it will be un-registered
55
56
 
57
+ #### Zookeeper Reporter ####
58
+
59
+ If you set your `reporter_type` to `"zookeeper"` you should also set these parameters:
60
+
61
+ * `zk_hosts`: a list of the zookeeper hosts comprising the [ensemble](https://zookeeper.apache.org/doc/r3.1.2/zookeeperAdmin.html#sc_zkMulitServerSetup) that nerve will submit registration to
62
+ * `zk_path`: the path (or [znode](https://zookeeper.apache.org/doc/r3.1.2/zookeeperProgrammers.html#sc_zkDataModel_znodes)) where the registration will be created; nerve will create the [ephemeral node](https://zookeeper.apache.org/doc/r3.1.2/zookeeperProgrammers.html#Ephemeral+Nodes) that is the registration as a child of this path
63
+
56
64
  ### Checks ###
57
65
 
58
66
  The core of nerve is a set of service checks.
data/Rakefile CHANGED
@@ -1 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :test => :spec
7
+ task :default => :spec
data/bin/nerve CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'json'
3
+ require 'yaml'
4
4
  require 'optparse'
5
5
 
6
6
  require 'nerve'
@@ -20,7 +20,13 @@ EOB
20
20
  options[:config] = key
21
21
  end
22
22
 
23
- opts.on( '-h', '--help', 'Display this screen' ) do
23
+ options[:instance_id] = ENV['NERVE_INSTANCE_ID']
24
+ opts.on('-i instance_id','--instance_id instance_id', String,
25
+ 'reported as `name` to ZK; overrides instance id from config file') do |key,value|
26
+ options[:instance_id] = key
27
+ end
28
+
29
+ opts.on('-h', '--help', 'Display this screen') do
24
30
  puts opts
25
31
  exit
26
32
  end
@@ -34,15 +40,15 @@ optparse.parse!
34
40
  def parseconfig(filename)
35
41
  # parse synapse config file
36
42
  begin
37
- c = JSON::parse(File.read(filename))
43
+ c = YAML::parse(File.read(filename))
38
44
  rescue Errno::ENOENT => e
39
45
  raise ArgumentError, "config file does not exist:\n#{e.inspect}"
40
46
  rescue Errno::EACCES => e
41
47
  raise ArgumentError, "could not open config file:\n#{e.inspect}"
42
- rescue JSON::ParserError => e
43
- raise "config file #{filename} is not json:\n#{e.inspect}"
48
+ rescue YAML::ParseError => e
49
+ raise "config file #{filename} is not yaml:\n#{e.inspect}"
44
50
  end
45
- return c
51
+ return c.to_ruby
46
52
  end
47
53
 
48
54
  config = parseconfig(options[:config])
@@ -53,8 +59,12 @@ if config.has_key?('service_conf_dir')
53
59
  if ! Dir.exists?(cdir) then
54
60
  raise "service conf dir does not exist:#{cdir}"
55
61
  end
56
- cfiles = Dir.glob(File.join(cdir, '*.json'))
57
- cfiles.each { |x| config['services'][File.basename(x[/(.*)\.json$/, 1])] = parseconfig(x) }
62
+ cfiles = Dir.glob(File.join(cdir, '*.{yaml,json}'))
63
+ cfiles.each { |x| config['services'][File.basename(x[/(.*)\.(yaml|json)$/, 1])] = parseconfig(x) }
64
+ end
65
+
66
+ if options[:instance_id] && !options[:instance_id].empty?
67
+ config['instance_id'] = options[:instance_id]
58
68
  end
59
69
 
60
70
  # create nerve object
@@ -3,8 +3,9 @@
3
3
  "service_conf_dir": "example/nerve_services",
4
4
  "services": {
5
5
  "your_http_service": {
6
+ "host": "1.2.3.4",
6
7
  "port": 3000,
7
- "host": "127.0.0.1",
8
+ "reporter_type": "zookeeper",
8
9
  "zk_hosts": ["localhost:2181"],
9
10
  "zk_path": "/nerve/services/your_http_service/services",
10
11
  "check_interval": 2,
@@ -19,8 +20,9 @@
19
20
  ]
20
21
  },
21
22
  "your_tcp_service": {
23
+ "host": "1.2.3.4",
22
24
  "port": 6379,
23
- "host": "127.0.0.1",
25
+ "reporter_type": "zookeeper",
24
26
  "zk_hosts": ["localhost:2181"],
25
27
  "zk_path": "/nerve/services/your_tcp_service/services",
26
28
  "check_interval": 2,
@@ -34,8 +36,9 @@
34
36
  ]
35
37
  },
36
38
  "rabbitmq_service": {
39
+ "host": "1.2.3.4",
37
40
  "port": 5672,
38
- "host": "127.0.0.1",
41
+ "reporter_type": "zookeeper",
39
42
  "zk_hosts": ["localhost:2181"],
40
43
  "zk_path": "/nerve/services/your_rabbitmq_service/services",
41
44
  "check_interval": 2,
@@ -1,6 +1,7 @@
1
1
  {
2
+ "host": "1.2.3.4",
2
3
  "port": 3000,
3
- "host": "127.0.0.1",
4
+ "reporter_type": "zookeeper",
4
5
  "zk_hosts": ["localhost:2181"],
5
6
  "zk_path": "/nerve/services/your_http_service/services",
6
7
  "check_interval": 2,
@@ -0,0 +1,26 @@
1
+
2
+ class Nerve::Reporter
3
+ class Base
4
+ include Nerve::Utils
5
+ include Nerve::Logging
6
+
7
+ def initialize(opts)
8
+ end
9
+
10
+ def start
11
+ end
12
+
13
+ def report_up
14
+ end
15
+
16
+ def report_down
17
+ end
18
+
19
+ def update_data(new_data='')
20
+ end
21
+
22
+ def ping?
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,70 @@
1
+ require 'nerve/reporter/base'
2
+ require 'zk'
3
+
4
+ class Nerve::Reporter
5
+ class Zookeeper < Base
6
+ def initalize(service)
7
+ %w{zk_hosts zk_path instance_id host port}.each do |required|
8
+ raise ArgumentError, "missing required argument #{required} for new service watcher" unless service[required]
9
+ end
10
+ @path = service['zk_hosts'].shuffle.join(',') + opts['zk_path']
11
+ @data = parse_data({'host' => service['host'], 'port' => service['port'], 'name' => service['instance_id']})
12
+
13
+ @key = "/#{service['instance_id']}_"
14
+ @full_key = nil
15
+ end
16
+
17
+ def start()
18
+ log.info "nerve: waiting to connect to zookeeper at #{@path}"
19
+ @zk = ZK.new(@path)
20
+
21
+ log.info "nerve: successfully created zk connection to #{@path}"
22
+ end
23
+
24
+ def report_up()
25
+ zk_save
26
+ end
27
+
28
+ def report_down
29
+ zk_delete
30
+ end
31
+
32
+ def update_data(new_data='')
33
+ @data = parse_data(new_data)
34
+ zk_save
35
+ end
36
+
37
+ def ping?
38
+ return @zk.ping?
39
+ end
40
+
41
+ private
42
+
43
+ def zk_delete
44
+ if @full_key
45
+ @zk.delete(@full_key, :ignore => :no_node)
46
+ @full_key = nil
47
+ end
48
+ end
49
+
50
+ def zk_create
51
+ @full_key = @zk.create(@key, :data => @data, :mode => :ephemeral_sequential)
52
+ end
53
+
54
+ def zk_save
55
+ return zk_create unless @full_key
56
+
57
+ begin
58
+ @zk.set(@full_key, @data)
59
+ rescue ZK::Exceptions::NoNode
60
+ zk_create
61
+ end
62
+ end
63
+
64
+ def parse_data(data)
65
+ return data if data.class == String
66
+ return data.to_json
67
+ end
68
+ end
69
+ end
70
+
@@ -1,64 +1,15 @@
1
- require 'zk'
2
1
 
3
2
  module Nerve
4
3
  class Reporter
5
- include Utils
6
- include Logging
7
-
8
- def initialize(opts)
9
- %w{hosts path key}.each do |required|
10
- raise ArgumentError, "you need to specify required argument #{required}" unless opts[required]
11
- end
12
-
13
- @path = opts['hosts'].shuffle.join(',') + opts['path']
14
- @data = parse_data(opts['data'] || '')
15
- @key = opts['key']
16
- @key.insert(0,'/') unless @key[0] == '/'
17
- end
18
-
19
- def start()
20
- log.info "nerve: waiting to connect to zookeeper at #{@path}"
21
- @zk = ZK.new(@path)
22
-
23
- log.info "nerve: successfully created zk connection to #{@path}"
24
- end
25
-
26
- def report_up()
27
- zk_save
28
- end
29
-
30
- def report_down
31
- zk_delete
32
- end
33
-
34
- def update_data(new_data='')
35
- @data = parse_data(new_data)
36
- zk_save
37
- end
38
-
39
- def ping?
40
- return @zk.ping?
41
- end
42
-
43
- private
44
-
45
- def zk_delete
46
- @zk.delete(@key, :ignore => :no_node)
47
- end
48
-
49
- def zk_save
50
- log.debug "nerve: writing data #{@data.class} to zk at #{@key} with #{@data.inspect}"
51
- begin
52
- @zk.set(@key,@data)
53
- rescue ZK::Exceptions::NoNode => e
54
- @zk.create(@key,:data => @data, :mode => :ephemeral)
4
+ def self.new_from_service(service)
5
+ type = service['reporter_type'] || 'zookeeper'
6
+ reporter = begin
7
+ require "nerve/reporter/#{type.downcase}"
8
+ self.const_get(type.downcase.capitalize)
9
+ rescue Exception => e
10
+ raise ArgumentError, "specified a reporter_type of #{type}, which could not be found: #{e}"
55
11
  end
12
+ reporter.new(service)
56
13
  end
57
-
58
- def parse_data(data)
59
- return data if data.class == String
60
- return data.to_json
61
- end
62
-
63
14
  end
64
15
  end
@@ -11,19 +11,14 @@ module Nerve
11
11
  log.debug "nerve: creating service watcher object"
12
12
 
13
13
  # check that we have all of the required arguments
14
- %w{name instance_id host port zk_hosts zk_path}.each do |required|
14
+ %w{name instance_id host port}.each do |required|
15
15
  raise ArgumentError, "missing required argument #{required} for new service watcher" unless service[required]
16
16
  end
17
17
 
18
18
  @name = service['name']
19
19
 
20
20
  # configure the reporter, which we use for talking to zookeeper
21
- @reporter = Reporter.new({
22
- 'hosts' => service['zk_hosts'],
23
- 'path' => service['zk_path'],
24
- 'key' => "#{service['instance_id']}_#{@name}",
25
- 'data' => {'host' => service['host'], 'port' => service['port']},
26
- })
21
+ @reporter = Reporter.new_from_service(service)
27
22
 
28
23
  # instantiate the checks for this service
29
24
  @service_checks = []
@@ -52,9 +47,7 @@ module Nerve
52
47
  def run()
53
48
  log.info "nerve: starting service watch #{@name}"
54
49
 
55
- # begin by reporting down
56
50
  @reporter.start()
57
- @reporter.report_down
58
51
  was_up = false
59
52
 
60
53
  until $EXIT
data/lib/nerve/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Nerve
2
- VERSION = "0.3.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/nerve.rb CHANGED
@@ -42,9 +42,12 @@ module Nerve
42
42
 
43
43
  begin
44
44
  sleep
45
- rescue
46
- log.debug 'nerve: sleep interrupted; exiting'
45
+ rescue StandardError => e
46
+ log.error 'nerve: encountered unexpected exception #{e.inspect} in main thread'
47
+ raise e
48
+ ensure
47
49
  $EXIT = true
50
+ log.warn 'nerve: reaping all watchers'
48
51
  @watchers.each do |name, watcher_thread|
49
52
  reap_watcher(name)
50
53
  end
data/nerve.gemspec CHANGED
@@ -18,5 +18,8 @@ Gem::Specification.new do |gem|
18
18
  gem.require_paths = ["lib"]
19
19
 
20
20
  gem.add_runtime_dependency "zk", "~> 1.9.2"
21
- gem.add_runtime_dependency "bunny", "= 1.0.0.rc2"
21
+ gem.add_runtime_dependency "bunny", "= 1.1.0"
22
+
23
+ gem.add_development_dependency "rake"
24
+ gem.add_development_dependency "rspec"
22
25
  end
data/spec/.gitkeep ADDED
File without changes
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Nerve::Reporter do
4
+ let(:subject) { {
5
+ 'zk_hosts' => ['zkhost1', 'zkhost2'],
6
+ 'zk_path' => 'zk_path',
7
+ 'instance_id' => 'instance_id',
8
+ 'host' => 'host',
9
+ 'port' => 'port'
10
+ }
11
+ }
12
+ it 'can new_from_service' do
13
+ expect(Nerve::Reporter::Zookeeper).to receive(:new).with(subject).and_return('kerplunk')
14
+ expect(Nerve::Reporter.new_from_service(subject)).to eq('kerplunk')
15
+ end
16
+ it 'actually constructs an instance of a specific backend' do
17
+ expect(Nerve::Reporter.new_from_service(subject).is_a?(Nerve::Reporter::Zookeeper)).to eql(true)
18
+ end
19
+ it 'the reporter backend inherits from the base class' do
20
+ expect(Nerve::Reporter.new_from_service(subject).is_a?(Nerve::Reporter::Base)).to eql(true)
21
+ end
22
+ it 'throws ArgumentError if you ask for a reporter type which does not exist' do
23
+ subject['reporter_type'] = 'does_not_exist'
24
+ expect { Nerve::Reporter.new_from_service(subject) }.to raise_error(ArgumentError)
25
+ end
26
+ end
27
+
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ require 'nerve/reporter/zookeeper'
3
+
4
+ describe Nerve::Reporter::Zookeeper do
5
+ let(:subject) { {
6
+ 'zk_hosts' => ['zkhost1', 'zkhost2'],
7
+ 'zk_path' => 'zk_path',
8
+ 'instance_id' => 'instance_id',
9
+ 'host' => 'host',
10
+ 'port' => 'port'
11
+ }
12
+ }
13
+ it 'actually constructs an instance' do
14
+ expect(Nerve::Reporter::Zookeeper.new(subject).is_a?(Nerve::Reporter::Zookeeper)).to eql(true)
15
+ end
16
+ end
17
+
@@ -0,0 +1,20 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ require "#{File.dirname(__FILE__)}/../lib/nerve"
8
+
9
+ RSpec.configure do |config|
10
+ config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ config.run_all_when_everything_filtered = true
12
+ config.filter_run :focus
13
+ config.include Config
14
+
15
+ # Run specs in random order to surface order dependencies. If you find an
16
+ # order dependency and want to debug it, you can fix the order by providing
17
+ # the seed, which is printed after each run.
18
+ # --seed 1234
19
+ config.order = 'random'
20
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nerve
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-01-27 00:00:00.000000000 Z
14
+ date: 2014-03-03 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: zk
@@ -36,7 +36,7 @@ dependencies:
36
36
  requirements:
37
37
  - - '='
38
38
  - !ruby/object:Gem::Version
39
- version: 1.0.0.rc2
39
+ version: 1.1.0
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
@@ -44,7 +44,39 @@ dependencies:
44
44
  requirements:
45
45
  - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: 1.0.0.rc2
47
+ version: 1.1.0
48
+ - !ruby/object:Gem::Dependency
49
+ name: rake
50
+ requirement: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ - !ruby/object:Gem::Dependency
65
+ name: rspec
66
+ requirement: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ type: :development
73
+ prerelease: false
74
+ version_requirements: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
48
80
  description: description
49
81
  email:
50
82
  - martin.rhoads@airbnb.com
@@ -57,6 +89,8 @@ files:
57
89
  - .gitignore
58
90
  - .mailmap
59
91
  - .nerve.rc
92
+ - .travis.yml
93
+ - CONTRIBUTING.md
60
94
  - Gemfile
61
95
  - Gemfile.lock
62
96
  - LICENSE.txt
@@ -69,6 +103,8 @@ files:
69
103
  - lib/nerve.rb
70
104
  - lib/nerve/log.rb
71
105
  - lib/nerve/reporter.rb
106
+ - lib/nerve/reporter/base.rb
107
+ - lib/nerve/reporter/zookeeper.rb
72
108
  - lib/nerve/ring_buffer.rb
73
109
  - lib/nerve/service_watcher.rb
74
110
  - lib/nerve/service_watcher/base.rb
@@ -78,6 +114,10 @@ files:
78
114
  - lib/nerve/utils.rb
79
115
  - lib/nerve/version.rb
80
116
  - nerve.gemspec
117
+ - spec/.gitkeep
118
+ - spec/lib/nerve/reporter_spec.rb
119
+ - spec/lib/nerve/reporter_zookeeper_spec.rb
120
+ - spec/spec_helper.rb
81
121
  homepage: ''
82
122
  licenses: []
83
123
  post_install_message:
@@ -102,4 +142,8 @@ rubygems_version: 1.8.23
102
142
  signing_key:
103
143
  specification_version: 3
104
144
  summary: summary
105
- test_files: []
145
+ test_files:
146
+ - spec/.gitkeep
147
+ - spec/lib/nerve/reporter_spec.rb
148
+ - spec/lib/nerve/reporter_zookeeper_spec.rb
149
+ - spec/spec_helper.rb