sensu 0.18.1 → 0.19.0.beta

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.
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