sensu 0.18.1 → 0.19.0.beta

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0c4b6e88fad200ce4a66173e385728a6107d62cc
4
- data.tar.gz: de4f574f36b4e060a6c8135afbd5186e4a9ef96b
3
+ metadata.gz: 4b4319c7b86de73fef4f4649e98a5d44639d195b
4
+ data.tar.gz: 5bbd3e47ec44b43845edfdd0fd4eac1c07b3b4d5
5
5
  SHA512:
6
- metadata.gz: 187cf3e05aeb12dfacdad053acb5433aa5d41c402d7627b14212fa30ecd34138d2df793c8bdb580ca9bb5190cabb2f6014469ceebfd42ab68b08af554b8aa297
7
- data.tar.gz: 0dbe79dbbfdb6138ebf0faa47d3a5e22a704011c99fbd05699cec64f9496213ec0d729c26ba83666cc6390642655e1428ffa4599508242c57b70ab85585f89dd
6
+ metadata.gz: 2aad30b0391244ae28bab870fda5fd5f830fba3765eed6eb74648a504a2cb0317b195c427ebe4a191b73f7b54cabbb7c10331ffb17b5ff5356ac31a961b17ed3
7
+ data.tar.gz: 9a870dcc2e466b4c0865c72c78552115dbd71059812ec4d8026461adfb7635d027de16ad297fe5d175791e990c41304e7ac569b4a504b5c60a8f11ea1d7e6a2b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ ## 0.19.0 - TBD
2
+
3
+ ### Features
4
+
5
+ Redis Sensu transport, a built-in alternative to the default RabbitMQ
6
+ transport. The Redis transport is currently considered experimental.
7
+ Configuring the transport name to be `redis` will enable the Redis
8
+ transport instead of RabbitMQ, e.g. `{"transport": {"name": "redis"}}`.
9
+
10
+ Round-robin client subscriptions, allowing check requests to be sent to a
11
+ single client in a subscription in a round-robin fashion. To create a
12
+ round-robin subscription, start its name with `roundrobin:` to specify the
13
+ type, e.g. "roundrobin:elasticsearch". Any check that targets the
14
+ "roundrobin:elasticsearch" subscription will have its check requests sent
15
+ to clients in a round-robin fashion.
16
+
17
+ Stale check result detection, using a defined check `ttl` and stored check
18
+ results. Sensu is now able to monitor check results, ensuring that checks
19
+ with a defined TTL (time to live) continue to be executed by clients. For
20
+ example, a standalone check could have an interval of 30 seconds and a ttl
21
+ of 50 seconds, Sensu would expect a result at least once every 50 seconds.
22
+
23
+ Check results API routes/endpoints: `/results`, `/results/:client`, and
24
+ `/results/:client/:check`. These new check result API routes/endpoints
25
+ enable new tooling, such as green light dashboards.
26
+
1
27
  ## 0.18.1 - 2015-05-11
2
28
 
3
29
  ### Other
@@ -725,6 +725,67 @@ module Sensu
725
725
  end
726
726
  end
727
727
  end
