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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +820 -0
- data/CONTRIBUTING.md +31 -0
- data/LICENSE +1 -1
- data/README.md +69 -30
- data/bin/bluepill +2 -2
- data/bluepill.gemspec +17 -6
- data/lib/bluepill/application.rb +311 -43
- data/lib/bluepill/condition_watch.rb +105 -10
- data/lib/bluepill/controller.rb +125 -14
- data/lib/bluepill/dsl/app_proxy.rb +103 -4
- data/lib/bluepill/dsl/process_factory.rb +185 -62
- data/lib/bluepill/dsl/process_proxy.rb +132 -14
- data/lib/bluepill/dsl.rb +27 -5
- data/lib/bluepill/group.rb +142 -9
- data/lib/bluepill/logger.rb +122 -7
- data/lib/bluepill/process.rb +582 -211
- data/lib/bluepill/process_commands.rb +262 -0
- data/lib/bluepill/process_conditions/always_true.rb +18 -4
- data/lib/bluepill/process_conditions/cpu_usage.rb +34 -0
- data/lib/bluepill/process_conditions/file_time.rb +40 -4
- data/lib/bluepill/process_conditions/http.rb +81 -23
- data/lib/bluepill/process_conditions/mem_usage.rb +64 -5
- data/lib/bluepill/process_conditions/process_condition.rb +45 -2
- data/lib/bluepill/process_conditions/running_time.rb +36 -1
- data/lib/bluepill/process_conditions/zombie_process.rb +26 -5
- data/lib/bluepill/process_conditions.rb +15 -1
- data/lib/bluepill/process_journal.rb +256 -104
- data/lib/bluepill/process_statistics.rb +33 -3
- data/lib/bluepill/socket.rb +63 -16
- data/lib/bluepill/system/commands.rb +286 -0
- data/lib/bluepill/system.rb +193 -229
- data/lib/bluepill/trigger.rb +111 -15
- data/lib/bluepill/triggers/flapping.rb +86 -5
- data/lib/bluepill/util/rotational_array.rb +25 -2
- data/lib/bluepill/version.rb +71 -19
- data/lib/bluepill.rb +25 -2
- data/sig/bluepill.rbs +926 -0
- data/sig/manifest.yaml +7 -0
- metadata +16 -35
- data/bin/sample_forking_server +0 -66
- data/lib/bluepill/application/client.rb +0 -6
- 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
|
-
|
|
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
|
-
|
|
25
|
+
# The options the trigger takes
|
|
26
|
+
#
|
|
27
|
+
# @api private
|
|
28
|
+
PARAMS = [:times, :within, :retry_in].freeze
|
|
7
29
|
|
|
8
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
34
|
+
super
|
|
12
35
|
|
|
13
36
|
shift if length > @capacity
|
|
14
37
|
self
|
|
15
38
|
end
|
|
16
|
-
|
|
39
|
+
alias << push
|
|
17
40
|
end
|
|
18
41
|
end
|
|
19
42
|
end
|
data/lib/bluepill/version.rb
CHANGED
|
@@ -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
|
-
|
|
4
|
-
|
|
5
|
-
# @
|
|
6
|
-
|
|
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
|
-
#
|
|
11
|
-
|
|
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
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
#
|
|
26
|
-
|
|
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
|
-
#
|
|
36
|
-
|
|
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
|
-
#
|
|
41
|
-
|
|
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
|
-
|
|
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
|
|