Module: Bluepill

Defined in:
lib/bluepill/dsl.rb,
lib/bluepill/group.rb,
lib/bluepill/logger.rb,
lib/bluepill/socket.rb,
lib/bluepill/system.rb,
lib/bluepill/process.rb,
lib/bluepill/trigger.rb,
lib/bluepill/version.rb,
lib/bluepill/controller.rb,
lib/bluepill/application.rb,
lib/bluepill/condition_watch.rb,
lib/bluepill/triggers/flapping.rb,
lib/bluepill/application/client.rb,
lib/bluepill/process_conditions.rb,
lib/bluepill/process_statistics.rb,
lib/bluepill/util/rotational_array.rb,
lib/bluepill/process_conditions/http.rb,
lib/bluepill/process_conditions/cpu_usage.rb,
lib/bluepill/process_conditions/mem_usage.rb,
lib/bluepill/process_conditions/always_true.rb,
lib/bluepill/process_conditions/process_condition.rb

Defined Under Namespace

Modules: Application, ProcessConditions, Socket, System, Triggers, Util Classes: ConditionWatch, Controller, Group, Logger, Process, ProcessStatistics, Trigger

Constant Summary collapse

VERSION =
"0.0.42"

Class Method Summary collapse

Class Method Details

.application(app_name, options = {}) {|app_proxy.new| ... } ⇒ Object

Yields:

  • (app_proxy.new)


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/bluepill/dsl.rb', line 8

def self.application(app_name, options = {}, &block)
  app = Application.new(app_name.to_s, options, &block)
 
  process_proxy = Class.new do
    attr_reader :attributes, :watches
    def initialize(process_name = nil)
      @name = process_name
      @attributes = {}
      @watches = {}
    end
    
    def method_missing(name, *args)
      if args.size == 1 && name.to_s =~ /^(.*)=$/
        @attributes[$1.to_sym] = args.first
      elsif args.empty? && @attributes.key?(name.to_sym)
        @attributes[name.to_sym]
      else
        super
      end
    end
    
    def checks(name, options = {})
      @watches[name] = options
    end
    
    def validate_child_process(child)
      unless child.attributes.has_key?(:stop_command)
        $stderr.puts "Config Error: Invalid child process monitor for #{@name}"
        $stderr.puts "You must specify a stop command to monitor child processes."
        exit(6)
      end
    end
    
    def create_child_process_template
      if @child_process_block
        child_proxy = self.class.new
        # Children inherit some properties of the parent
        [:start_grace_time, :stop_grace_time, :restart_grace_time].each do |attribute|
          child_proxy.send("#{attribute}=", @attributes[attribute]) if @attributes.key?(attribute)
        end
        @child_process_block.call(child_proxy)
        validate_child_process(child_proxy)
        @attributes[:child_process_template] = child_proxy.to_process(nil)
      end
    end
    
    def monitor_children(&child_process_block)
      @child_process_block = child_process_block
      @attributes[:monitor_children] = true
    end
    
    def to_process(process_name)
      process = Bluepill::Process.new(process_name, @attributes)
      @watches.each do |name, opts|
        if Bluepill::Trigger[name]
          process.add_trigger(name, opts)
        else
          process.add_watch(name, opts)
        end
      end

      process
    end
  end
  app_proxy = Class.new do
    if RUBY_VERSION >= '1.9'
      class_variable_set(:@@app, app)
      class_variable_set(:@@process_proxy, process_proxy)
      class_variable_set(:@@process_keys, Hash.new) # because I don't want to require Set just for validations
      class_variable_set(:@@pid_files, Hash.new)
    else
      @@app = app
      @@process_proxy = process_proxy
      @@process_keys = Hash.new
      @@pid_files = Hash.new
    end
    attr_accessor :working_dir, :uid, :gid, :environment
    
    def validate_process(process, process_name)
      # validate uniqueness of group:process
      process_key = [process.attributes[:group], process_name].join(":")
      if @@process_keys.key?(process_key)
        $stderr.print "Config Error: You have two entries for the process name '#{process_name}'"
        $stderr.print " in the group '#{process.attributes[:group]}'" if process.attributes.key?(:group)
        $stderr.puts
        exit(6)
      else
        @@process_keys[process_key] = 0
      end
      
      # validate required attributes
      [:start_command].each do |required_attr|
        if !process.attributes.key?(required_attr)
          $stderr.puts "Config Error: You must specify a #{required_attr} for '#{process_name}'"
          exit(6)
        end
      end
      
      # validate uniqueness of pid files
      pid_key = process.pid_file.strip
      if @@pid_files.key?(pid_key)
        $stderr.puts "Config Error: You have two entries with the pid file: #{process.pid_file}"
        exit(6)
      else
        @@pid_files[pid_key] = 0
      end
    end
    
    def process(process_name, &process_block)
      process_proxy = @@process_proxy.new(process_name)
      process_block.call(process_proxy)
      process_proxy.create_child_process_template
      
      set_app_wide_attributes(process_proxy)
      
      assign_default_pid_file(process_proxy, process_name)
      
      validate_process(process_proxy, process_name)
      
      group = process_proxy.attributes.delete(:group)
      process = process_proxy.to_process(process_name)
      
      
      
      @@app.add_process(process, group)
    end
    
    def set_app_wide_attributes(process_proxy)
      [:working_dir, :uid, :gid, :environment].each do |attribute|
        unless process_proxy.attributes.key?(attribute)
          process_proxy.attributes[attribute] = self.send(attribute)
        end
      end
    end
    
    def assign_default_pid_file(process_proxy, process_name)
      unless process_proxy.attributes.key?(:pid_file)
        group_name = process_proxy.attributes["group"]
        default_pid_name = [group_name, process_name].compact.join('_').gsub(/[^A-Za-z0-9_\-]/, "_")
        process_proxy.pid_file = File.join(@@app.pids_dir, default_pid_name + ".pid")
      end
    end
  end
  
  yield(app_proxy.new)
  app.load
end

.define_process_condition(name, &block) ⇒ Object



3
4
5
6
# File 'lib/bluepill/dsl.rb', line 3

def self.define_process_condition(name, &block)
  klass = Class.new(ProcessConditions::ProcessCondition, &block)
  ProcessConditions.const_set("#{name.to_s.camelcase}", klass)
end