bluepill 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +820 -0
  3. data/CONTRIBUTING.md +31 -0
  4. data/LICENSE +1 -1
  5. data/README.md +69 -30
  6. data/bin/bluepill +2 -2
  7. data/bluepill.gemspec +17 -6
  8. data/lib/bluepill/application.rb +311 -43
  9. data/lib/bluepill/condition_watch.rb +105 -10
  10. data/lib/bluepill/controller.rb +125 -14
  11. data/lib/bluepill/dsl/app_proxy.rb +103 -4
  12. data/lib/bluepill/dsl/process_factory.rb +185 -62
  13. data/lib/bluepill/dsl/process_proxy.rb +132 -14
  14. data/lib/bluepill/dsl.rb +27 -5
  15. data/lib/bluepill/group.rb +142 -9
  16. data/lib/bluepill/logger.rb +122 -7
  17. data/lib/bluepill/process.rb +582 -211
  18. data/lib/bluepill/process_commands.rb +262 -0
  19. data/lib/bluepill/process_conditions/always_true.rb +18 -4
  20. data/lib/bluepill/process_conditions/cpu_usage.rb +34 -0
  21. data/lib/bluepill/process_conditions/file_time.rb +40 -4
  22. data/lib/bluepill/process_conditions/http.rb +81 -23
  23. data/lib/bluepill/process_conditions/mem_usage.rb +64 -5
  24. data/lib/bluepill/process_conditions/process_condition.rb +45 -2
  25. data/lib/bluepill/process_conditions/running_time.rb +36 -1
  26. data/lib/bluepill/process_conditions/zombie_process.rb +26 -5
  27. data/lib/bluepill/process_conditions.rb +15 -1
  28. data/lib/bluepill/process_journal.rb +256 -104
  29. data/lib/bluepill/process_statistics.rb +33 -3
  30. data/lib/bluepill/socket.rb +63 -16
  31. data/lib/bluepill/system/commands.rb +286 -0
  32. data/lib/bluepill/system.rb +193 -229
  33. data/lib/bluepill/trigger.rb +111 -15
  34. data/lib/bluepill/triggers/flapping.rb +86 -5
  35. data/lib/bluepill/util/rotational_array.rb +25 -2
  36. data/lib/bluepill/version.rb +71 -19
  37. data/lib/bluepill.rb +25 -2
  38. data/sig/bluepill.rbs +926 -0
  39. data/sig/manifest.yaml +7 -0
  40. metadata +16 -35
  41. data/bin/sample_forking_server +0 -66
  42. data/lib/bluepill/application/client.rb +0 -6
  43. data/lib/bluepill/application/server.rb +0 -21
@@ -1,13 +1,69 @@
1
1
  module Bluepill
2
+ # The triggers a process can have
3
+ #
4
+ # @api private
2
5
  module Triggers
6
+ # Stops a process that keeps restarting, and retries it later
7
+ #
8
+ # A config names it `checks :flapping`, with times: how many starts or
9
+ # restarts count as flapping (5 by default), within: the seconds they
10
+ # must happen in (1 by default), and retry_in: the seconds to wait before
11
+ # starting the process again (5 by default, and 0 never to retry). When
12
+ # the process flaps, the trigger unmonitors it, and starts it again
13
+ # after retry_in.
14
+ #
15
+ # @example
16
+ # process.checks :flapping, times: 2, within: 30.seconds, retry_in: 7.seconds
17
+ #
18
+ # @api private
3
19
  class Flapping < Bluepill::Trigger
4
- TRIGGER_STATES = [:starting, :restarting]
20
+ # The states that a start or a restart moves the process to
21
+ #
22
+ # @api private
23
+ TRIGGER_STATES = [:starting, :restarting].freeze
5
24
 
6
- PARAMS = [:times, :within, :retry_in]
25
+ # The options the trigger takes
26
+ #
27
+ # @api private
28
+ PARAMS = [:times, :within, :retry_in].freeze
7
29
 
