evented_bluepill 0.0.46 → 0.0.47

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,9 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  module Bluepill
2
4
  module Application
3
5
  module Client
4
-
6
+
5
7
  end
6
8
  end
7
9
  end
@@ -1,7 +1,9 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  module Bluepill
2
4
  module Application
3
5
  module ServerMethods
4
-
6
+
5
7
  def status
6
8
  buffer = ""
7
9
  self.processes.each do | process |
@@ -11,12 +13,12 @@ module Bluepill
11
13
  end
12
14
 
13
15
  def restart
14
- self.socket = Bluepill::Socket.new(name, base_dir).client
16
+ self.socket = Bluepill::Socket.new(name, base_dir).client
15
17
  socket.send("restart\n", 0)
16
18
  end
17
19
 
18
20
  def stop
19
- self.socket = Bluepill::Socket.new(name, base_dir).client
21
+ self.socket = Bluepill::Socket.new(name, base_dir).client
20
22
  socket.send("stop\n", 0)
21
23
  end
22
24
  end
@@ -1,48 +1,54 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'coolio'
4
+
1
5
  module Bluepill
2
- class ConditionWatch
3
- attr_accessor :logger, :name
4
- EMPTY_ARRAY = [].freeze # no need to recreate one every tick
5
-
6
- def initialize(name, options = {})
6
+ class ConditionWatch < Coolio::TimerWatcher
7
+ attr_accessor :logger, :name, :process
8
+ attr_reader :every, :process_condition, :fires
9
+
10
+ def initialize(name, process, options = {})
7
11
  @name = name
8
12
 
9
- @logger = options.delete(:logger)
10
- @fires = options.has_key?(:fires) ? Array(options.delete(:fires)) : [:restart]
13
+ @logger = options.delete(:logger)
14
+ @fires = options.delete(:fires) || :restart
11
15
  @every = options.delete(:every)
12
16
  @times = options.delete(:times) || [1,1]
13
17
  @times = [@times, @times] unless @times.is_a?(Array) # handles :times => 5
18
+ @process = process
14
19
 
15
20
  self.clear_history!
16
-
21
+
17
22
  @process_condition = ProcessConditions[@name].new(options)
23
+
24
+ super(@every, true)
18
25
  end
19
-
20
- def run(pid, tick_number = Time.now.to_i)
21
- if @last_ran_at.nil? || (@last_ran_at + @every) <= tick_number
22
- @last_ran_at = tick_number
23
- self.record_value(@process_condition.run(pid))
24
- return @fires if self.fired?
26
+
27
+ def on_timer
28
+ self.record_value(self.process_condition.run(@process.actual_pid))
29
+ if self.fired?
30
+ @process.dispatch!(self.fires, self.to_s)
31
+ logger.info "#{self.name} dispatched: #{self.fires}"
25
32
  end
26
- EMPTY_ARRAY
27
33
  end
28
-
34
+
29
35
  def record_value(value)
30
36
  # TODO: record value in ProcessStatistics
31
37
  @history[@history_index] = [value, @process_condition.check(value)]
32
38
  @history_index = (@history_index + 1) % @history.size
33
39
  self.logger.info(self.to_s)
34
40
  end
35
-
41
+
36
42
  def clear_history!
37
43
  @last_ran_at = nil
38
44
  @history = Array.new(@times[1])
39
45
  @history_index = 0
40
46
  end
41
-
47
+
42
48
  def fired?
43
49
  @history.select {|v| v && !v[1]}.size >= @times[0]
44
50
  end
45
-
51
+
46
52
  def to_s
47
53
  # TODO: this will be out of order because of the way history values are assigned
48
54
  # use (@history[(@history_index - 1)..1] + @history[0..(@history_index - 1)]).
@@ -1,23 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  require 'fileutils'
2
4
 
3
5
  module Bluepill
4
6
  class Controller
5
7
  attr_accessor :base_dir, :log_file, :sockets_dir, :pids_dir
6
-
8
+
7
9
  def initialize(options = {})
8
10
  self.log_file = options[:log_file]
9
11
  self.base_dir = options[:base_dir]
10
12
  self.sockets_dir = File.join(base_dir, 'socks')
11
13
  self.pids_dir = File.join(base_dir, 'pids')
