nerve 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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