nerve_pharmeasy 0.7.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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.mailmap +2 -0
  4. data/.nerve.rc +2 -0
  5. data/.travis.yml +8 -0
  6. data/CONTRIBUTING.md +28 -0
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +75 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +116 -0
  11. data/Rakefile +7 -0
  12. data/Vagrantfile +121 -0
  13. data/bin/nerve +16 -0
  14. data/example/nerve.conf.json +54 -0
  15. data/example/nerve_services/etcd_service1.json +19 -0
  16. data/example/nerve_services/zookeeper_service1.json +18 -0
  17. data/lib/nerve/configuration_manager.rb +106 -0
  18. data/lib/nerve/log.rb +24 -0
  19. data/lib/nerve/reporter/base.rb +61 -0
  20. data/lib/nerve/reporter/etcd.rb +73 -0
  21. data/lib/nerve/reporter/zookeeper.rb +101 -0
  22. data/lib/nerve/reporter.rb +18 -0
  23. data/lib/nerve/ring_buffer.rb +30 -0
  24. data/lib/nerve/service_watcher/base.rb +65 -0
  25. data/lib/nerve/service_watcher/http.rb +70 -0
  26. data/lib/nerve/service_watcher/rabbitmq.rb +68 -0
  27. data/lib/nerve/service_watcher/tcp.rb +56 -0
  28. data/lib/nerve/service_watcher.rb +152 -0
  29. data/lib/nerve/utils.rb +17 -0
  30. data/lib/nerve/version.rb +3 -0
  31. data/lib/nerve.rb +249 -0
  32. data/nerve.conf.json +23 -0
  33. data/nerve.gemspec +33 -0
  34. data/spec/.gitkeep +0 -0
  35. data/spec/configuration_manager_spec.rb +31 -0
  36. data/spec/example_services_spec.rb +42 -0
  37. data/spec/factories/check.rb +16 -0
  38. data/spec/factories/service.rb +26 -0
  39. data/spec/lib/nerve/reporter_etcd_spec.rb +18 -0
  40. data/spec/lib/nerve/reporter_spec.rb +86 -0
  41. data/spec/lib/nerve/reporter_zookeeper_spec.rb +32 -0
  42. data/spec/lib/nerve/service_watcher_spec.rb +89 -0
  43. data/spec/lib/nerve_spec.rb +186 -0
  44. data/spec/spec_helper.rb +33 -0
  45. metadata +216 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7fa208f63d742675316279d2dada16d3675a2cb8