12
-
14
+
13
15
  setup_dir_structure
14
16
  cleanup_bluepill_directory
15
17
  end
16
-
18
+
17
19
  def running_applications
18
20
  Dir[File.join(sockets_dir, "*.sock")].map{|x| File.basename(x, ".sock")}
19
21
  end
20
-
22
+
21
23
  def handle_command(application, command, *args)
22
24
  case command.to_sym
23
25
  when :status
@@ -44,10 +46,10 @@ module Bluepill
44
46
  when :log
45
47
  log_file_location = self.send_to_daemon(application, :log_file)
46
48
  log_file_location = self.log_file if log_file_location.to_s.strip.empty?
47
-
49
+
48
50
  requested_pattern = args.first
49
51
  grep_pattern = self.grep_pattern(application, requested_pattern)
50
-
52
+
51
53
  tail = "tail -n 100 -f #{log_file_location} | grep -E '#{grep_pattern}'"
52
54
  puts "Tailing log for #{requested_pattern}..."
53
55
  Kernel.exec(tail)
@@ -56,7 +58,7 @@ module Bluepill
56
58
  exit(1)
57
59
  end
58
60
  end
59
-
61
+
60
62
  def send_to_daemon(application, command, *args)
61
63
  begin
62
64
  verify_version!(application)
@@ -76,13 +78,13 @@ module Bluepill
76
78
  abort("Connection Refused: Server is not running")
77
79
  end
78
80
  end
79
-
81
+
80
82
  def grep_pattern(application, query = nil)
81
83
  pattern = [application, query].compact.join(':')
82
84
  ['\[.*', Regexp.escape(pattern), '.*'].compact.join
83
85
  end
84
86
  private
85
-
87
+
86
88
  def cleanup_bluepill_directory
87
89
  self.running_applications.each do |app|
88
90
  pid = pid_for(app)
@@ -94,18 +96,18 @@ module Bluepill
94
96
  end
95
97
  end
96
98
  end
97
-
99
+
98
100
  def pid_for(app)
99
101
  pid_file = File.join(self.pids_dir, "#{app}.pid")
100
102
  File.exists?(pid_file) && File.read(pid_file).to_i
101
103
  end
102
-
104
+
103
105
  def setup_dir_structure
104
106
  [@sockets_dir, @pids_dir].each do |dir|
105
107
  FileUtils.mkdir_p(dir) unless File.exists?(dir)
106
108
  end
107
109
  end
108
-
110
+
109
111
  def verify_version!(application)
110
112
  begin
111
113
  version = Socket.client_command(base_dir, application, "version")
@@ -1,155 +1,11 @@
1
- require 'ostruct'
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'bluepill/dsl/app_proxy'
4
+
2
5
  module Bluepill
3
- def self.define_process_condition(name, &block)
4
- klass = Class.new(ProcessConditions::ProcessCondition, &block)
5
- ProcessConditions.const_set("#{name.to_s.camelcase}", klass)
6
- end
7
-
8
6
  def self.application(app_name, options = {}, &block)