728
+
729
+ aget "/results/?" do
730
+ response = Array.new
731
+ settings.redis.smembers("clients") do |clients|
732
+ unless clients.empty?
733
+ clients.each_with_index do |client_name, client_index|
734
+ settings.redis.smembers("result:#{client_name}") do |checks|
735
+ unless checks.empty?
736
+ checks.each_with_index do |check_name, check_index|
737
+ result_key = "result:#{client_name}:#{check_name}"
738
+ settings.redis.get(result_key) do |result_json|
739
+ check = MultiJson.load(result_json)
740
+ response << {:client => client_name, :check => check}
741
+ if client_index == clients.size - 1 && check_index == checks.size - 1
742
+ body MultiJson.dump(response)
743
+ end
744
+ end
745
+ end
746
+ else
747
+ body MultiJson.dump(response)
748
+ end
749
+ end
750
+ end
751
+ else
752
+ body MultiJson.dump(response)
753
+ end
754
+ end
755
+ end
756
+
757
+ aget %r{^/results?/([\w\.-]+)/?$} do |client_name|
758
+ response = Array.new
759
+ settings.redis.smembers("result:#{client_name}") do |checks|
760
+ unless checks.empty?
761
+ checks.each_with_index do |check_name, check_index|
762
+ result_key = "result:#{client_name}:#{check_name}"
763
+ settings.redis.get(result_key) do |result_json|
764
+ check = MultiJson.load(result_json)
765
+ response << {:client => client_name, :check => check}
766
+ if check_index == checks.size - 1
767
+ body MultiJson.dump(response)
768
+ end
769
+ end
770
+ end
771
+ else
772
+ not_found!
773
+ end
774
+ end
775
+ end
776
+
777
+ aget %r{^/results?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
778
+ result_key = "result:#{client_name}:#{check_name}"
779
+ settings.redis.get(result_key) do |result_json|
780
+ unless result_json.nil?
781
+ check = MultiJson.load(result_json)
782
+ response = {:client => client_name, :check => check}
783
+ body MultiJson.dump(response)
784
+ else
785
+ not_found!
786
+ end
787
+ end
788
+ end
728
789
  end
729
790
  end
730
791
  end
@@ -233,20 +233,42 @@ module Sensu
233
233
  end
234
234
  end
235
235
 
236
+ # Determine the Sensu transport subscribe options for a
237
+ # subscription. If a subscription begins with a transport pipe
238
+ # type, either "direct:" or "roundrobin:", the subscription uses
239
+ # a direct transport pipe, and the subscription name is used for
240
+ # both the pipe and the funnel names. If a subscription does not
241
+ # specify a transport pipe type, a fanout transport pipe is
242
+ # used, the subscription name is used for the pipe, and a unique
243
+ # funnel is created for the Sensu client. The unique funnel name
244
+ # for the Sensu client is created using a combination of the
245
+ # client name, the Sensu version, and the process start time
246
+ # (epoch).
247
+ #
248
+ # @param subscription [String]
249
+ # @return [Array] containing the transport subscribe options:
250
+ # the transport pipe type, pipe, and funnel.
251
+ def transport_subscribe_options(subscription)
252
+ _, raw_type = subscription.split(":", 2).reverse
253
+ case raw_type
254
+ when "direct", "roundrobin"
255
+ [:direct, subscription, subscription]
256
+ else
257
+ funnel = [@settings[:client][:name], VERSION, start_time].join("-")
258
+ [:fanout, subscription, funnel]
259
+ end
260
+ end
261
+
236
262
  # Set up Sensu client subscriptions. Subscriptions determine the
237
- # kinds of check requests the client will receive. A unique
238
- # transport funnel is created for the Sensu client, using a
239
- # combination of it's name, the Sensu version, and the current
240
- # timestamp (epoch). The unique funnel is bound to each
241
- # transport pipe, named after the client subscription. The Sensu
263
+ # kinds of check requests the client will receive. The Sensu
242
264
  # client will receive JSON serialized check requests from its
243
- # funnel, that get parsed and processed.
265
+ # subscriptions, that get parsed and processed.
244
266
  def setup_subscriptions
245
267
  @logger.debug("subscribing to client subscriptions")
246
268
  @settings[:client][:subscriptions].each do |subscription|
247
269
  @logger.debug("subscribing to a subscription", :subscription => subscription)
248
- funnel = [@settings[:client][:name], VERSION, Time.now.to_i].join("-")
249
- @transport.subscribe(:fanout, subscription, funnel) do |message_info, message|
270
+ options = transport_subscribe_options(subscription)
271
+ @transport.subscribe(*options) do |message_info, message|
250
272
  begin
251
273
  check = MultiJson.load(message)
252
274
  @logger.info("received check request", :check => check)
@@ -1,7 +1,7 @@
1
1
  module Sensu
2
2
  unless defined?(Sensu::VERSION)
3
3
  # Sensu release version.
4
- VERSION = "0.18.1"
4
+ VERSION = "0.19.0.beta"
5
5
 
6
6
  # Sensu check severities.
7
7
  SEVERITIES = %w[ok warning critical unknown]
data/lib/sensu/daemon.rb CHANGED
@@ -4,10 +4,10 @@ gem "multi_json", "1.11.0"
4
4
 
5
5
  gem "sensu-em", "2.4.1"
6
6
  gem "sensu-logger", "1.0.0"
7
- gem "sensu-settings", "1.8.0"
7
+ gem "sensu-settings", "1.9.0"
8
8
  gem "sensu-extension", "1.1.2"
9
9
  gem "sensu-extensions", "1.2.0"
10
- gem "sensu-transport", "2.4.0"
10
+ gem "sensu-transport", "3.0.0"
11
11
  gem "sensu-spawn", "1.1.0"
12
12
 
13
13
  require "time"
@@ -31,13 +31,16 @@ module Sensu
31
31
  module Daemon
32
32
  include Utilities
33
33
 
34
- # Initialize the Sensu process. Set the initial service state, set
35
- # up the logger, load settings, load extensions, and optionally
36
- # daemonize the process and/or create a PID file. A subclass may
37
- # override this method.
34
+ attr_reader :start_time
35
+
36
+ # Initialize the Sensu process. Set the start time, initial
37
+ # service state, set up the logger, load settings, load
38
+ # extensions, and optionally daemonize the process and/or create a
39
+ # PID file. A subclass may override this method.
38
40
  #
39
41
  # @param options [Hash]
40
42
  def initialize(options={})
43
+ @start_time = Time.now.to_i
41
44
  @state = :initializing
42
45
  @timers = {:run => []}
43
46
  setup_logger(options)
data/lib/sensu/redis.rb CHANGED
@@ -1,6 +1,6 @@
1
- gem "em-redis-unified", "0.6.0"
1
+ gem "em-redis-unified", "1.0.0"
2
2
 
3
- require "em-redis"
3
+ require "em-redis-unified"
4
4
 
5
5
  module Sensu
6
6
  class Redis
@@ -436,6 +436,26 @@ module Sensu
436
436
  end
437
437
  end
438
438
 
439
+ # Determine the Sensu transport publish options for a
440
+ # subscription. If a subscription begins with a transport pipe
441
+ # type, either "direct:" or "roundrobin:", the subscription uses
442
+ # a direct transport pipe. If a subscription does not specify a
443
+ # transport pipe type, a fanout transport pipe is used.
444
+ #
445
+ # @param subscription [String]
446
+ # @return [Array] containing the transport publish options:
447
+ # the transport pipe type, pipe, and the message to be
448
+ # published.
449
+ def transport_publish_options(subscription, message)
450
+ _, raw_type = subscription.split(":", 2).reverse
451
+ case raw_type
452
+ when "direct", "roundrobin"
453
+ [:direct, subscription, message]
454
+ else
455
+ [:fanout, subscription, message]
456
+ end
457
+ end
458
+
439
459
  # Publish a check request to the transport. A check request is
440
460
  # composted of a check `:name`, an `:issued` timestamp, and a
441
461
  # check `:command` if available. The check request is published
@@ -456,7 +476,8 @@ module Sensu
456
476
  :subscribers => check[:subscribers]
457
477
  })
