cognizant 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.travis.yml +17 -0
- data/Gemfile +4 -1
- data/{LICENSE → License.md} +4 -2
- data/Rakefile +5 -0
- data/Readme.md +95 -0
- data/bin/cognizant +76 -122
- data/bin/cognizantd +28 -61
- data/cognizant.gemspec +8 -4
- data/examples/apps/redis-server.cz +42 -0
- data/examples/apps/redis-server.yml +29 -0
- data/examples/apps/redis-server_dsl.cz +54 -0
- data/examples/apps/resque.cz +17 -0
- data/examples/apps/thin.cz +32 -0
- data/examples/apps/thin.yml +48 -0
- data/examples/cognizantd.yml +18 -47
- data/features/child_process.feature +62 -0
- data/features/commands.feature +65 -0
- data/features/cpu_usage.feature +45 -0
- data/features/daemon.feature +12 -0
- data/features/flapping.feature +39 -0
- data/features/memory_usage.feature +45 -0
- data/features/shell.feature +30 -0
- data/features/step_definitions/common_steps.rb +14 -0
- data/features/step_definitions/daemon_steps.rb +25 -0
- data/features/step_definitions/shell_steps.rb +96 -0
- data/features/support/env.rb +54 -0
- data/lib/cognizant.rb +1 -5
- data/lib/cognizant/application.rb +122 -0
- data/lib/cognizant/application/dsl_proxy.rb +23 -0
- data/lib/cognizant/client.rb +61 -0
- data/lib/cognizant/commands.rb +164 -0
- data/lib/cognizant/commands/actions.rb +30 -0
- data/lib/cognizant/commands/help.rb +10 -0
- data/lib/cognizant/commands/load.rb +10 -0
- data/lib/cognizant/commands/shutdown.rb +7 -0
- data/lib/cognizant/commands/status.rb +11 -0
- data/lib/cognizant/commands/use.rb +15 -0
- data/lib/cognizant/controller.rb +17 -0
- data/lib/cognizant/daemon.rb +279 -0
- data/lib/cognizant/interface.rb +17 -0
- data/lib/cognizant/log.rb +25 -0
- data/lib/cognizant/process.rb +138 -94
- data/lib/cognizant/process/actions.rb +30 -41
- data/lib/cognizant/process/actions/restart.rb +73 -17
- data/lib/cognizant/process/actions/start.rb +35 -12
- data/lib/cognizant/process/actions/stop.rb +38 -17
- data/lib/cognizant/process/attributes.rb +41 -10
- data/lib/cognizant/process/children.rb +36 -0
- data/lib/cognizant/process/{condition_check.rb → condition_delegate.rb} +11 -13
- data/lib/cognizant/process/conditions.rb +7 -4
- data/lib/cognizant/process/conditions/cpu_usage.rb +5 -6
- data/lib/cognizant/process/conditions/memory_usage.rb +2 -6
- data/lib/cognizant/process/dsl_proxy.rb +23 -0
- data/lib/cognizant/process/execution.rb +16 -9
- data/lib/cognizant/process/pid.rb +16 -6
- data/lib/cognizant/process/status.rb +14 -2
- data/lib/cognizant/process/trigger_delegate.rb +57 -0
- data/lib/cognizant/process/triggers.rb +19 -0
- data/lib/cognizant/process/triggers/flapping.rb +68 -0
- data/lib/cognizant/process/triggers/transition.rb +22 -0
- data/lib/cognizant/process/triggers/trigger.rb +15 -0
- data/lib/cognizant/shell.rb +142 -0
- data/lib/cognizant/system.rb +16 -0
- data/lib/cognizant/system/ps.rb +1 -1
- data/lib/cognizant/system/signal.rb +2 -2
- data/lib/cognizant/util/dsl_proxy_methods_handler.rb +25 -0
- data/lib/cognizant/util/fixnum_percent.rb +5 -0
- data/lib/cognizant/util/transform_hash_keys.rb +33 -0
- data/lib/cognizant/validations.rb +142 -142
- data/lib/cognizant/version.rb +1 -1
- metadata +131 -71
- data/README.md +0 -221
- data/examples/redis-server.rb +0 -28
- data/examples/resque.rb +0 -10
- data/images/logo-small.png +0 -0
- data/images/logo.png +0 -0
- data/images/logo.pxm +0 -0
- data/lib/cognizant/logging.rb +0 -33
- data/lib/cognizant/process/conditions/flapping.rb +0 -57
- data/lib/cognizant/process/conditions/trigger_condition.rb +0 -52
- data/lib/cognizant/server.rb +0 -14
- data/lib/cognizant/server/commands.rb +0 -80
- data/lib/cognizant/server/daemon.rb +0 -277
- data/lib/cognizant/server/interface.rb +0 -86
- data/lib/cognizant/util/symbolize_hash_keys.rb +0 -19
@@ -0,0 +1,17 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
|
3
|
+
require "cognizant/commands"
|
4
|
+
|
5
|
+
module Cognizant
|
6
|
+
class Interface < EventMachine::Connection
|
7
|
+
def post_init
|
8
|
+
end
|
9
|
+
|
10
|
+
def receive_data(args)
|
11
|
+
Cognizant::Commands.process_command(self, args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def unbind
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "logging"
|
2
|
+
|
3
|
+
module Cognizant
|
4
|
+
module Log
|
5
|
+
def self.logger
|
6
|
+
Logging.logger
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.[](name)
|
10
|
+
self.logger[name]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.stdout
|
14
|
+
Logging.appenders.stdout
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.syslog(*args)
|
18
|
+
Logging.appenders.syslog(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.file(*args)
|
22
|
+
Logging.appenders.file(*args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/cognizant/process.rb
CHANGED
@@ -3,13 +3,18 @@ require "thread"
|
|
3
3
|
|
4
4
|
require "state_machine"
|
5
5
|
|
6
|
+
require "cognizant/process/dsl_proxy"
|
6
7
|
require "cognizant/process/pid"
|
7
8
|
require "cognizant/process/status"
|
8
9
|
require "cognizant/process/execution"
|
9
10
|
require "cognizant/process/attributes"
|
10
11
|
require "cognizant/process/actions"
|
11
|
-
require "cognizant/process/
|
12
|
-
require "cognizant/
|
12
|
+
require "cognizant/process/conditions"
|
13
|
+
require "cognizant/process/condition_delegate"
|
14
|
+
require "cognizant/process/triggers"
|
15
|
+
require "cognizant/process/trigger_delegate"
|
16
|
+
require "cognizant/process/children"
|
17
|
+
require "cognizant/util/transform_hash_keys"
|
13
18
|
|
14
19
|
module Cognizant
|
15
20
|
class Process
|
@@ -18,6 +23,7 @@ module Cognizant
|
|
18
23
|
include Cognizant::Process::Execution
|
19
24
|
include Cognizant::Process::Attributes
|
20
25
|
include Cognizant::Process::Actions
|
26
|
+
include Cognizant::Process::Children
|
21
27
|
|
22
28
|
state_machine :initial => :unmonitored do
|
23
29
|
# These are the idle states, i.e. only an event (either external or internal) will trigger a transition.
|
@@ -65,40 +71,69 @@ module Cognizant
|
|
65
71
|
transition any => :unmonitored
|
66
72
|
end
|
67
73
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
before_transition any => :
|
72
|
-
after_transition
|
74
|
+
before_transition any => :starting, :do => lambda { |p| p.autostart = true }
|
75
|
+
after_transition any => :starting, :do => :start_process
|
76
|
+
|
77
|
+
before_transition any => :stopping, :do => lambda { |p| p.autostart = false }
|
78
|
+
after_transition :running => :stopping, :do => :stop_process
|
79
|
+
|
80
|
+
before_transition any => :restarting, :do => lambda { |p| p.autostart = true }
|
81
|
+
after_transition any => :restarting, :do => :restart_process
|
82
|
+
|
83
|
+
before_transition any => :unmonitored, :do => lambda { |p| p.autostart = false }
|
73
84
|
|
74
85
|
before_transition any => any, :do => :notify_triggers
|
75
86
|
after_transition any => any, :do => :record_transition
|
76
87
|
end
|
77
88
|
|
78
|
-
def initialize(process_name = nil,
|
89
|
+
def initialize(process_name = nil, attributes = {}, &block)
|
90
|
+
reset!
|
91
|
+
|
92
|
+
@name = process_name.to_s if process_name
|
93
|
+
|
94
|
+
set_attributes(attributes)
|
95
|
+
|
96
|
+
handle_initialize_block(&block) if block
|
97
|
+
|
98
|
+
raise "Process name is missing. Aborting." unless self.name
|
99
|
+
Log[self].info "Loading process #{self.name}..."
|
100
|
+
|
101
|
+
# Let state_machine initialize as well.
|
102
|
+
initialize_state_machines
|
103
|
+
end
|
104
|
+
|
105
|
+
def handle_initialize_block(&block)
|
106
|
+
if block.arity == 0
|
107
|
+
attributes = Cognizant::Process::DSLProxy.new(self, &block).attributes
|
108
|
+
set_attributes(attributes)
|
109
|
+
else
|
110
|
+
instance_exec(self, &block)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def reset!
|
115
|
+
reset_attributes!
|
116
|
+
|
117
|
+
@application = nil
|
79
118
|
@ticks_to_skip = 0
|
80
|
-
@
|
119
|
+
@conditions = []
|
81
120
|
@triggers = []
|
121
|
+
@children = []
|
82
122
|
@action_mutex = Monitor.new
|
83
|
-
|
84
|
-
|
85
|
-
self.autostart = true # Default.
|
86
|
-
|
87
|
-
if options.has_key?(:checks) and options[:checks].kind_of?(Hash)
|
88
|
-
options[:checks].each do |condition_name, args|
|
89
|
-
self.check(condition_name, args)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
options.delete(:checks)
|
123
|
+
@monitor_children = false
|
124
|
+
end
|
93
125
|
|
94
|
-
|
95
|
-
|
126
|
+
def check(check_name, options, &block)
|
127
|
+
if klass = Cognizant::Process::Conditions[check_name]
|
128
|
+
@conditions << ConditionDelegate.new(check_name, options.deep_symbolize_keys!, &block)
|
129
|
+
elsif klass = Cognizant::Process::Triggers[check_name]
|
130
|
+
@triggers << TriggerDelegate.new(check_name, self, options.deep_symbolize_keys!, &block)
|
96
131
|
end
|
132
|
+
end
|
97
133
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
super
|
134
|
+
def monitor_children(child_process_attributes = {}, &child_process_block)
|
135
|
+
@monitor_children = true
|
136
|
+
@child_process_attributes, @child_process_block = child_process_attributes, child_process_block
|
102
137
|
end
|
103
138
|
|
104
139
|
def tick
|
@@ -108,18 +143,28 @@ module Cognizant
|
|
108
143
|
# Invoke the state_machine event.
|
109
144
|
super
|
110
145
|
|
111
|
-
|
146
|
+
if self.running? # State method.
|
147
|
+
run_conditions
|
148
|
+
|
149
|
+
if @monitor_children
|
150
|
+
refresh_children!
|
151
|
+
@children.each(&:tick)
|
152
|
+
end
|
153
|
+
end
|
112
154
|
end
|
113
155
|
|
114
|
-
def
|
115
|
-
|
116
|
-
|
117
|
-
|
156
|
+
def skip_ticks_for(skips)
|
157
|
+
# Accept negative skips with the result being >= 0.
|
158
|
+
# +1 so that we don't have to >= and ensure 0 in #skip_tick?.
|
159
|
+
@ticks_to_skip = [@ticks_to_skip + (skips.to_i + 1), 0].max
|
160
|
+
end
|
118
161
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
162
|
+
def pidfile
|
163
|
+
@pidfile || File.join(@application.pids_dir, @name + '.pid')
|
164
|
+
end
|
165
|
+
|
166
|
+
def logfile
|
167
|
+
@logfile || File.join(@application.logs_dir, @name + '.log')
|
123
168
|
end
|
124
169
|
|
125
170
|
def last_transition_time
|
@@ -127,11 +172,9 @@ module Cognizant
|
|
127
172
|
end
|
128
173
|
|
129
174
|
def handle_user_command(command)
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
@triggers.each { |trigger| trigger.reset! }
|
134
|
-
end
|
175
|
+
# When the user issues a command, reset any
|
176
|
+
# triggers so that scheduled events gets cleared.
|
177
|
+
@triggers.each { |trigger| trigger.reset! }
|
135
178
|
dispatch!(command, "user initiated")
|
136
179
|
end
|
137
180
|
|
@@ -145,72 +188,42 @@ module Cognizant
|
|
145
188
|
end
|
146
189
|
end
|
147
190
|
|
148
|
-
|
149
|
-
klass = Cognizant::Process::Conditions[condition_name]
|
150
|
-
case klass.superclass.name.split("::").last
|
151
|
-
when "TriggerCondition"
|
152
|
-
@triggers << klass.new(self, options.deep_symbolize_keys!)
|
153
|
-
when "PollCondition"
|
154
|
-
@checks << ConditionCheck.new(condition_name, options.deep_symbolize_keys!, &block)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def notify_triggers(transition)
|
159
|
-
@triggers.each { |trigger| trigger.notify(transition) }
|
160
|
-
end
|
161
|
-
|
162
|
-
def run_checks
|
163
|
-
now = Time.now.to_i
|
191
|
+
private
|
164
192
|
|
165
|
-
|
166
|
-
|
167
|
-
|
193
|
+
def record_transition(transition)
|
194
|
+
unless transition.loopback?
|
195
|
+
@transitioned = true
|
196
|
+
@last_transition_time = Time.now.to_i
|
168
197
|
|
169
|
-
|
198
|
+
# When a process changes state, we should clear the memory of all the conditions.
|
199
|
+
@conditions.each { |condition| condition.clear_history! }
|
200
|
+
Log[self].debug "Changing state of #{name} from #{transition.from_name} => #{transition.to_name}"
|
170
201
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
puts "#{check.condition_name} dispatched: #{thread[:actions].join(',')}"
|
175
|
-
thread[:actions].each do |action|
|
176
|
-
actions << [action, check.to_s]
|
177
|
-
end
|
202
|
+
# And we should re-populate its child list.
|
203
|
+
if @monitor_children
|
204
|
+
@children.clear
|
178
205
|
end
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
self.dispatch!(action, reason)
|
206
|
+
|
207
|
+
# Update the pid from pidfile, since the state of process changed, if the process is managing it's own pidfile.
|
208
|
+
read_pid if @pidfile
|
183
209
|
end
|
184
210
|
end
|
185
211
|
|
186
|
-
def
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
@process_running
|
191
|
-
elsif self.ping_command and run(self.ping_command).succeeded?
|
192
|
-
true
|
193
|
-
elsif pid_running?
|
194
|
-
true
|
195
|
-
else
|
196
|
-
false
|
212
|
+
def set_attributes(attributes)
|
213
|
+
if attributes.has_key?(:checks) and attributes[:checks].kind_of?(Hash)
|
214
|
+
attributes[:checks].each do |check_name, args, &block|
|
215
|
+
check(check_name, args, &block)
|
197
216
|
end
|
198
217
|
end
|
199
|
-
|
200
|
-
|
201
|
-
def pidfile
|
202
|
-
@pidfile = @pidfile || File.join(Cognizant::Server.daemon.pids_dir, self.name + '.pid')
|
203
|
-
end
|
218
|
+
attributes.delete(:checks)
|
204
219
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
private
|
220
|
+
if attributes.has_key?(:monitor_children) and attributes[:monitor_children].kind_of?(Hash)
|
221
|
+
monitor_children(attributes[:monitor_children])
|
222
|
+
end
|
210
223
|
|
211
|
-
|
212
|
-
|
213
|
-
|
224
|
+
attributes.each do |attribute_name, value|
|
225
|
+
self.send("#{attribute_name}=", value) if self.respond_to?("#{attribute_name}=")
|
226
|
+
end
|
214
227
|
end
|
215
228
|
|
216
229
|
def skip_tick?
|
@@ -225,5 +238,36 @@ module Cognizant
|
|
225
238
|
end
|
226
239
|
execute(command, options.merge(action_overrides))
|
227
240
|
end
|
241
|
+
|
242
|
+
def run_conditions
|
243
|
+
now = Time.now.to_i
|
244
|
+
|
245
|
+
threads = @conditions.collect do |condition|
|
246
|
+
[condition, Thread.new { Thread.current[:actions] = condition.run(cached_pid, now) }]
|
247
|
+
end
|
248
|
+
|
249
|
+
@transitioned = false
|
250
|
+
|
251
|
+
collect_conditions_actions(threads).each do |(action, reason)|
|
252
|
+
break if @transitioned
|
253
|
+
dispatch!(action, reason)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def collect_conditions_actions(threads)
|
258
|
+
threads.inject([]) do |actions, (condition, thread)|
|
259
|
+
thread.join
|
260
|
+
thread[:actions].each do |action|
|
261
|
+
action_name = action.respond_to?(:call) ? "call to custom block" : action
|
262
|
+
Log[self].debug "Dispatching #{action_name} to #{name} for #{condition.to_s.strip}."
|
263
|
+
actions << [action, condition.to_s]
|
264
|
+
end
|
265
|
+
actions
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def notify_triggers(transition)
|
270
|
+
@triggers.each { |trigger| trigger.notify(transition) }
|
271
|
+
end
|
228
272
|
end
|
229
273
|
end
|
@@ -12,63 +12,52 @@ module Cognizant
|
|
12
12
|
|
13
13
|
private
|
14
14
|
|
15
|
-
def
|
16
|
-
before_command = options[:before]
|
17
|
-
command = options[:command]
|
18
|
-
after_command = options[:after]
|
19
|
-
signals = options[:signals]
|
20
|
-
timeout = options[:timeout] || 10
|
21
|
-
|
15
|
+
def handle_action(result_handler, options)
|
22
16
|
# TODO: Works well but can some refactoring make it more declarative?
|
23
17
|
@action_thread = Thread.new do
|
24
18
|
result = false
|
25
|
-
queue =
|
26
|
-
thread = Thread.new do
|
27
|
-
if before_command and not success = run(before_command).succeeded?
|
28
|
-
queue.push(success)
|
29
|
-
Thread.exit
|
30
|
-
end
|
19
|
+
queue, thread = execute_action(options)
|
31
20
|
|
32
|
-
|
33
|
-
|
34
|
-
queue.push(success)
|
35
|
-
Thread.exit
|
36
|
-
end
|
37
|
-
|
38
|
-
# If the caller has attempted to set signals, then it can handle it's result.
|
39
|
-
if success = send_signals(signals: signals, timeout: timeout)
|
40
|
-
run(after_command) if after_command
|
41
|
-
queue.push(success)
|
42
|
-
Thread.exit
|
43
|
-
end
|
44
|
-
|
45
|
-
queue.push(false)
|
46
|
-
Thread.exit
|
47
|
-
end
|
48
|
-
|
49
|
-
timeout_left = timeout + 1
|
50
|
-
while (timeout_left -= 1) > 0 do
|
21
|
+
time_left = options[:timeout]
|
22
|
+
while time_left >= 0 do
|
51
23
|
# If there is something in the queue, we have the required result.
|
52
|
-
|
53
|
-
|
54
|
-
skip_ticks_for(-timeout_left)
|
24
|
+
unless queue.empty?
|
25
|
+
result = queue.pop
|
55
26
|
break
|
56
27
|
end
|
57
28
|
sleep 1
|
29
|
+
time_left -= 1
|
58
30
|
end
|
59
31
|
|
60
32
|
# Kill the nested thread.
|
61
33
|
thread.kill
|
62
34
|
|
63
35
|
# Action callback.
|
64
|
-
|
65
|
-
|
66
|
-
# Kill self.
|
67
|
-
Thread.kill
|
36
|
+
self.send(result_handler, result, time_left) if result_handler.present? and self.respond_to?(result_handler)
|
68
37
|
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def execute_action(options)
|
41
|
+
before_command = options[:before]
|
42
|
+
command = options[:command]
|
43
|
+
after_command = options[:after]
|
44
|
+
signals = options[:signals]
|
45
|
+
timeout = options[:timeout]
|
69
46
|
|
70
|
-
|
71
|
-
|
47
|
+
queue = Queue.new
|
48
|
+
thread = Thread.new do
|
49
|
+
# If before_command succeeds, we move to the next command.
|
50
|
+
(before_command and not success = run(before_command).succeeded?) or
|
51
|
+
# If the command is available and it succeeds, we stop here.
|
52
|
+
(command and success = run(command, options) and success.succeeded?) or
|
53
|
+
# As a last try, check for signals. If the action has set signals, then it can handle its result.
|
54
|
+
(success = send_signals(signals: signals, timeout: timeout))
|
55
|
+
|
56
|
+
run(after_command) if success and after_command
|
57
|
+
queue.push(success)
|
58
|
+
Thread.exit
|
59
|
+
end
|
60
|
+
return queue, thread
|
72
61
|
end
|
73
62
|
|
74
63
|
def send_signals(options = {})
|
@@ -14,45 +14,101 @@ module Cognizant
|
|
14
14
|
# The command to restart the process with. This command can optionally
|
15
15
|
# be similar in behavior to the stop command, since the process will
|
16
16
|
# anyways be automatically started again, if autostart is set to true.
|
17
|
+
# Also see restart_expect variable.
|
17
18
|
# @return [String] Defaults to nil
|
18
19
|
attr_accessor :restart_command
|
19
20
|
|
20
21
|
# The signals to pass to the process one by one attempting to restart
|
21
22
|
# it. Each signal is passed within the timeout period over equally
|
22
|
-
#
|
23
|
+
# divided intervals (min. 2 seconds). Override with signals without
|
23
24
|
# "KILL" to never force kill the process.
|
25
|
+
# Also see restart_expect variable.
|
24
26
|
# e.g. ["TERM", "INT"]
|
25
|
-
# @return [Array] Defaults to ["
|
27
|
+
# @return [Array] Defaults to ["QUIT", "TERM", "INT"]
|
26
28
|
attr_accessor :restart_signals
|
27
29
|
|
30
|
+
# Whether or not the process is expected to stop itself after the
|
31
|
+
# restart action is executed. If, upon restart action, the process will
|
32
|
+
# stop but not start again by itself, it should be set to true. If the
|
33
|
+
# process will start again within the timeout period, it should be set
|
34
|
+
# to false. For convenience, it defaults to false, if restart_command
|
35
|
+
# or restart_signals are set, as the restart action is then expected to
|
36
|
+
# start itself after a stop.
|
37
|
+
# @return [true, false] Defaults to !restart_command.present? ||
|
38
|
+
# !restart_signals.present?
|
39
|
+
attr_accessor :restart_expect_stopped
|
40
|
+
|
28
41
|
# The grace time period in seconds for the process to stop within
|
29
42
|
# (for restart). Covers the time period for the restart command or
|
30
43
|
# signals. After the timeout is over, the process is checked for
|
31
44
|
# running status and if not stopped, it re-enters the auto start
|
32
|
-
# lifecycle based on conditions.
|
33
|
-
#
|
45
|
+
# lifecycle based on conditions. Timeout of request has the same effect
|
46
|
+
# as :stopped setting :restart_expect.
|
47
|
+
# @return [String] Defaults to 30
|
34
48
|
attr_accessor :restart_timeout
|
35
49
|
|
36
50
|
# The command to run after the process is restarted.
|
37
51
|
# @return [String] Defaults to nil
|
38
52
|
attr_accessor :restart_after_command
|
39
53
|
|
54
|
+
def restart_expect_stopped!
|
55
|
+
self.restart_expect_stopped = true
|
56
|
+
end
|
57
|
+
|
58
|
+
def restart_expect_stopped
|
59
|
+
unless !!@restart_expect_stopped == @restart_expect_stopped
|
60
|
+
@restart_expect_stopped = !(self.restart_command.present? or self.restart_signals.present?)
|
61
|
+
end
|
62
|
+
@restart_expect_stopped
|
63
|
+
end
|
64
|
+
|
65
|
+
def reset_attributes!
|
66
|
+
self.restart_env = {}
|
67
|
+
self.restart_before_command = nil
|
68
|
+
self.restart_command = nil
|
69
|
+
self.restart_signals = nil
|
70
|
+
self.restart_expect_stopped = nil
|
71
|
+
self.restart_timeout = 30
|
72
|
+
self.restart_after_command = nil
|
73
|
+
super
|
74
|
+
end
|
75
|
+
|
40
76
|
def restart_process
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
77
|
+
# We skip so that we're not reinformed about the required transition by the tick.
|
78
|
+
skip_ticks_for(self.restart_timeout)
|
79
|
+
|
80
|
+
options = {
|
81
|
+
env: self.env.merge(self.restart_env),
|
82
|
+
before: self.restart_before_command,
|
83
|
+
command: self.restart_command,
|
84
|
+
signals: self.restart_signals || ["QUIT", "TERM", "INT"],
|
85
|
+
after: self.restart_after_command,
|
86
|
+
timeout: self.restart_timeout
|
87
|
+
}
|
88
|
+
handle_action('_restart_result_handler', options)
|
89
|
+
end
|
90
|
+
|
91
|
+
# @private
|
92
|
+
def _restart_result_handler(result, time_left = 0)
|
93
|
+
# If it is a boolean and value is true OR if it's an execution result and it succeeded.
|
94
|
+
if (!!result == result and result) or (result.respond_to?(:succeeded?) and result.succeeded?)
|
95
|
+
unlink_pid if not pid_running? and self.daemonize
|
96
|
+
|
97
|
+
# We are not resetting @process_pid here to give process a second of grace period.
|
98
|
+
|
99
|
+
unless self.restart_expect_stopped
|
100
|
+
while (time_left >= 0 and not process_running?) do
|
101
|
+
sleep 1
|
102
|
+
time_left -= 1
|
103
|
+
@process_pid = nil
|
104
|
+
end
|
45
105
|
end
|
106
|
+
else
|
107
|
+
@process_pid = nil
|
46
108
|
end
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
before: self.restart_before_command,
|
51
|
-
command: self.restart_command,
|
52
|
-
signals: self.restart_signals,
|
53
|
-
after: self.restart_after_command,
|
54
|
-
timeout: self.restart_timeout
|
55
|
-
)
|
109
|
+
|
110
|
+
# Rollback the pending skips.
|
111
|
+
skip_ticks_for(-time_left) if time_left > 0
|
56
112
|
end
|
57
113
|
end
|
58
114
|
end
|