8
- attr_accessor(*PARAMS)
30
+ # Return how many starts or restarts count as flapping
31
+ #
32
+ # @return [Integer] the number of transitions
33
+ #
34
+ # @api private
35
+ attr_accessor :times
36
+
37
+ # Return the seconds the starts or restarts must happen in to flap
38
+ #
39
+ # @return [Integer] the seconds
40
+ #
41
+ # @api private
42
+ attr_accessor :within
43
+
44
+ # Return the seconds to wait before starting a flapping process again
45
+ #
46
+ # @return [Integer] the seconds, or 0 never to start it again
47
+ #
48
+ # @api private
49
+ attr_accessor :retry_in
50
+
51
+ # Return when the last starts and restarts happened
52
+ #
53
+ # @return [Util::RotationalArray<Integer>] their times, in seconds
54
+ #
55
+ # @api private
9
56
  attr_reader :timeline
10
57
 
58
+ # Create the trigger for a process
59
+ #
60
+ # @param [Process] process the process to watch
61
+ # @param [Hash{Symbol => Object}] options the trigger's options, with
62
+ # :times, :within, :retry_in and :logger
63
+ #
64
+ # @return [void]
65
+ #
66
+ # @api private
11
67
  def initialize(process, options = {})
12
68
  options.reverse_merge!(times: 5, within: 1, retry_in: 5)
13
69
 
@@ -19,20 +75,45 @@ module Bluepill
19
75
  super
20
76
  end
21
77
 
78
+ # Record a start or a restart, and check whether the process flaps
79
+ #
80
+ # @param [StateMachine::Transition] transition the transition, which
81
+ # the trigger records if it's to starting or restarting
82
+ #
83
+ # @return [void]
84
+ #
85
+ # @api private
22
86
  def notify(transition)
23
87
  return unless TRIGGER_STATES.include?(transition.to_name)
24
- timeline << Time.now.to_i
88
+
89
+ timeline << Integer(Time.now)
25
90
  check_flapping
26
91
  end
27
92
 
93
+ # Forget the recorded starts and cancel the scheduled events
94
+ #
95
+ # @return [void]
96
+ #
97
+ # @api private
28
98
  def reset!
29
99
  @timeline.clear
30
100
  super
31
101
  end
32
102
 
103
+ # Unmonitor the process and schedule its retry, if it's flapping
104
+ #
105
+ # It flaps when the last times starts or restarts happened within
106
+ # within seconds.
107
+ #
108
+ # @return [void]
109
+ #
110
+ # @raise [UncaughtThrowError] :halt, which state_machine catches to stop
111
+ # the transition that flapped
112
+ #
113
+ # @api private
33
114
  def check_flapping
34
115
  # The process has not flapped if we haven't encountered enough incidents
35
- return unless @timeline.compact.length == times
116
+ return unless @timeline.length == times
36
117
 
37
118
  # Check if the incident happend within the timeframe
38
119
  return unless @timeline.last - @timeline.first <= within
@@ -1,19 +1,42 @@
1
1
  module Bluepill
2
+ # Small helpers for the rest of bluepill
3
+ #
4
+ # @api private
2
5
  module Util
6
+ # An array that drops its oldest element when it grows past its capacity
7
+ #
8
+ # Watches keep their last few values in one, and processes their last
9
+ # few events.
10
+ #
11
+ # @api private
3
12
  class RotationalArray < Array
13
+ # Create an empty array that holds at most size elements
14
+ #
15
+ # @param [Integer] size the most elements it holds
16
+ #
17
+ # @return [void]
18
+ #
19
+ # @api private
4
20
  def initialize(size)
5
21
  @capacity = size
6
22
 
7
23
  super() # no size - intentionally
8
24
  end
9
25
 
26
+ # Add an element, dropping the oldest one when it's full
27
+ #
28
+ # @param [Object] value the element to add
29
+ #
30
+ # @return [RotationalArray] self
31
+ #
32
+ # @api private
10
33
  def push(value)
11
- super(value)
34
+ super
12
35
 
13
36
  shift if length > @capacity
14
37
  self
15
38
  end
16
- alias_method :<<, :push
39
+ alias << push
17
40
  end
18
41
  end
19
42
  end
@@ -1,29 +1,67 @@
1
1
  module Bluepill
2
+ # The version of bluepill
3
+ #
4
+ # @example
5
+ # Bluepill::Version.to_s # => "0.1.4"
6
+ #
7
+ # @api public
2
8
  module Version
