litmus_paper 1.1.1 → 1.2.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/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