9
- app = Application.new(app_name.to_s, options, &block)
10
-
11
- process_proxy = Class.new do
12
- attr_reader :attributes, :watches
13
- def initialize(process_name = nil)
14
- @name = process_name
15
- @attributes = {}
16
- @watches = {}
17
- end
18
-
19
- def method_missing(name, *args)
20
- if args.size == 1 && name.to_s =~ /^(.*)=$/
21
- @attributes[$1.to_sym] = args.first
22
- elsif args.empty? && @attributes.key?(name.to_sym)
23
- @attributes[name.to_sym]
24
- else
25
- super
26
- end
27
- end
28
-
29
- def checks(name, options = {})
30
- @watches[name] = options
31
- end
32
-
33
- def validate_child_process(child)
34
- unless child.attributes.has_key?(:stop_command)
35
- $stderr.puts "Config Error: Invalid child process monitor for #{@name}"
36
- $stderr.puts "You must specify a stop command to monitor child processes."
37
- exit(6)
38
- end
39
- end
40
-
41
- def create_child_process_template
42
- if @child_process_block
43
- child_proxy = self.class.new
44
- # Children inherit some properties of the parent
45
- [:start_grace_time, :stop_grace_time, :restart_grace_time].each do |attribute|
46
- child_proxy.send("#{attribute}=", @attributes[attribute]) if @attributes.key?(attribute)
47
- end
48
- @child_process_block.call(child_proxy)
49
- validate_child_process(child_proxy)
50
- @attributes[:child_process_template] = child_proxy.to_process(nil)
51
- end
52
- end
53
-
54
- def monitor_children(&child_process_block)
55
- @child_process_block = child_process_block
56
- @attributes[:monitor_children] = true
57
- end
58
-
59
- def to_process(process_name)
60
- process = Bluepill::Process.new(process_name, @attributes)
61
- @watches.each do |name, opts|
62
- if Bluepill::Trigger[name]
63
- process.add_trigger(name, opts)
64
- else
65
- process.add_watch(name, opts)
66
- end
67
- end
68
-
69
- process
70
- end
71
- end
72
- app_proxy = Class.new do
73
- if RUBY_VERSION >= '1.9'
74
- class_variable_set(:@@app, app)
75
- class_variable_set(:@@process_proxy, process_proxy)
76
- class_variable_set(:@@process_keys, Hash.new) # because I don't want to require Set just for validations
77
- class_variable_set(:@@pid_files, Hash.new)
78
- else
79
- @@app = app
80
- @@process_proxy = process_proxy
81
- @@process_keys = Hash.new
82
- @@pid_files = Hash.new
83
- end
84
- attr_accessor :working_dir, :uid, :gid, :environment
85
-
86
- def validate_process(process, process_name)
87
- # validate uniqueness of group:process
88
- process_key = [process.attributes[:group], process_name].join(":")
89
- if @@process_keys.key?(process_key)
90
- $stderr.print "Config Error: You have two entries for the process name '#{process_name}'"
91
- $stderr.print " in the group '#{process.attributes[:group]}'" if process.attributes.key?(:group)
92
- $stderr.puts
93
- exit(6)
94
- else
95
- @@process_keys[process_key] = 0
96
- end
97
-
98
- # validate required attributes
99
- [:start_command].each do |required_attr|
100
- if !process.attributes.key?(required_attr)
101
- $stderr.puts "Config Error: You must specify a #{required_attr} for '#{process_name}'"
102
- exit(6)
103
- end
104
- end
105
-
106
- # validate uniqueness of pid files
107
- pid_key = process.pid_file.strip
108
- if @@pid_files.key?(pid_key)
109
- $stderr.puts "Config Error: You have two entries with the pid file: #{process.pid_file}"
110
- exit(6)
111
- else
112
- @@pid_files[pid_key] = 0
113
- end
114
- end
115
-
116
- def process(process_name, &process_block)
117
- process_proxy = @@process_proxy.new(process_name)
118
- process_block.call(process_proxy)
119
- process_proxy.create_child_process_template
120
-
121
- set_app_wide_attributes(process_proxy)
122
-
123
- assign_default_pid_file(process_proxy, process_name)
124
-
125
- validate_process(process_proxy, process_name)
126
-
127
- group = process_proxy.attributes.delete(:group)
128
- process = process_proxy.to_process(process_name)
129
-
130
-
131
-
132
- @@app.add_process(process, group)
133
- end
134
-
135
- def set_app_wide_attributes(process_proxy)
136
- [:working_dir, :uid, :gid, :environment].each do |attribute|
137
- unless process_proxy.attributes.key?(attribute)
138
- process_proxy.attributes[attribute] = self.send(attribute)
139
- end
140
- end
141
- end
142
-
143
- def assign_default_pid_file(process_proxy, process_name)
144
- unless process_proxy.attributes.key?(:pid_file)
145
- group_name = process_proxy.attributes["group"]
146
- default_pid_name = [group_name, process_name].compact.join('_').gsub(/[^A-Za-z0-9_\-]/, "_")
147
- process_proxy.pid_file = File.join(@@app.pids_dir, default_pid_name + ".pid")
148
- end
149
- end
150
- end
151
-
152
- yield(app_proxy.new)
153
- app.load
7
+ app_proxy = AppProxy.new(app_name, options)
8
+ yield(app_proxy)
9
+ app_proxy.app.load
154
10
  end
155
11
  end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'bluepill/application'
