litmus_paper 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Dockerfile ADDED
@@ -0,0 +1,20 @@
1
+ FROM debian:stretch
2
+ EXPOSE 9293/TCP
3
+ EXPOSE 9294/TCP
4
+ WORKDIR /home/litmus_paper
5
+ RUN apt-get update && apt-get install -y ruby ruby-dev git curl rsyslog build-essential
6
+ RUN gem install --no-ri --no-rdoc bundler \
7
+ && gem install sinatra --no-ri --no-rdoc --version "~> 1.3.2" \
8
+ && gem install remote_syslog_logger --no-ri --no-rdoc --version "~> 1.0.3" \
9
+ && gem install unicorn --no-ri --no-rdoc --version "~> 4.6.2" \
10
+ && gem install colorize --no-ri --no-rdoc \
11
+ && gem install rspec --no-ri --no-rdoc --version "~> 2.9.0" \
12
+ && gem install rack-test --no-ri --no-rdoc --version "~> 0.6.1" \
13
+ && gem install rake --no-ri --no-rdoc --version "~> 0.9.2.2" \
14
+ && gem install rake_commit --no-ri --no-rdoc --version "~> 0.13"
15
+ ADD . /home/litmus_paper
16
+ RUN ln -sf /home/litmus_paper/docker/litmus.conf /etc/litmus.conf \
17
+ && ln -sf /home/litmus_paper/docker/litmus_unicorn.rb /etc/litmus_unicorn.rb
18
+ RUN gem build litmus_paper.gemspec && gem install litmus_paper*.gem
19
+
20
+ CMD ["bin/litmus", "-p", "9293", "-c", "/etc/litmus_unicorn.rb"]
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # LitmusPaper
2
2
 
3
- Backend health tester for HA Services, or as an agent-check for HAProxy
3
+ LitmusPaper is a backend health tester for Highly Available (HA) services.
4
4
 