458
478
  check[:subscribers].each do |subscription|
459
- @transport.publish(:fanout, subscription, MultiJson.dump(payload)) do |info|
479
+ options = transport_publish_options(subscription, MultiJson.dump(payload))
480
+ @transport.publish(*options) do |info|
460
481
  if info[:error]
461
482
  @logger.error("failed to publish check request", {
462
483
  :subscription => subscription,
@@ -527,11 +548,11 @@ module Sensu
527
548
  # serialization is used when publishing the check result payload
528
549
  # to the transport pipe. Transport errors are logged.
529
550
  #
530
- # @param client [Hash]
551
+ # @param client_name [String]
531
552
  # @param check [Hash]
532
- def publish_check_result(client, check)
553
+ def publish_check_result(client_name, check)
533
554
  payload = {
534
- :client => client[:name],
555
+ :client => client_name,
535
556
  :check => check
536
557
  }
537
558
  @logger.debug("publishing check result", :payload => payload)
@@ -606,7 +627,7 @@ module Sensu
606
627
  check[:output] << "#{time_since_last_keepalive} seconds ago"
607
628
  check[:status] = 0
608
629
  end
609
- publish_check_result(client, check)
630
+ publish_check_result(client[:name], check)
610
631
  end
611
632
  end
612
633
  end
@@ -623,6 +644,50 @@ module Sensu
623
644
  end
624
645
  end
625
646
 
647
+ # Determine stale check results, those that have not executed in
648
+ # a specified amount of time (check TTL). This method iterates
649
+ # through the client registry and check results for checks with
650
+ # a defined TTL value (in seconds). If a check result has a
651
+ # defined TTL, the time since last check execution (in seconds)
652
+ # is calculated. If the time since last execution is equal to or
653
+ # greater than the check TTL, a warning check result is
654
+ # published with the appropriate check output.
655
+ def determine_stale_check_results
656
+ @logger.info("determining stale check results")
657
+ @redis.smembers("clients") do |clients|
658
+ clients.each do |client_name|
659
+ @redis.smembers("result:#{client_name}") do |checks|
660
+ checks.each do |check_name|
661
+ result_key = "#{client_name}:#{check_name}"
662
+ @redis.get("result:#{result_key}") do |result_json|
663
+ unless result_json.nil?
664
+ check = MultiJson.load(result_json)
665
+ next unless check[:ttl] && check[:executed]
666
+ time_since_last_execution = Time.now.to_i - check[:executed]
667
+ if time_since_last_execution >= check[:ttl]
668
+ check[:output] = "Last check execution was "
669
+ check[:output] << "#{time_since_last_execution} seconds ago"
670
+ check[:status] = 1
671
+ publish_check_result(client_name, check)
672
+ end
673
+ end
674
+ end
675
+ end
676
+ end
677
+ end
678
+ end
679
+ end
680
+
681
+ # Set up the check result monitor, a periodic timer to run
682
+ # `determine_stale_check_results()` every 30 seconds. The timer
683
+ # is stored in the timers hash under `:leader`.
684
+ def setup_check_result_monitor
685
+ @logger.debug("monitoring check results")
686
+ @timers[:leader] << EM::PeriodicTimer.new(30) do
687
+ determine_stale_check_results
688
+ end
689
+ end
690
+
626
691
  # Prune check result aggregations (aggregates). Sensu only
627
692
  # stores the 20 latest aggregations for a check, to keep the
628
693
  # amount of data stored to a minimum.
@@ -672,6 +737,7 @@ module Sensu
672
737
  def leader_duties
673
738
  setup_check_request_publisher
674
739
  setup_client_monitor
740
+ setup_check_result_monitor
675
741
  setup_check_result_aggregation_pruner
676
742
  end
677
743
 
data/sensu.gemspec CHANGED
@@ -19,12 +19,12 @@ Gem::Specification.new do |s|
19
19
  s.add_dependency "eventmachine", "1.0.3"
20
20
  s.add_dependency "sensu-em", "2.4.1"
21
21
  s.add_dependency "sensu-logger", "1.0.0"
22
- s.add_dependency "sensu-settings", "1.8.0"
22
+ s.add_dependency "sensu-settings", "1.9.0"
23
23
  s.add_dependency "sensu-extension", "1.1.2"
24
24
  s.add_dependency "sensu-extensions", "1.2.0"
25
- s.add_dependency "sensu-transport", "2.4.0"
25
+ s.add_dependency "sensu-transport", "3.0.0"
26
26
  s.add_dependency "sensu-spawn", "1.1.0"
27
- s.add_dependency "em-redis-unified", "0.6.0"
27
+ s.add_dependency "em-redis-unified", "1.0.0"
28
28
  s.add_dependency "sinatra", "1.4.6"
29
29
  s.add_dependency "async_sinatra", "1.2.0"
30
30
  s.add_dependency "thin", "1.5.0" unless RUBY_PLATFORM =~ /java/
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sensu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.1
4
+ version: 0.19.0.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Porter
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-05-12 00:00:00.000000000 Z
12
+ date: 2015-05-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -87,14 +87,14 @@ dependencies:
87
87
  requirements:
88
88
  - - '='
89
89
  - !ruby/object:Gem::Version
90
- version: 1.8.0
90
+ version: 1.9.0
91
91
  type: :runtime
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
95
  - - '='
96
96
  - !ruby/object:Gem::Version
97
- version: 1.8.0
97
+ version: 1.9.0
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: sensu-extension
100
100
  requirement: !ruby/object:Gem::Requirement
@@ -129,14 +129,14 @@ dependencies:
129
129
  requirements:
130
130
  - - '='
131
131
  - !ruby/object:Gem::Version
132
- version: 2.4.0
132
+ version: 3.0.0
133
133
  type: :runtime
134
134
  prerelease: false
135
135
  version_requirements: !ruby/object:Gem::Requirement
136
136
  requirements:
137
137
  - - '='
138
138
  - !ruby/object:Gem::Version
139
- version: 2.4.0
139
+ version: 3.0.0
140
140
  - !ruby/object:Gem::Dependency
141
141
  name: sensu-spawn
142
142
  requirement: !ruby/object:Gem::Requirement
@@ -157,14 +157,14 @@ dependencies:
157
157
  requirements:
158
158
  - - '='
159
159
  - !ruby/object:Gem::Version
160
- version: 0.6.0
160
+ version: 1.0.0
161
161
  type: :runtime
162
162
  prerelease: false
163
163
  version_requirements: !ruby/object:Gem::Requirement
164
164
  requirements:
165
165
  - - '='
166
166
  - !ruby/object:Gem::Version
167
- version: 0.6.0
167
+ version: 1.0.0
168
168
  - !ruby/object:Gem::Dependency
169
169
  name: sinatra
170
170
  requirement: !ruby/object:Gem::Requirement
@@ -297,9 +297,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
297
297
  version: '0'
298
298
  required_rubygems_version: !ruby/object:Gem::Requirement
299
299
  requirements:
300
- - - ">="
300
+ - - ">"
301
301
  - !ruby/object:Gem::Version
302
- version: '0'
302
+ version: 1.3.1
303
303
  requirements: []
304
304
  rubyforge_project:
305
305
  rubygems_version: 2.2.2