3
- module_function
4
-
5
- # @return [Integer]
6
- def major
9
+ # Return the major version
10
+ #
11
+ # @example
12
+ # Bluepill::Version.major # => 0
13
+ #
14
+ # @return [Integer] the major version
15
+ #
16
+ # @api public
17
+ def self.major
7
18
  0
8
19
  end
9
20
 
10
- # @return [Integer]
11
- def minor
21
+ # Return the minor version
22
+ #
23
+ # @example
24
+ # Bluepill::Version.minor # => 1
25
+ #
26
+ # @return [Integer] the minor version
27
+ #
28
+ # @api public
29
+ def self.minor
12
30
  1
13
31
  end
14
32
 
15
- # @return [Integer]
16
- def patch
17
- 2
33
+ # Return the patch version
34
+ #
35
+ # @example
36
+ # Bluepill::Version.patch # => 4
37
+ #
38
+ # @return [Integer] the patch version
39
+ #
40
+ # @api public
41
+ def self.patch
42
+ 4
18
43
  end
19
44
 
20
- # @return [Integer, NilClass]
21
- def pre
22
- nil
23
- end
45
+ # Return the pre-release version
46
+ #
47
+ # @example
48
+ # Bluepill::Version.pre # => nil
49
+ #
50
+ # @return [Integer, nil] the pre-release version, or nil for a release
51
+ #
52
+ # @api public
53
+ def self.pre; end
24
54
 
25
- # @return [Hash]
26
- def to_h
55
+ # Return the version's parts by name
56
+ #
57
+ # @example
58
+ # Bluepill::Version.to_h # => {major: 0, minor: 1, patch: 4, pre: nil}
59
+ #
60
+ # @return [Hash{Symbol => Integer, nil}] the major, minor, patch and
61
+ # pre-release versions
62
+ #
63
+ # @api public
64
+ def self.to_h
27
65
  {
28
66
  major: major,
29
67
  minor: minor,
@@ -32,13 +70,27 @@ module Bluepill
32
70
  }
33
71
  end
34
72
 
35
- # @return [Array]
36
- def to_a
73
+ # Return the version's parts, without a missing pre-release version
74
+ #
75
+ # @example
76
+ # Bluepill::Version.to_a # => [0, 1, 4]
77
+ #
78
+ # @return [Array<Integer>] the parts of the version
79
+ #
80
+ # @api public
81
+ def self.to_a
37
82
  to_h.values.compact
38
83
  end
39
84
 
40
- # @return [String]
41
- def to_s
85
+ # Return the version as a string
86
+ #
87
+ # @example
88
+ # Bluepill::Version.to_s # => "0.1.4"
89
+ #
90
+ # @return [String] the version, such as "0.1.4"
91
+ #
92
+ # @api public
93
+ def self.to_s
42
94
  to_a.join('.')
43
95
  end
44
96
  end
data/lib/bluepill.rb CHANGED
@@ -1,10 +1,33 @@
1
1
  require 'rubygems'
2
2
 
3
- require 'bundler/setup' if ENV['BUNDLE_GEMFILE'] && File.exist?(ENV['BUNDLE_GEMFILE'])
3
+ # A process monitor that keeps daemons running
4
+ #
5
+ # A config file calls {Bluepill.application} to describe an application's
6
+ # processes, and the bluepill daemon monitors and restarts them.
7
+ #
8
+ # @api public
9
+ module Bluepill
10
+ # Set up Bundler when bluepill runs with a Gemfile
11
+ #
12
+ # Bluepill calls this once, when it's loaded, so that it runs with the
13
+ # app's bundle, such as under bundle exec.
14
+ #
15
+ # @param [String, nil] gemfile the path of the Gemfile, BUNDLE_GEMFILE by
16
+ # default
17
+ #
18
+ # @return [Boolean, nil] what requiring bundler/setup returned, or nil
19
+ # when there's no Gemfile
20
+ #
21
+ # @api private
22
+ def self.setup_bundler(gemfile = ENV['BUNDLE_GEMFILE'])
23
+ require 'bundler/setup' if gemfile && File.exist?(gemfile)
24
+ end
25
+ end
26
+
27
+ Bluepill.setup_bundler
4
28
 
5
29
  require 'thread'
6
30
  require 'monitor'
7
- require 'syslog'
8
31
  require 'timeout'
9
32
  require 'logger'
10
33