4
+ data.tar.gz: d858a5049890b8f90a0e3113f4d2add354777f39
5
+ SHA512:
6
+ metadata.gz: 968b7d526d0db51e38b14f379d3c376a3597c2484f39f3320d872fa9314150709372ecd88c28f8b81ab187a060742f13a383d13e1619588e2b015df0f9e75379
7
+ data.tar.gz: f008dee705d0cff7412e36236c8a3549c98fd6d5e9d6e49b06aea5e8a82d2938577483c217fad62d9d5ea413b0f09662a096d85209b134ae42581ac1fe038f58
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
17
+ *.sw?
18
+ \#*\#
19
+ .\#*
20
+ vendor
21
+ .vagrant
22
+ .ruby-version
data/.mailmap ADDED
@@ -0,0 +1,2 @@
1
+ <igor.serebryany@airbedandbreakfast.com> <igor47@moomers.org>
2
+ <martin.rhoads@airbnb.com> <ermal14@gmail.com>
data/.nerve.rc ADDED
@@ -0,0 +1,2 @@
1
+ export DATA_BAG_DIR=/Users/martin/Dropbox/airbnb/src/chef/data_bags
2
+ export COOKBOOK_DIR=/Users/martin/Dropbox/airbnb/src/chef/cookbooks
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ cache: bundler
3
+ sudo: false
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.6
8
+ - 2.2.2
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,28 @@
1
+ # Contributing Guidelines #
2
+
3
+ Thanks for contributing to SmartStack!
4
+
5
+ If you are opening a new PR, please ask for a merge into the `master` branch.
6
+
7
+ ## Writing Reporters ##
8
+ Nerve supports *pluggable* reporters, which means that you can easily add
9
+ a reporter by making your own gem that contains your reporter available for
10
+ require at ``nerve/reporter/#{name.downcase}``. If you do this please do
11
+ submit a PR with a link to your gem/repo and we can reference it from the
12
+ README.
13
+
14
+ In general it is preferred to keep reporters that require specific dependencies
15
+ out of nerve because that way you can select the version of dependencies that
16
+ you need (for example if you have a particular version of the docker api or
17
+ etcd). That being said, if your reporter has no external dependencies
18
+ (e.g. files) or is extremely common (e.g. zookeeper, etcd), we may choose to
19
+ support it in the repo itself.
20
+
21
+ ## Writing Checks ##
22
+
23
+ We welcome additional service checks into the core of nerve.
24
+ However, your checks must follow a few guidelines or they will not be accepted:
25
+
26
+ * be sure to respect timeouts; checks that do not time-out will not be accepted
27
+ * do NOT shell out; this becomes very expensive when done frequently
28
+ * use well-tested, stable, core libraries whenever possible
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,75 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ nerve (0.7.0)
5
+ bunny (= 1.1.0)
6
+ etcd (~> 0.2.3)
7
+ json
8
+ zk (~> 1.9.2)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ activesupport (4.2.0)
14
+ i18n (~> 0.7)
15
+ json (~> 1.7, >= 1.7.7)
16
+ minitest (~> 5.1)
17
+ thread_safe (~> 0.3, >= 0.3.4)
18
+ tzinfo (~> 1.1)
19
+ amq-protocol (1.9.2)
20
+ bunny (1.1.0)
21
+ amq-protocol (>= 1.9.2)
22
+ coderay (1.1.0)
23
+ diff-lcs (1.2.5)
24
+ etcd (0.2.4)
25
+ mixlib-log
26
+ factory_girl (4.5.0)
27
+ activesupport (>= 3.0.0)
28
+ i18n (0.7.0)
29
+ json (1.8.2)
30
+ little-plugger (1.1.3)
31
+ logging (1.8.2)
32
+ little-plugger (>= 1.1.3)
33
+ multi_json (>= 1.8.4)
34
+ method_source (0.8.2)
35
+ minitest (5.5.1)
36
+ mixlib-log (1.6.0)
37
+ multi_json (1.11.2)
38
+ pry (0.10.1)
39
+ coderay (~> 1.1.0)
40
+ method_source (~> 0.8.1)
41
+ slop (~> 3.4)
42
+ rake (10.1.1)
43
+ rspec (3.1.0)
44
+ rspec-core (~> 3.1.0)
45
+ rspec-expectations (~> 3.1.0)
46
+ rspec-mocks (~> 3.1.0)
47
+ rspec-core (3.1.7)
48
+ rspec-support (~> 3.1.0)
49
+ rspec-expectations (3.1.2)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.1.0)
52
+ rspec-mocks (3.1.3)
53
+ rspec-support (~> 3.1.0)
54
+ rspec-support (3.1.2)
55
+ slop (3.6.0)
56
+ thread_safe (0.3.4)
57
+ tzinfo (1.2.2)
58
+ thread_safe (~> 0.1)
59
+ zk (1.9.5)
60
+ logging (~> 1.8.2)
61
+ zookeeper (~> 1.4.0)
62
+ zookeeper (1.4.10)
63
+
64
+ PLATFORMS
65
+ ruby
66
+
67
+ DEPENDENCIES
68
+ factory_girl
69
+ nerve!
70
+ pry
71
+ rake
72
+ rspec (~> 3.1.0)
73
+
74
+ BUNDLED WITH
75
+ 1.10.5
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Airbnb, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ [![Build Status](https://travis-ci.org/airbnb/nerve.png?branch=master)](https://travis-ci.org/airbnb/nerve)
2
+
3
+ # Nerve
4
+
5
+ Nerve is a utility for tracking the status of machines and services.
6
+ It runs locally on the boxes which make up a distributed system, and reports state information to a distributed key-value store.
7
+ At Airbnb, we use Zookeeper as our key-value store.
8
+ The combination of Nerve and [Synapse](https://github.com/airbnb/synapse) make service discovery in the cloud easy!
9
+
10
+ ## Motivation ##
11
+
12
+ We already use [Synapse](https://github.com/airbnb/synapse) to discover remote services.
13
+ However, those services needed boilerplate code to register themselves in [Zookeeper](http://zookeeper.apache.org/).
14
+ Nerve simplifies underlying services, enables code reuse, and allows us to create a more composable system.
15
+ It does so by factoring out the boilerplate into it's own application, which independenly handles monitoring and reporting.
16
+
17
+ Beyond those benefits, nerve also acts as a general watchdog on systems.
18
+ The information it reports can be used to take action from a centralized automation center: action like scaling distributed systems up or down or alerting ops or engineering about downtime.
19
+
20
+ ## Installation ##
21
+
22
+ To download and run the nerve binary, first install a version of ruby. Then,
23
+ install nerve with:
24
+
25
+ ```bash
26
+ $ mkdir -p /opt/smartstack/nerve
27
+
28
+ # If you want to install specific versions of dependencies such as an older
29
+ # version of the aws-sdk, the docker-api, etc, gem install that here *before*
30
+ # gem installing nerve. This is also where you would gem install
31
+ # custom reporters.
32
+
33
+ # If you are on Ruby 2.X use --no-document instead of --no-ri --no-rdoc
34
+ $ gem install nerve --install-dir /opt/smartstack/nerve --no-ri --no-rdoc
35
+ ```
36
+
37
+ This will download nerve and its dependencies into /opt/smartstack/nerve. You
38
+ might wish to omit the `--install-dir` flag to use your system's default gem
39
+ path, however this will require you to run `gem install nerve` with root
40
+ permissions. You can also install via bundler, but keep in mind you'll pick up
41
+ Nerve's version of library dependencies and possibly not the ones you need
42
+ for your infra/apps.
43
+
44
+ ## Configuration ##
45
+
46
+ Nerve depends on a single configuration file, in json format.
47
+ It is usually called `nerve.conf.json`.
48
+ An example config file is available in `example/nerve.conf.json`.
49
+ The config file is composed of two main sections:
50
+
51
+ * `instance_id`: the name nerve will submit when registering services; makes debugging easier
52
+ * `heartbeat_path`: a path to a file on disk to touch as nerve makes progress. This allows you to work around https://github.com/zk-ruby/zk/issues/50 by restarting a stuck nerve.
53
+ * `services`: the hash (from service name to config) of the services nerve will be monitoring
54
+ * `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
55
+
56
+ ### Services Config ###
57
+
58
+ Each service that nerve will be monitoring is specified in the `services` hash.
59
+ The key is the name of the service, and the value is a configuration hash telling nerve how to monitor the service.
60
+ The configuration contains the following options:
61
+
62
+ * `host`: the default host on which to make service checks; you should make this your *public* ip to ensure your service is publically accessible
63
+ * `port`: the default port for service checks; nerve will report the `host`:`port` combo via your chosen reporter
64
+ * `reporter_type`: the mechanism used to report up/down information; depending on the reporter you choose, additional parameters may be required. Defaults to `zookeeper`
65
+ * `check_interval`: the frequency with which service checks will be initiated; defaults to `500ms`
66
+ * `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
67
+ * `weight` (optional): a positive integer weight value which can be used to affect the haproxy backend weighting in synapse.
68
+ * `haproxy_server_options` (optional): a string containing any special haproxy server options for this service instance. For example if you wanted to set a service instance as a backup.
69
+ * `labels` (optional): an object containing user-defined key-value pairs that describe this service instance. For example, you could label service instances with datacenter information.
70
+
71
+ #### Zookeeper Reporter ####
72
+
73
+ If you set your `reporter_type` to `"zookeeper"` you should also set these parameters:
74
+
75
+ * `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
76
+ * `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
77
+
78
+ #### Etcd Reporter ####
79
+
80
+ Note: Etcd support is currently experimental!
81
+
82
+ If you set your `reporter_type` to `"etcd"` you should also set these parameters:
83
+
84
+ * `etcd_host`: etcd host that nerve will submit registration to
85
+ * `etcd_port`: port to connect to etcd.
86
+ * `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
87
+
88
+ ### Checks ###
89
+
90
+ The core of nerve is a set of service checks.
91
+ Each service can define a number of checks, and all of them must pass for the service to be registered.
92
+ Although the exact parameters passed to each check are different, all take a number of common arguments:
93
+
94
+ * `type`: (required) the kind of check; you can see available check types in the `lib/nerve/service_watcher` dir of this repo
95
+ * `name`: (optional) a descriptive, human-readable name for the check; it will be auto-generated based on the other parameters if not specified
96
+ * `host`: (optional) the host on which the check will be performed; defaults to the `host` of the service to which the check belongs
97
+ * `port`: (optional) the port on which the check will be performed; like `host`, it defaults to the `port` of the service
98
+ * `timeout`: (optional) maximum time the check can take; defaults to `100ms`
99
+ * `rise`: (optional) how many consecutive checks must pass before the check is considered passing; defaults to 1
100
+ * `fall`: (optional) how many consecutive checks must fail before the check is considered failing; defaults to 1
101
+
102
+ #### Custom External Checks ####
103
+
104
+ If you would like to run a custom check but don't feel like trying to get it merged into this project, there is a mechanism for including external checks thanks to @bakins (airbnb/nerve#36).
105
+ Build your custom check as a separate gem and make sure to `bundle install` it on your system.
106
+
107
+ Ideally, you should name your gem `"nerve-watcher-#{type}"`, as that is what nerve will `require` on boot.
108
+ However, if you have a custom name for your gem, you can specify that in the `module` argument to the check.
109
+
110
+ ## Contributing
111
+
112
+ 1. Fork it
113
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
114
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
115
+ 4. Push to the branch (`git push origin my-new-feature`)
116
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
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/Vagrantfile ADDED
@@ -0,0 +1,121 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ unless ENV['COOKBOOK_DIR'] and ENV['DATA_BAG_DIR']
5
+ STDERR.puts "you need to set COOKBOOK_DIR and DATA_BAG_DIR as environment variables"
6
+ Kernel.exit 1
7
+ end
8
+
9
+ this_dir = File.dirname(File.expand_path __FILE__)
10
+
11
+ STDERR.puts "mounting #{this_dir} as /root/this_dir"
12
+
13
+ Vagrant::Config.run do |config|
14
+ # All Vagrant configuration is done here. The most common configuration
15
+ # options are documented and commented below. For a complete reference,
16
+ # please see the online documentation at vagrantup.com.
17
+
18
+ # Every Vagrant virtual environment requires a box to build off of.
19
+ config.vm.box = "precise64"
20
+
21
+ # The url from where the 'config.vm.box' box will be fetched if it
22
+ # doesn't already exist on the user's system.
23
+ # config.vm.box_url = "http://domain.com/path/to/above.box"
24
+
25
+ config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
26
+
27
+ # Boot with a GUI so you can see the screen. (Default is headless)
28
+ # config.vm.boot_mode = :gui
29
+
30
+ # Assign this VM to a host-only network IP, allowing you to access it
31
+ # via the IP. Host-only networks can talk to the host machine as well as
32
+ # any other machines on the same network, but cannot be accessed (through this
33
+ # network interface) by any external networks.
34
+ # config.vm.network :hostonly, "192.168.33.10"
35
+
36
+ # Assign this VM to a bridged network, allowing you to connect directly to a
37
+ # network using the host's network device. This makes the VM appear as another
38
+ # physical device on your network.
39
+ # config.vm.network :bridged
40
+
41
+ # Forward a port from the guest to the host, which allows for outside
42
+ # computers to access the VM, whereas host only networking does not.
43
+ # config.vm.forward_port 80, 8080
44
+
45
+ # Share an additional folder to the guest VM. The first argument is
46
+ # an identifier, the second is the path on the guest to mount the
47
+ # folder, and the third is the path on the host to the actual folder.
48
+ # config.vm.share_folder "v-data", "/vagrant_data", "../data"
49
+
50
+ config.vm.share_folder 'this_dir', '/this_dir', this_dir
51
+
52
+ # Enable provisioning with Puppet stand alone. Puppet manifests
53
+ # are contained in a directory path relative to this Vagrantfile.
54
+ # You will need to create the manifests directory and a manifest in
55
+ # the file base.pp in the manifests_path directory.
56
+ #
57
+ # An example Puppet manifest to provision the message of the day:
58
+ #
59
+ # # group { "puppet":
60
+ # # ensure => "present",
61
+ # # }
62
+ # #
63
+ # # File { owner => 0, group => 0, mode => 0644 }
64
+ # #
65
+ # # file { '/etc/motd':
66
+ # # content => "Welcome to your Vagrant-built virtual machine!
67
+ # # Managed by Puppet.\n"
68
+ # # }
69
+ #
70
+ # config.vm.provision :puppet do |puppet|
71
+ # puppet.manifests_path = "manifests"
72
+ # puppet.manifest_file = "base.pp"
73
+ # end
74
+
75
+ # Enable provisioning with chef solo, specifying a cookbooks path, roles
76
+ # path, and data_bags path (all relative to this Vagrantfile), and adding
77
+ # some recipes and/or roles.
78
+ #
79
+ config.vm.provision :chef_solo do |chef|
80
+ # chef.cookbooks_path = "../my-recipes/cookbooks"
81
+ # chef.roles_path = "../my-recipes/roles"
82
+ # chef.data_bags_path = '/tmp/foo'
83
+ # chef.add_recipe "mysql"
84
+ # chef.add_role "web"
85
+
86
+ # You may also specify custom JSON attributes:
87
+ # chef.json = { :mysql_password => "foo" }
88
+
89
+ chef.data_bags_path = ENV['DATA_BAG_DIR']
90
+ chef.cookbooks_path = ENV['COOKBOOK_DIR']
91
+ chef.add_recipe 'vagrant'
92
+ chef.add_recipe 'nerve'
93
+ end
94
+
95
+ # Enable provisioning with chef server, specifying the chef server URL,
96
+ # and the path to the validation key (relative to this Vagrantfile).
97
+ #
98
+ # The Opscode Platform uses HTTPS. Substitute your organization for
99
+ # ORGNAME in the URL and validation key.
100
+ #
101
+ # If you have your own Chef Server, use the appropriate URL, which may be
102
+ # HTTP instead of HTTPS depending on your configuration. Also change the
103
+ # validation key to validation.pem.
104
+ #
105
+ # config.vm.provision :chef_client do |chef|
106
+ # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
107
+ # chef.validation_key_path = "ORGNAME-validator.pem"
108
+ # end
109
+ #
110
+ # If you're using the Opscode platform, your validator client is
111
+ # ORGNAME-validator, replacing ORGNAME with your organization name.
112
+ #
113
+ # IF you have your own Chef Server, the default validation client name is
114
+ # chef-validator, unless you changed the configuration.
115
+ #
116
+ # chef.validation_client_name = "ORGNAME-validator"
117
+
118
+ # Vagrant::Config.run do |config|
119
+ # config.vm.provision :shell, :path => "vagrant/init.sh"
120
+ # end
121
+ end
data/bin/nerve ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require 'optparse'
5
+
6
+ require 'nerve'
7
+ require 'nerve/configuration_manager'
8
+
9
+ # Parse command line options and parse configuration
10
+ config_manager = Nerve::ConfigurationManager.new()
11
+ config_manager.parse_options!
12
+
13
+ nerve_application = Nerve::Nerve.new(config_manager)
14
+ nerve_application.run
15
+
16
+ puts "exiting nerve"
@@ -0,0 +1,54 @@
1
+ {
2
+ "instance_id": "mymachine",
3
+ "service_conf_dir": "example/nerve_services",
4
+ "services": {
5
+ "your_http_service": {
6
+ "host": "1.2.3.4",
7
+ "port": 3000,
8
+ "reporter_type": "zookeeper",
9
+ "zk_hosts": ["localhost:2181"],
10
+ "zk_path": "/nerve/services/your_http_service/services",
11
+ "check_interval": 2,
12
+ "checks": [
13
+ {
14
+ "type": "http",
15
+ "uri": "/health",
16
+ "timeout": 0.2,
17
+ "rise": 3,
18
+ "fall": 2
19
+ }
20
+ ]
21
+ },
22
+ "your_tcp_service": {
23
+ "host": "1.2.3.4",
24
+ "port": 6379,
25
+ "reporter_type": "zookeeper",
26
+ "zk_hosts": ["localhost:2181"],
27
+ "zk_path": "/nerve/services/your_tcp_service/services",
28
+ "check_interval": 2,
29
+ "checks": [
30
+ {
31
+ "type": "tcp",
32
+ "timeout": 0.2,
33
+ "rise": 3,
34
+ "fall": 2
35
+ }
36
+ ]
37
+ },
38
+ "rabbitmq_service": {
39
+ "host": "1.2.3.4",
40
+ "port": 5672,
41
+ "reporter_type": "zookeeper",
42
+ "zk_hosts": ["localhost:2181"],
43
+ "zk_path": "/nerve/services/your_rabbitmq_service/services",
44
+ "check_interval": 2,
45
+ "checks": [
46
+ {
47
+ "type": "rabbitmq",
48
+ "username": "guest",
49
+ "password": "guest"
50
+ }
51
+ ]
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "host": "1.2.3.4",
3
+ "port": 3000,
4
+ "reporter_type": "etcd",
5
+ "etcd_host": "localhost",
6
+ "etcd_port": 4001,
7
+ "etcd_path": "/nerve/services/your_http_service/services",
8
+ "check_interval": 2,
9
+ "weight": 2,
10
+ "checks": [
11
+ {
12
+ "type": "http",
13
+ "uri": "/health",
14
+ "timeout": 0.2,
15
+ "rise": 3,
16
+ "fall": 2
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "host": "1.2.3.4",
3
+ "port": 3000,
4
+ "reporter_type": "zookeeper",
5
+ "zk_hosts": ["localhost:2181"],
6
+ "zk_path": "/nerve/services/your_http_service/services",
7
+ "check_interval": 2,
8
+ "weight": 2,
9
+ "checks": [
10
+ {
11
+ "type": "http",
12
+ "uri": "/health",
13
+ "timeout": 0.2,
14
+ "rise": 3,
15
+ "fall": 2
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,106 @@
1
+ require 'yaml'
2
+ require 'optparse'
3
+ require 'nerve/log'
4
+ require 'logger'
5
+
6
+
7
+ module Nerve
8
+ class ConfigurationManager
9
+ include Logging
10
+
11
+ attr_reader :options
12
+ attr_reader :config
13
+
14
+ def parse_options_from_argv!
15
+ options = {}
16
+ # set command line options
17
+ optparse = OptionParser.new do |opts|
18
+ opts.banner =<<EOB
19
+ Welcome to nerve
20
+
21
+ Usage: nerve --config /path/to/nerve/config
22
+ EOB
23
+
24
+ options[:config] = ENV['NERVE_CONFIG']
25
+ opts.on('-c config','--config config', String, 'path to nerve config') do |key,value|
26
+ options[:config] = key
27
+ end
28
+
29
+ options[:instance_id] = ENV['NERVE_INSTANCE_ID']
30
+ opts.on('-i instance_id','--instance_id instance_id', String,
31
+ 'reported as `name` to ZK; overrides instance id from config file') do |key,value|
32
+ options[:instance_id] = key
33
+ end
34
+
35
+ options[:check_config] = ENV['NERVE_CHECK_CONFIG']
36
+ opts.on('-k', '--check-config',
37
+ 'Validate the nerve config ONLY and exit 0 if valid (non zero otherwise)') do |_|
38
+ options[:check_config] = true
39
+ end
40
+
41
+ opts.on('-h', '--help', 'Display this screen') do
42
+ puts opts
43
+ exit
44
+ end
45
+ end
46
+ optparse.parse!
47
+ options.each do|key, value|
48
+ log.info key
49
+ log.info value
50
+ end
51
+ options
52
+ end
53
+
54
+ def parse_options!
55
+ @options = parse_options_from_argv!
56
+ end
57
+
58
+ def generate_nerve_config(options)
59
+
60
+ config = parse_config_file(options[:config])
61
+ config['services'] ||= {}
62
+
63
+ if config.has_key?('service_conf_dir')
64
+ cdir = File.expand_path(config['service_conf_dir'])
65
+ if ! Dir.exists?(cdir) then
66
+ raise "service conf dir does not exist:#{cdir}"
67
+ end
68
+ cfiles = Dir.glob(File.join(cdir, '*.{yaml,json}'))
69
+ cfiles.each { |x| config['services'][File.basename(x[/(.*)\.(yaml|json)$/, 1])] = parse_config_file(x) }
70
+ end
71
+
72
+ if options[:instance_id] && !options[:instance_id].empty?
73
+ config['instance_id'] = options[:instance_id]
74
+ end
75
+
76
+ config
77
+ end
78
+
79
+ def parse_config_file(filename)
80
+ # parse nerve config file
81
+ begin
82
+ c = YAML::parse(File.read(filename))
83
+ rescue Errno::ENOENT => e
84
+ raise ArgumentError, "config file does not exist:\n#{e.inspect}"
85
+ rescue Errno::EACCES => e
86
+ raise ArgumentError, "could not open config file:\n#{e.inspect}"
87
+ rescue YAML::SyntaxError => e
88
+ raise "config file #{filename} is not proper yaml:\n#{e.inspect}"
89
+ end
90
+ return c.to_ruby
91
+ end
92
+
93
+ def reload!
94
+ raise "You must parse command line options before reloading config" if @options.nil?
95
+ @config = generate_nerve_config(@options)
96
+ end
97
+
98
+ def setConfig(configuration)
99
+ @config = configuration
100
+ @options = {}
101
+ @options[:check_config]=false
102
+ end
103
+ end
104
+ end
105
+
106
+
data/lib/nerve/log.rb ADDED
@@ -0,0 +1,24 @@
1
+ module Nerve
2
+ module Logging
3
+
4
+ def log
5
+ @logger ||= Logging.logger_for(self.class.name)
6
+ end
7
+
8
+ # Use a hash class-ivar to cache a unique Logger per class:
9
+ @loggers = {}
10
+
11
+ class << self
12
+ def logger_for(classname)
13
+ @loggers[classname] ||= configure_logger_for(classname)
14
+ end
15
+
16
+ def configure_logger_for(classname)
17
+ logger = Logger.new(STDERR)
18
+ logger.level = Logger::INFO unless ENV['DEBUG']
19
+ logger.progname = classname
20
+ return logger
21
+ end
22
+ end
23
+ end
24
+ end