4
+ require 'bluepill/dsl/process_factory'
5
+
6
+ module Bluepill
7
+ class AppProxy
8
+ APP_ATTRIBUTES = [:working_dir, :uid, :gid, :environment]
9
+
10
+ attr_accessor *APP_ATTRIBUTES
11
+ attr_reader :app
12
+
13
+ def initialize(app_name, options)
14
+ @app = Application.new(app_name.to_s, options)
15
+ end
16
+
17
+ def process(process_name, &process_block)
18
+ attributes = {}
19
+ APP_ATTRIBUTES.each {|a| attributes[a] = self.send(a) }
20
+
21
+ process_factory = ProcessFactory.new(attributes, process_block)
22
+
23
+ process = process_factory.create_process(process_name, @app.pids_dir)
24
+ group = process_factory.attributes.delete(:group)
25
+
26
+ @app.add_process(process, group)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,87 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'bluepill/dsl/process_proxy'
4
+
5
+ module Bluepill
6
+ class ProcessFactory
7
+ attr_reader :attributes
8
+
9
+ @@process_keys = Hash.new
10
+ @@pid_files = Hash.new
11
+
12
+ def initialize(attributes, process_block)
13
+ @attributes = attributes
14
+ @process_block = process_block
15
+ end
16
+
17
+ def create_process(name, pids_dir)
18
+ self.assign_default_pid_file(name, pids_dir)
19
+
20
+ process = Bluepill::ProcessProxy.new(name, @attributes, @process_block)
21
+ child_process_block = @attributes.delete(:child_process_block)
22
+ @attributes[:child_process_factory] = ProcessFactory.new(@attributes, child_process_block) if @attributes[:monitor_children]
23
+
24
+ self.validate_process! process
25
+ process.to_process
26
+ end
27
+
28
+ def create_child_process(name, pid, logger)
29
+ attributes = {}
30
+ [:start_grace_time, :stop_grace_time, :restart_grace_time].each {|a| attributes[a] = @attributes[a]}
31
+ attributes[:actual_pid] = pid
32
+ attributes[:logger] = logger
33
+
34
+ child = Bluepill::ProcessProxy.new(name, attributes, @process_block)
35
+ self.validate_child_process! child
36
+ child.to_process
37
+ end
38
+
39
+ protected
40
+
41
+ def assign_default_pid_file(process_name, pids_dir)
42
+ unless @attributes.key?(:pid_file)
43
+ group_name = @attributes[:group]
44
+ default_pid_name = [group_name, process_name].compact.join('_').gsub(/[^A-Za-z0-9_\-]/, "_")
45
+ @attributes[:pid_file] = File.join(pids_dir, default_pid_name + ".pid")
46
+ end
47
+ end
48
+
49
+ def validate_process!(process)
50
+ # validate uniqueness of group:process
51
+ process_key = [process.attributes[:group], process.name].join(":")
52
+ if @@process_keys.key?(process_key)
53
+ $stderr.print "Config Error: You have two entries for the process name '#{process.name}'"
54
+ $stderr.print " in the group '#{process.attributes[:group]}'" if process.attributes.key?(:group)
55
+ $stderr.puts
56
+ exit(6)
57
+ else
58
+ @@process_keys[process_key] = 0
59
+ end
60
+
61
+ # validate required attributes
62
+ [:start_command].each do |required_attr|
63
+ if !process.attributes.key?(required_attr)
64
+ $stderr.puts "Config Error: You must specify a #{required_attr} for '#{process.name}'"
65
+ exit(6)
66
+ end
67
+ end
68
+
69
+ # validate uniqueness of pid files
70
+ pid_key = process.attributes[:pid_file].strip
71
+ if @@pid_files.key?(pid_key)
72
+ $stderr.puts "Config Error: You have two entries with the pid file: #{pid_key}"
73
+ exit(6)
74
+ else
75
+ @@pid_files[pid_key] = 0
76
+ end
77
+ end
78
+
79
+ def validate_child_process!(child)
80
+ unless child.attributes.has_key?(:stop_command)
81
+ $stderr.puts "Config Error: Invalid child process monitor for #{child.name}"
82
+ $stderr.puts "You must specify a stop command to monitor child processes."
83
+ exit(6)
84
+ end
85
+ end
86
+ end
87
+ end