5
5
  [![Build Status](https://secure.travis-ci.org/braintree/litmus_paper.png)](http://travis-ci.org/braintree/litmus_paper)
6
6
 
@@ -18,10 +18,251 @@ Or install it yourself as:
18
18
 
19
19
  $ gem install litmus_paper
20
20
 
21
+ ## Overview
22
+
23
+ Litmus Paper reports health for each service on a node (like a server, vm, or container) on a 0-100 scale. Health is computed by aggregating the values returned from running various subchecks. Health information can be queried through a REST API for consumption by other services (like load balancers or monitoring systems), or queried on the command line.
24
+
25
+ There are two classes of subchecks: Dependencies and Metrics. Dependencies report either 0 or 100 health, and aggregate such that if any dependency is not 100 the whole service is down. Metrics report health on a scale from 0-100 and aggregate as averages based on their weight.
26
+
27
+ You can also force a service to report a health value on a host.
28
+ Forcing a service up makes it report a health of 100, regardless of the measured health.
29
+ Forcing a service down makes it report a health of 0.
30
+ Forcing a service's health to a value between 0 and 100 places a ceiling on its health. The service will report the lower of the measured health or the forced health value.
31
+
32
+ Force downs take precedence, followed by force ups, and finally force healths. If you specify both a force up and a force down, the service will report 0 health. If you specify a force up and a force health, the force health will be ignored and the service will report 100 health.
33
+
34
+ Using litmus-agent-check, Litmus can also output health information in haproxy agent check format.
35
+
21
36
  ## Usage
22
37
 
23
- Use the sample config to run it under unicorn. Or execute litmus-agent-check to
24
- use it as an agent-check for HAProxy.
38
+ ### Running Litmus Paper
39
+
40
+ Start the process under unicorn with `/usr/bin/litmus --unicorn-config <path_to_unicorn_conf>`. In the unicorn config file, set the number of worker processes, the pid, and the working directory. See the [unicorn documentation](https://bogomips.org/unicorn/Unicorn/Configurator.html) for the config format. There are a few command line options:
41
+
42
+ ```
43
+ Usage: litmus [options]
44
+
45
+ -b, --binding=ip Binds Litmus to the specified ip.
46
+ Default: 0.0.0.0
47
+ -d, --daemon Make server run as a Daemon.
48
+ -p, --port=port Listen Port
49
+ -c, --unicorn-config=config Unicorn Config
50
+
51
+ -h, --help Show this help message.
52
+ ```
53
+
54
+ For HAProxy agent checks, run `/usr/bin/litmus-agent-check`. See the "HAProxy Agent Check Configuration" section below for a full list of options.
55
+
56
+ ### Global configuration
57
+
58
+ This can be either yaml or ruby.
59
+
60
+ Examples:
61
+
62
+ ```
63
+ # /etc/litmus.conf
64
+
65
+ include_files "litmus.d/*.conf"
66
+
67
+ port 80
68
+
69
+ data_directory "/litmus"
70
+ ```
71
+
72
+ ```yaml
73
+ # /etc/litmus.conf.yaml
74
+ ---
75
+ include-files: 'litmus.d/*.yaml'
76
+ port: 80
77
+ data_directory: '/litmus'
78
+ ```
79
+
80
+ Available fields:
81
+ - include_files: Tells Litmus to load health check configurations from a path.
82
+ - port: Port Litmus unicorn server will listen on, defaults to 9292.
83
+ - data_directory: Where to store force down, up, and health files. Defaults to "/etc/litmus".
84
+ - cache_location: Where to store cached health information, defaults to "/run/shm".
85
+ - cache_ttl: Time to live in seconds for cached health check values, defaults to -1.
86
+
87
+ ### Service health check configuration
88
+
89
+ To add services and health checks, Litmus Paper loads YAML configurations or ruby code. The examples below show both styles.
90
+
91
+ Suppose you're writing a health check for a web application. You might start with a simple check to report if the server is responding at all:
92
+
93
+ ```yaml
94
+ # /etc/litmus.d/myapp.yaml
95
+ ---
96
+ services:
97
+ myapp:
98
+ dependencies:
99
+ - type: http
100
+ uri: 'https://localhost/heartbeat'
101
+ method: 'GET'
102
+ ca_file: '/etc/ssl/certs/ca-certificates.crt'
103
+ ```
104
+
105
+ ```ruby
106
+ # /etc/litmus.d/myapp.conf
107
+ service "myapp" do |s|
108
+ s.depends Dependency::HTTP, "https://localhost/heartbeat", :method => "GET", :ca_file => "/etc/ssl/certs/ca-certificates.crt"
109
+ end
110
+ ```
111
+
112
+ Maybe you also want to balance traffic based on CPU load:
113
+
114
+ ```yaml
115
+ # /etc/litmus.d/myapp.yaml
116
+ ---
117
+ services:
118
+ myapp:
119
+ dependencies:
120
+ - type: http
121
+ uri: 'https://localhost/heartbeat'
122
+ method: 'GET'
123
+ ca_file: '/etc/ssl/certs/ca-certificates.crt'
124
+ checks:
125
+ - type: cpu_load
126
+ weight: 100
127
+ ```
128
+
129
+ ```ruby
130
+ # /etc/litmus.d/myapp.conf
131
+ service "myapp" do |s|
132
+ s.depends Dependency::HTTP, "https://localhost/heartbeat", :method => "GET", :ca_file => "/etc/ssl/certs/ca-certificates.crt"
133
+ s.measure_health Metric::CPULoad, :weight => 100
134
+ end
135
+ ```
136
+
137
+ Once you've finished adding checks, restart litmus paper to pick up the new service.
138
+
139
+ Here are all the types of checks currently implemented:
140
+
141
+ - `http` (`Dependency::HTTP`): Checks HTTP 200 response from a URI.
142
+ * uri
143
+ * method (defaults to get)
144
+ * ca_file (defaults to nil)
145
+ * timeout (defaults to 2s)
146
+
147
+ - `tcp` (`Dependency::TCP`): Checks successful completion of a TCP handshake with an address.
148
+ * ip
149
+ * port
150
+ * input_data (defaults to nil)
151
+ * expected_output (defaults to nil)
152
+ * timeout (defaults to 2s)
153
+
154
+ - `file_contents` (`Dependency::FileContents`): Checks whether the contents of a file match a string or regex.
155
+ * path
156
+ * regex
157
+ * timeout (defaults to 5s)
158
+
159
+ - `script` (`Dependency::Script`): Checks whether a command exits with 0 (success) or not (failure).
160
+ * command
161
+ * timeout (defaults to 5s)
162
+
163
+ - `constant_metric` (`Metric::ConstantMetric`): A dummy metric that always reports a constant.
164
+ * weight (0-100)
165
+
166
+ - `cpu_load` (`Metric::CPULoad`): Normalizes CPU load to a value between 1-100 and inverts it, so higher numbers mean less load and lower numbers mean more. Final health is weighted against other checks by `:weight`. The lower bound of 1 ensures that nodes will not leave the cluster solely based on CPU load. An example of how allowing 0 can cause problems: If one node has 4 CPUs and a load of 4 with CPU usage weighted at 100, it will report its health as 0, and all traffic will be shifted towards other nodes. These nodes in turn hit 100% CPU usage and report 0 health, causing a cascade of exiting nodes that shuts down the service.
167
+ * weight (1-100)
168
+
169
+ - `internet_health` (`Metric::InternetHealth`): Checks connectivity across a set of hosts and computes a weight based on how many are reachable. Helpful if you want to check outbound connectivity through multiple ISPs.
170
+ * weight (0-100)
171
+ * hosts
172
+ * timeout (defaults to 5s)
173
+
174
+ - `script` (`Metric::Script`): Runs a script to obtain a health from 0-100. This is helpful for customized metrics.
175
+ * command
176
+ * weight (0-100)
177
+ * timeout (defaults to 5s)
178
+
179
+ - `big_brother_service` (`Metric::BigBrotherService`): Used in conjunction with [Big Brother](https://github.com/braintree/big_brother), reports health based on the overall health of another load balanced service.
180
+ * service
181
+
182
+ ### HAProxy agent check configuration
183
+
184
+ Litmus paper can also report health checks in HAProxy agent check format. The agent check functionality takes the health data from a service health check, and exposes it on a different port in the format HAProxy expects.
185
+
186
+ There are no additional configuration files for the agent check, since all options are specified on the command line. Services are configured as normal litmus services as described above.
187
+
188
+ ```
189
+ Usage: litmus-agent-check [options]
190
+ -s, --service SERVICE:PORT,... agent-check service to port mappings
191
+ -c, --config CONFIG Path to litmus paper config file
192
+ -p, --pid-file PID_FILE Where to write the pid
193
+ -w, --workers WORKERS Number of worker processes
194
+ -D, --daemonize Daemonize the process
195
+ -h, --help Help text
196
+ ```
197
+
198
+ The service:port argument means that the server will expose the data from the litmus check for `service` on `port` in HAProxy agent check format. For example, if you wanted to serve status information about `myapp` on port `8080`, and already had a service config for it, you'd pass `-s myapp:8080`.
199
+
200
+ On the HAProxy server, add `agent-check agent-port 8080 agent-inter <seconds>s` to the config line for each server listed for that backend. This tells HAProxy to query port 8080 on the backend every `<seconds>` seconds for health information. See the [HAProxy agent check documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#5.2-agent-check) for more details.
201
+
202
+ ### REST API
203
+
204
+ The REST API is the main way other services should interact with Litmus. For routes that take parameters, pass them as form parameters in the request body.
205
+
206
+ - /
207
+ * GET: Returns table with status of each service.
208
+
209
+ Litmus Paper 1.1.1
210
+
211
+ Service │ Reported │ Measured │ Health
212
+ Name │ Health │ Health │ Forced?
213
+ ────────────┴──────────┴──────────┴─────────
214
+ myapp 0 100 Yes, Reason: testing
215
+ myotherapp 72 72 No
216
+
217
+ - /down
218
+ * POST: Creates a global force down. Parameters: reason => reason for the force down.
219
+ * DELETE: Deletes global force down, if one is in place. Any service-specific force downs remain in effect.
220
+
221
+ - /up
222
+ * POST: Creates a global force up. Parameters: reason => reason for the force up.
223
+ * DELETE: Deletes global force up, if one is in place. Any service-specific force ups remain in effect.
224
+
225
+ - /health
226
+ * POST: Creates a global force health. Parameters: reason => reason for the force health, health => health to force to.
227
+ * DELETE: Deletes global force health, if one is in place. Any service-specific force healths remain in effect.
228
+
229
+ - /`<service>`/status
230
+ * GET: Returns a detailed status output for `<service>`, including all the subchecks. This output easier to parse than the output from GET /. Also sets X-Health and X-Health-Forced headers in the response.
231
+
232
+ ```
233
+ Health: 82
234
+ Measured Health: 82
235
+ Dependency::HTTP(http://localhost/heartbeat): OK
236
+ Metric::CPULoad(100): 82
237
+ ```
238
+
239
+ - /`<service>`/down
240
+ * POST: Creates a force down just for `<service>`. Parameters: reason => reason for the force down.
241
+ * DELETE: Deletes force down for `<service>`, if one is in place. Global force downs remain in effect.
242
+
243
+ - /`<service>`/up
244
+ * POST: Creates a force up just for `<service>`. Parameters: reason => reason for the force up.
245
+ * DELETE: Deletes force up for `<service>`, if one is in place. Global force ups remain in effect.
246
+
247
+ - /`<service>`/health
248
+ * POST: Creates a force health just for `<service>`. Parameters: reason => reason for the force health, health => health to force to.
249
+ * DELETE: Deletes force health for `<service>`, if one is in place. Global force healths remain in effect.
250
+
251
+ ### Litmusctl
252
+
253
+ There is also a CLI included called `litmusctl`.
254
+
255
+ This has the same functionality as the rest API:
256
+ - `litmusctl list` = `GET /`
257
+ - `litmusctl status <service>` = `GET /<service>/status`
258
+ - `litmusctl force (down|up|health) [health] [-r REASON]` = `POST /(down|up|health)`
259
+ - `litmusctl force (down|up|health) [health] <service> [-r REASON]` = `POST /<service>/(down|up|health)`
260
+ - `litmusctl force (down|up|health) -d` = `DELETE /(down|up|health)`
261
+ - `litmusctl force (down|up|health) <service> -d` = `DELETE /<service>/(down|up|health)`
262
+
263
+ ## Tests
264
+
265
+ Run tests using `rake`. The default task runs all the tests.
25
266
 
26
267
  ## Releasing
27
268
 
@@ -34,3 +275,38 @@ use it as an agent-check for HAProxy.
34
275
  3. Commit your changes (`git commit -am 'Added some feature'`)
35
276
  4. Push to the branch (`git push origin my-new-feature`)
36
277
  5. Create new Pull Request
278
+
279
+ ## Docker
280
+
281
+ We've provided a simple docker image that can be used to try out litmus_paper, run tests, or to assist in feature development.
282
+
283
+ To run it, from the top of your copy of the litmus_paper repository:
284
+
285
+ ```
286
+ docker build -t litmus_paper .
287
+ # Run litmus_paper
288
+ docker run -d -p 9293:9293 litmus_paper
289
+ ```
290
+
291
+ Then, you can `curl localhost:9293` from your host machine to interact with the REST API.
292
+
293
+ To run interactively and attach to the container:
294
+
295
+ ```
296
+ docker run -it litmus_paper /bin/bash
297
+ # bin/litmus -p 9293 -d
298
+ # curl localhost:9293
299
+ ```
300
+
301
+ To run litmus-agent-check on port 9294:
302
+
303
+ ```
304
+ # Run litmus paper with litmus-agent-check using 10 workers
305
+ docker run -d -p 9294:9294 litmus_paper bin/litmus-agent-check -s test:9294 -c /etc/litmus.conf -w 10
306
+ ```
307
+
308
+ ## TODO
309
+
310
+ 1. Accept configuration in either YAML or ruby format.
311
+ 2. Improve concurrency model, with a health-check process and a responder process.
312
+ 3. Improve concurrency of agent-check daemon.
@@ -0,0 +1,7 @@
1
+ port 9292
2
+ data_directory '/tmp/litmus_paper'
3
+ cache_location '/tmp/litmus_paper_cache'
4
+ cache_ttl -1
5
+ service :test do |s|
6
+ s.measure_health Metric::CPULoad, :weight => 100
7
+ end
@@ -0,0 +1,11 @@
1
+ # vim: set ft=ruby
2
+ require 'remote_syslog_logger'
3
+
4
+ APP_ROOT = '/var/lib/gems/2.3.0/gems/litmus_paper-1.1.1'
5
+
6
+ worker_processes 5
7
+ working_directory APP_ROOT
8
+
9
+ logger(RemoteSyslogLogger.new('127.0.0.1', 514, :program => 'litmus_paper', :facility => 'daemon'))
10
+
11
+ pid "/tmp/unicorn.pid"
@@ -1,8 +1,21 @@
1
1
  module LitmusPaper
2
2
  class AgentCheckHandler
3
3
  def self.handle(service)
4
+ @cache ||= LitmusPaper::Cache.new(
5
+ LitmusPaper.cache_location,
6
+ "litmus_agent_cache",
7
+ LitmusPaper.cache_ttl
8
+ )
4
9
  output = []
5
- health = LitmusPaper.check_service(service)
10
+
11
+ health = @cache.get(service)
12
+ if !health
13
+ @cache.set(
14
+ service,
15
+ health = LitmusPaper.check_service(service)
16
+ )
17
+ end
18
+
6
19
  if health.nil?
7
20
  output << "failed#NOT_FOUND"
8
21
  else
@@ -1,3 +1,5 @@
1
+ require 'fileutils'
2
+ require 'tempfile'
1
3
  require 'yaml'
2
4
 
3
5
  module LitmusPaper
@@ -11,12 +13,24 @@ module LitmusPaper
11
13
 
12
14
  def set(key, value)
13
15
  return unless @ttl > 0
14
- File.open(File.join(@path, key), "a") do |f|
15
- f.flock(File::LOCK_EX)
16
- f.rewind
17
- f.write("#{Time.now.to_f + @ttl} #{YAML::dump(value)}")
18
- f.flush
19
- f.truncate(f.pos)
16
+ filename = File.join(@path, key)
17
+ if File.exist?(filename)
18
+ File.open(filename, "r+") do |f|
19
+ f.flock(File::LOCK_EX)
20
+ f.rewind
21
+ f.write(_entry(@ttl, value))
22
+ f.flush
23
+ f.truncate(f.pos)
24
+ end
25
+ else
26
+ temp = Tempfile.new("#{key}_init", @path)
27
+ begin
28
+ temp.write(_entry(@ttl, value))
29
+ temp.flush
30
+ ensure
31
+ temp.close
32
+ end
33
+ FileUtils.mv(temp.path, filename)
20
34
  end
21
35
  end
22
36
 
@@ -29,5 +43,9 @@ module LitmusPaper
29
43
  expires_at.to_f < Time.now.to_f ? nil : YAML::load(value)
30
44
  end
31
45
  end
46
+
47
+ def _entry(ttl, value)
48
+ "#{Time.now.to_f + ttl} #{YAML::dump(value)}"
49
+ end
32
50
  end
33
51
  end
@@ -1,3 +1,5 @@
1
+ require 'yaml'
2
+
1
3
  module LitmusPaper
2
4
  class ConfigurationFile
3
5
  def initialize(config_file_path)
@@ -12,7 +14,11 @@ module LitmusPaper
12
14
  def evaluate(file = @config_file_path)
13
15
  LitmusPaper.logger.info "Loading file #{file}"
14
16
  config_contents = File.read(file)
15
- instance_eval(config_contents)
17
+ if file =~ /\.(yaml|yml)/
18
+ load_yaml(config_contents)
19
+ else
20
+ instance_eval(config_contents)
21
+ end
16
22
  LitmusPaper::Configuration.new(
17
23
  @port,
18
24
  @data_directory,
@@ -45,6 +51,64 @@ module LitmusPaper
45
51
  @services[name.to_s] = service
46
52
  end
47
53
 
54
+ def yaml_service(value)
55
+ value.each do |name, config|
56
+ config = Util.symbolize_keys(config)
57
+ dependencies = parse_yaml_dependencies(config.fetch(:dependencies, []))
58
+ checks = parse_yaml_checks(config.fetch(:checks, []))
59
+ service = Service.new(name.to_s, dependencies, checks)
60
+ @services[name.to_s] = service
61
+ end
62
+ end
63
+
64
+ def parse_yaml_checks(config)
65
+ config.map do |check|
66
+ check_config = Util.symbolize_keys(check)
67
+ case check[:type].to_sym
68
+ when :big_brother_service
69
+ Metric::BigBrotherService.new(check_config.delete(:service))
70
+ when :cpu_load
71
+ Metric::CPULoad.new(check_config.delete(:weight))
72
+ when :constant_metric
73
+ Metric::ConstantMetric.new(check_config.delete(:weight))
74
+ when :internet_health
75
+ weight = check_config.delete(:weight)
76
+ hosts = check_config.delete(:hosts)
77
+ Metric::InternetHealth.new(weight, hosts, check_config)
78
+ when :script
79
+ command = check_config.delete(:command)
80
+ weight = check_config.delete(:weight)
81
+ Metric::Script.new(command, weight, check_config)
82
+ end
83
+ end
84
+ end
85
+
86
+ def parse_yaml_dependencies(config)
87
+ config.map do |dep|
88
+ dep_config = Util.symbolize_keys(dep)
89
+ case dep[:type].to_sym
90
+ when :file_contents
91
+ path = dep_config.delete(:path)
92
+ regex = dep_config.delete(:regex)
93
+ Dependency::FileContents.new(path, regex, dep_config)
94
+ when :haproxy_backends
95
+ domain_socket = dep_config.delete(:domain_socket)
96
+ cluster = dep_config.delete(:cluster)
97
+ Dependency::HaproxyBackends.new(domain_socket, cluster, dep_config)
98
+ when :http
99
+ uri = dep_config.delete(:uri)
100
+ Dependency::HTTP.new(uri, dep_config)
101
+ when "script"
102
+ command = dep_config.delete(:command)
103
+ Dependency::Script.new(command, options)
104
+ when "tcp"
105
+ ip = dep_config.delete(:ip)
106
+ port = dep_config.delete(:port)
107
+ Dependency::TCP.new(ip, port, dep_config)
108
+ end
109
+ end
110
+ end
111
+
48
112
  def cache_location(location)
49
113
  @cache_location = location
50
114
  end
@@ -52,5 +116,18 @@ module LitmusPaper
52
116
  def cache_ttl(ttl)
53
117
  @cache_ttl = ttl
54
118
  end
119
+
120
+ def load_yaml(contents)
121
+ config = Util.symbolize_keys(YAML.load(contents))
122
+
123
+ config.each do |key, value|
124
+ case key
125
+ when :include_files, :data_directory, :cache_location, :cache_ttl, :port
126
+ send(key, value)
127
+ when :services
128
+ yaml_service(value)
129
+ end
130
+ end
131
+ end
55
132
  end
56
133
  end
@@ -0,0 +1,12 @@
1
+ module LitmusPaper
2
+ class Util
3
+ def self.symbolize_keys(hash)
4
+ hash.keys.each do |k|
5
+ new_key = (k.to_sym rescue k.to_s.to_sym)
6
+ hash[new_key] = hash.delete(k)
7
+ end
8
+
9
+ hash
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module LitmusPaper
2
- VERSION = "1.1.1"
2
+ VERSION = "1.2.0"
3
3
  end
data/lib/litmus_paper.rb CHANGED
@@ -31,6 +31,7 @@ require 'litmus_paper/metric/internet_health'
31
31
  require 'litmus_paper/metric/script'
32
32
  require 'litmus_paper/service'
33
33
  require 'litmus_paper/status_file'
34
+ require 'litmus_paper/util'
34
35
  require 'litmus_paper/version'
35
36
 
36
37
  module LitmusPaper
@@ -2,6 +2,10 @@ require 'spec_helper'
2
2
  require 'litmus_paper/agent_check_handler'
3
3
 
4
4
  describe LitmusPaper::AgentCheckHandler do
5
+ def app
6
+ LitmusPaper::App
7
+ end
8
+
5
9
  before :each do
6
10
  LitmusPaper.configure(TEST_CONFIG)
7
11
  end
@@ -73,5 +77,39 @@ describe LitmusPaper::AgentCheckHandler do
73
77
  LitmusPaper::StatusFile.global_up_file.create("Up for testing")
74
78
  LitmusPaper::AgentCheckHandler.handle("test").should == "ready\tup\t100%"
75
79
  end
80
+
81
+ it "retrieves a cached value during the cache_ttl" do
82
+ begin
83
+ cache = LitmusPaper::AgentCheckHandler.instance_variable_get(:@cache)
84
+ cache.instance_variable_set(:@ttl, 0.25)
85
+ LitmusPaper::AgentCheckHandler.instance_variable_set(:@cache, cache)
86
+
87
+ test_service = LitmusPaper::Service.new(
88
+ 'test',
89
+ [AlwaysAvailableDependency.new],
90
+ [LitmusPaper::Metric::ConstantMetric.new(100)]
91
+ )
92
+ LitmusPaper.services['test'] = test_service
93
+
94
+ post "/test/health", :reason => "health for testing", :health => 88
95
+ last_response.status.should == 201
96
+
97
+ LitmusPaper::AgentCheckHandler.handle('test').should == "ready\tup\t88%"
98
+
99
+ delete "/test/health"
100
+ last_response.status.should == 200
101
+
102
+ LitmusPaper::AgentCheckHandler.handle('test').should == "ready\tup\t88%"
103
+
104
+ sleep 0.25
105
+
106
+ LitmusPaper::AgentCheckHandler.handle('test').should_not == "ready\tup\t88%"
107
+ ensure
108
+ FileUtils.rm_rf(LitmusPaper.cache_location)
109
+ cache = LitmusPaper::AgentCheckHandler.instance_variable_get(:@cache)
110
+ cache.instance_variable_set(:@ttl, -1)
111
+ LitmusPaper::AgentCheckHandler.instance_variable_set(:@cache, cache)
112
+ end
113
+ end
76
114
  end
77
115
  end
@@ -70,5 +70,13 @@ describe LitmusPaper::Cache do
70
70
  sleep ttl
71
71
  cache.get(key).should be_nil
72
72
  end
73
+
74
+ it "works when setting multiple entries" do
75
+ key = "key"
76
+ cache = LitmusPaper::Cache.new(@location, @namespace, 1)
77
+ cache.set(key, "some value")
78
+ cache.set(key, "other value")
79
+ cache.get(key).should == "other value"
80
+ end
73
81
  end
74
82
  end
@@ -8,29 +8,59 @@ describe LitmusPaper::ConfigurationFile do
8
8
  config.services.has_key?('test').should == true
9
9
  end
10
10
 
11
+ it "configures a service via yaml" do
12
+ config_file = LitmusPaper::ConfigurationFile.new(TEST_YAML_CONFIG)
13
+ config = config_file.evaluate
14
+ config.services.has_key?('test').should == true
15
+ end
16
+
11
17
  it "configures the port to listen on" do
12
18
  config_file = LitmusPaper::ConfigurationFile.new(TEST_CONFIG)
13
19
  config = config_file.evaluate
14
20
  config.port.should == 9293
15
21
  end
16
22
 
23
+ it "configures the port to listen on via yaml" do
24
+ config_file = LitmusPaper::ConfigurationFile.new(TEST_YAML_CONFIG)
25
+ config = config_file.evaluate
26
+ config.port.should == 9293
27
+ end
28
+
17
29
  it "configures the data directory" do
18
30
  config_file = LitmusPaper::ConfigurationFile.new(TEST_CONFIG)
19
31
  config = config_file.evaluate
20
32
  config.data_directory.should == "/tmp/litmus_paper"
21
33
  end
22
34
 
35
+ it "configures the data directory via yaml" do
36
+ config_file = LitmusPaper::ConfigurationFile.new(TEST_YAML_CONFIG)
37
+ config = config_file.evaluate
38
+ config.data_directory.should == "/tmp/litmus_paper"
39
+ end
40
+
23
41
  it "configures the cache_location" do
24
42
  config_file = LitmusPaper::ConfigurationFile.new(TEST_CONFIG)
25
43
  config = config_file.evaluate
26
44
  config.cache_location.should == "/tmp/litmus_paper_cache"
27
45
  end
28
46
 
47
+ it "configures the cache_location via yaml" do
48
+ config_file = LitmusPaper::ConfigurationFile.new(TEST_YAML_CONFIG)
49
+ config = config_file.evaluate
50
+ config.cache_location.should == "/tmp/litmus_paper_cache"
51
+ end
52
+
29
53
  it "configures the cache_ttl" do
30
54
  config_file = LitmusPaper::ConfigurationFile.new(TEST_CONFIG)
31
55
  config = config_file.evaluate
32
56
  config.cache_ttl.should == -1
33
57
  end
58
+
59
+ it "configures the cache_ttl via yaml" do
60
+ config_file = LitmusPaper::ConfigurationFile.new(TEST_YAML_CONFIG)
61
+ config = config_file.evaluate
62
+ config.cache_ttl.should == -1
63
+ end
34
64
  end
35
65
 
36
66
  describe "include_files" do
@@ -40,6 +70,19 @@ describe LitmusPaper::ConfigurationFile do
40
70
  config.services.has_key?('test').should == true
41
71
  end
42
72
 
73
+ it "configures a dir glob of services in yaml" do
74
+ config_file = LitmusPaper::ConfigurationFile.new(TEST_YAML_D_CONFIG)
75
+ config = config_file.evaluate
76
+ config.services.has_key?('test').should == true
77
+ end
78
+
79
+ it "configures a dir glob of mixed services" do
80
+ config_file = LitmusPaper::ConfigurationFile.new(TEST_MIXED_D_CONFIG)
81
+ config = config_file.evaluate
82
+ config.services.has_key?('test').should == true
83
+ config.services.has_key?('passing_test').should == true
84
+ end
85
+
43
86
  it "defaults configuration options" do
44
87
  config_file = LitmusPaper::ConfigurationFile.new(TEST_D_CONFIG)
45
88
  config = config_file.evaluate
@@ -47,5 +90,21 @@ describe LitmusPaper::ConfigurationFile do
47
90
  config.port.should == 9292
48
91
  config.data_directory.should == "/etc/litmus"
49
92
  end
93
+
94
+ it "defaults configuration options in yaml" do
95
+ config_file = LitmusPaper::ConfigurationFile.new(TEST_YAML_D_CONFIG)
96
+ config = config_file.evaluate
97
+ config.services.has_key?('test').should == true
98
+ config.port.should == 9292
99
+ config.data_directory.should == "/etc/litmus"
100
+ end
101
+
102
+ it "defaults configuration options in mixed dir" do
103
+ config_file = LitmusPaper::ConfigurationFile.new(TEST_MIXED_D_CONFIG)
104
+ config = config_file.evaluate
105
+ config.services.has_key?('test').should == true
106
+ config.port.should == 9292
107
+ config.data_directory.should == "/etc/litmus"
108
+ end
50
109
  end
51
110
  end
data/spec/spec_helper.rb CHANGED
@@ -8,9 +8,12 @@ require 'tempfile'
8
8
 
9
9
  TEST_CONFIG_DIR = "/tmp/litmus_paper"
10
10
  TEST_CONFIG = File.expand_path('support/test.config', File.dirname(__FILE__))
11
+ TEST_YAML_CONFIG = File.expand_path('support/test.config.yaml', File.dirname(__FILE__))
11
12
  TEST_RELOAD_CONFIG = File.expand_path('support/test.reload.config', File.dirname(__FILE__))
12
13
  TEST_UNICORN_CONFIG = File.expand_path('support/test.unicorn.config', File.dirname(__FILE__))
13
14
  TEST_D_CONFIG = File.expand_path('support/test.d.config', File.dirname(__FILE__))
15
+ TEST_YAML_D_CONFIG = File.expand_path('support/test.d.config.yaml', File.dirname(__FILE__))
16
+ TEST_MIXED_D_CONFIG = File.expand_path('support/test.d.config.mixed', File.dirname(__FILE__))
14
17
  TEST_CA_CERT = File.expand_path('ssl/server.crt', File.dirname(__FILE__))
15
18
 
16
19
  Dir.glob("#{File.expand_path('support', File.dirname(__FILE__))}/**/*.rb").each { |f| require f }
@@ -0,0 +1,5 @@
1
+ # vim: ft=ruby
2
+
3
+ service :passing_test do |s|
4
+ s.measure_health Metric::CPULoad, :weight => 50
5
+ end
@@ -0,0 +1,9 @@
1
+ # vim: ft=ruby
2
+ services:
3
+ test:
4
+ dependencies:
5
+ - type: http
6
+ uri: 'http://localhost/heartbeat'
7
+ checks:
8
+ - type: cpu_load
9
+ weight: 50
@@ -0,0 +1,6 @@
1
+ # vim: ft=ruby
2
+ services:
3
+ passing_test:
4
+ checks:
5
+ - type: cpu_load
6
+ weight: 50
@@ -0,0 +1,9 @@
1
+ # vim: ft=ruby
2
+ services:
3
+ test:
4
+ dependencies:
5
+ - type: http
6
+ uri: 'http://localhost/heartbeat'
7
+ checks:
8
+ - type: cpu_load
9
+ weight: 50
@@ -0,0 +1,20 @@
1
+ # vim: set ft=ruby
2
+ port: 9293
3
+ data_directory: "/tmp/litmus_paper"
4
+ cache_location: "/tmp/litmus_paper_cache"
5
+ cache_ttl: -1
6
+ services:
7
+ test:
8
+ dependencies:
9
+ - type: http
10
+ uri: 'http://localhost/heartbeat'
11
+ checks:
12
+ - type: cpu_load
13
+ weight: 50
14
+ passing_test:
15
+ dependencies:
16
+ - type: script
17
+ command: '/bin/true'
18
+ checks:
19
+ - type: cpu_load
20
+ weight: 50
@@ -0,0 +1,2 @@
1
+ # vim: ft=ruby
2
+ include_files 'config.d.mixed/*.config*'
@@ -0,0 +1,2 @@
1
+ # vim: ft=ruby
2
+ include_files: 'config.d.yaml/*.config.yaml'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: litmus_paper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-01-25 00:00:00.000000000 Z
12
+ date: 2018-04-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sinatra
@@ -153,6 +153,7 @@ files:
153
153
  - .rake_commit
154
154
  - .ruby-version
155
155
  - .travis.yml
156
+ - Dockerfile
156
157
  - Gemfile
157
158
  - LICENSE
158
159
  - README.md
@@ -161,6 +162,8 @@ files:
161
162
  - bin/litmus-agent-check
162
163
  - bin/litmusctl
163
164
  - config.ru
165
+ - docker/litmus.conf
166
+ - docker/litmus_unicorn.rb
164
167
  - lib/litmus_paper.rb
165
168
  - lib/litmus_paper/agent_check_handler.rb
166
169
  - lib/litmus_paper/agent_check_server.rb
@@ -190,6 +193,7 @@ files:
190
193
  - lib/litmus_paper/service.rb
191
194
  - lib/litmus_paper/status_file.rb
192
195
  - lib/litmus_paper/terminal_output.rb
196
+ - lib/litmus_paper/util.rb
193
197
  - lib/litmus_paper/version.rb
194
198
  - litmus-agent-check.init.sh
195
199
  - litmus_paper.gemspec
@@ -219,6 +223,11 @@ files:
219
223
  - spec/ssl/server.crt
220
224
  - spec/ssl/server.key
221
225
  - spec/support/always_available_dependency.rb
226
+ - spec/support/config.d.mixed/.passing_test.config.swp
227
+ - spec/support/config.d.mixed/passing_test.config
228
+ - spec/support/config.d.mixed/test.config.yaml
229
+ - spec/support/config.d.yaml/passing_test.config.yaml
230
+ - spec/support/config.d.yaml/test.config.yaml
222
231
  - spec/support/config.d/passing_test.config
223
232
  - spec/support/config.d/test.config
224
233
  - spec/support/haproxy_test_socket
@@ -227,7 +236,10 @@ files:
227
236
  - spec/support/never_available_dependency.rb
228
237
  - spec/support/stdout_logger.rb
229
238
  - spec/support/test.config
239
+ - spec/support/test.config.yaml
230
240
  - spec/support/test.d.config
241
+ - spec/support/test.d.config.mixed
242
+ - spec/support/test.d.config.yaml
231
243
  - spec/support/test.reload.config
232
244
  - spec/support/test.unicorn.config
233
245
  homepage: https://github.com/braintree/litmus_paper
@@ -250,7 +262,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
250
262
  version: '0'
251
263
  requirements: []
252
264
  rubyforge_project:
253
- rubygems_version: 1.8.24
265
+ rubygems_version: 1.8.23
254
266
  signing_key:
255
267
  specification_version: 3
256
268
  summary: Backend health tester for HA Services, partner project of big_brother
@@ -280,6 +292,11 @@ test_files:
280
292
  - spec/ssl/server.crt
281
293
  - spec/ssl/server.key
282
294
  - spec/support/always_available_dependency.rb
295
+ - spec/support/config.d.mixed/.passing_test.config.swp
296
+ - spec/support/config.d.mixed/passing_test.config
297
+ - spec/support/config.d.mixed/test.config.yaml
298
+ - spec/support/config.d.yaml/passing_test.config.yaml
299
+ - spec/support/config.d.yaml/test.config.yaml
283
300
  - spec/support/config.d/passing_test.config
284
301
  - spec/support/config.d/test.config
285
302
  - spec/support/haproxy_test_socket
@@ -288,6 +305,9 @@ test_files:
288
305
  - spec/support/never_available_dependency.rb
289
306
  - spec/support/stdout_logger.rb
290
307
  - spec/support/test.config
308
+ - spec/support/test.config.yaml
291
309
  - spec/support/test.d.config
310
+ - spec/support/test.d.config.mixed
311
+ - spec/support/test.d.config.yaml
292
312
  - spec/support/test.reload.config
293
313
  - spec/support/test.unicorn.config