epi 0.0.1 → 0.1.0
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 +4 -4
- data/bin/epi +1 -1
- data/lib/epi/cli/command.rb +7 -0
- data/lib/epi/cli/commands/concerns/daemon.rb +33 -0
- data/lib/epi/cli/commands/config.rb +11 -3
- data/lib/epi/cli/commands/daemon.rb +14 -0
- data/lib/epi/cli/commands/help.rb +0 -0
- data/lib/epi/cli/commands/job.rb +1 -1
- data/lib/epi/cli/commands/restart.rb +16 -0
- data/lib/epi/cli/commands/start.rb +15 -0
- data/lib/epi/cli/commands/status.rb +6 -2
- data/lib/epi/cli/commands/stop.rb +16 -0
- data/lib/epi/cli.rb +1 -1
- data/lib/epi/connection.rb +7 -0
- data/lib/epi/core_ext/inflector.rb +1 -1
- data/lib/epi/daemon/receiver.rb +37 -0
- data/lib/epi/{server → daemon}/responder.rb +14 -5
- data/lib/epi/{server → daemon}/responders/config.rb +14 -2
- data/lib/epi/{server → daemon}/responders/job.rb +5 -6
- data/lib/epi/{server → daemon}/responders/shutdown.rb +1 -1
- data/lib/epi/daemon/responders/start.rb +19 -0
- data/lib/epi/{server → daemon}/responders/status.rb +4 -2
- data/lib/epi/daemon/responders/stop_all.rb +20 -0
- data/lib/epi/daemon/sender.rb +74 -0
- data/lib/epi/{server.rb → daemon.rb} +26 -27
- data/lib/epi/data.rb +4 -5
- data/lib/epi/job.rb +60 -2
- data/lib/epi/job_description.rb +6 -8
- data/lib/epi/jobs.rb +30 -2
- data/lib/epi/launch.rb +1 -1
- data/lib/epi/logging.rb +33 -0
- data/lib/epi/process_status.rb +1 -1
- data/lib/epi/running_process.rb +21 -3
- data/lib/epi/trigger.rb +53 -0
- data/lib/epi/triggers/concerns/comparison.rb +43 -0
- data/lib/epi/triggers/memory.rb +16 -0
- data/lib/epi/triggers/touch.rb +37 -0
- data/lib/epi/triggers/uptime.rb +12 -0
- data/lib/epi/version.rb +1 -1
- data/lib/epi.rb +4 -22
- metadata +26 -26
- data/lib/epi/cli/commands/server.rb +0 -38
- data/lib/epi/server/receiver.rb +0 -46
- data/lib/epi/server/responders/command.rb +0 -15
- data/lib/epi/server/sender.rb +0 -64
    
        data/lib/epi/job.rb
    CHANGED
    
    | @@ -15,6 +15,7 @@ module Epi | |
| 15 15 |  | 
| 16 16 | 
             
                def initialize(job_description, state)
         | 
| 17 17 | 
             
                  @job_description = job_description
         | 
| 18 | 
            +
                  @triggers = job_description.triggers.map { |t| Trigger.make self, *t }
         | 
| 18 19 | 
             
                  @expected_count = state['expected_count'] || job_description.initial_processes
         | 
| 19 20 | 
             
                  @pids = state['pids']
         | 
| 20 21 | 
             
                  @dying_pids = state['dying_pids']
         | 
| @@ -76,25 +77,80 @@ module Epi | |
| 76 77 | 
             
                  stop_one while running_count > expected_count
         | 
| 77 78 | 
             
                end
         | 
| 78 79 |  | 
| 80 | 
            +
                def run_triggers!
         | 
| 81 | 
            +
                  @triggers.each &:try
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                def shutdown!(&callback)
         | 
| 85 | 
            +
                  count = running_count
         | 
| 86 | 
            +
                  if count > 0
         | 
| 87 | 
            +
                    count.times do
         | 
| 88 | 
            +
                      stop_one do
         | 
| 89 | 
            +
                        count -= 1
         | 
| 90 | 
            +
                        callback.call if callback && count == 0
         | 
| 91 | 
            +
                      end
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
                  else
         | 
| 94 | 
            +
                    callback.call if callback
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 79 98 | 
             
                def terminate!
         | 
| 80 99 | 
             
                  self.expected_count = 0
         | 
| 81 100 | 
             
                  sync!
         | 
| 82 101 | 
             
                end
         | 
| 83 102 |  | 
| 103 | 
            +
                def restart!
         | 
| 104 | 
            +
                  count = expected_count
         | 
| 105 | 
            +
                  if count > 0
         | 
| 106 | 
            +
                    self.expected_count = 0
         | 
| 107 | 
            +
                    sync!
         | 
| 108 | 
            +
                    self.expected_count = count
         | 
| 109 | 
            +
                    sync!
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
                  self
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                def running_processes
         | 
| 115 | 
            +
                  pids.map do |proc_id, pid|
         | 
| 116 | 
            +
                    [proc_id, ProcessStatus[pid] || RunningProcess.new(pid)]
         | 
| 117 | 
            +
                  end.to_h
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 84 120 | 
             
                def running_count
         | 
| 85 121 | 
             
                  pids.count
         | 
| 86 122 | 
             
                end
         | 
| 87 123 |  | 
| 124 | 
            +
                def dying_count
         | 
| 125 | 
            +
                  dying_pids.count
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                # Replace a running process with a new one
         | 
| 129 | 
            +
                # @param pid [Fixnum] PID of the process to replace
         | 
| 130 | 
            +
                def replace(pid, &callback)
         | 
| 131 | 
            +
                  stop_one pid do
         | 
| 132 | 
            +
                    start_one while running_count < expected_count
         | 
| 133 | 
            +
                    callback.call if callback
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
             | 
| 88 137 | 
             
                private
         | 
| 89 138 |  | 
| 90 139 | 
             
                def start_one
         | 
| 91 140 | 
             
                  proc_id, pid = job_description.launch
         | 
| 92 141 | 
             
                  pids[proc_id] = pid
         | 
| 93 142 | 
             
                  Data.write pid_key(proc_id), pid
         | 
| 143 | 
            +
                  Jobs.by_pid[pid] = self
         | 
| 94 144 | 
             
                end
         | 
| 95 145 |  | 
| 96 | 
            -
                def stop_one
         | 
| 97 | 
            -
                   | 
| 146 | 
            +
                def stop_one(pid = nil, &callback)
         | 
| 147 | 
            +
                  if pid
         | 
| 148 | 
            +
                    proc_id = pids.key pid
         | 
| 149 | 
            +
                    raise Exceptions::Fatal, "Process #{pid} isn't managed by job #{id}" unless proc_id
         | 
| 150 | 
            +
                    pids.delete proc_id
         | 
| 151 | 
            +
                  else
         | 
| 152 | 
            +
                    proc_id, pid = pids.shift
         | 
| 153 | 
            +
                  end
         | 
| 98 154 | 
             
                  dying_pids[proc_id] = pid
         | 
| 99 155 | 
             
                  work = proc do
         | 
| 100 156 | 
             
                    ProcessStatus[pid].kill job_description.kill_timeout
         | 
| @@ -102,6 +158,8 @@ module Epi | |
| 102 158 | 
             
                  done = proc do
         | 
| 103 159 | 
             
                    dying_pids.delete proc_id
         | 
| 104 160 | 
             
                    Data.write pid_key(proc_id), nil
         | 
| 161 | 
            +
                    Jobs.by_pid.delete pid
         | 
| 162 | 
            +
                    callback.call if callback
         | 
| 105 163 | 
             
                  end
         | 
| 106 164 | 
             
                  EventMachine.defer work, done
         | 
| 107 165 | 
             
                end
         | 
    
        data/lib/epi/job_description.rb
    CHANGED
    
    | @@ -56,11 +56,11 @@ module Epi | |
| 56 56 | 
             
                  'Must be a non-negative number' unless value.is_a?(Numeric) && value >= 0
         | 
| 57 57 | 
             
                end
         | 
| 58 58 |  | 
| 59 | 
            -
                attr_reader :id
         | 
| 59 | 
            +
                attr_reader :id, :triggers
         | 
| 60 60 |  | 
| 61 61 | 
             
                def initialize(id)
         | 
| 62 62 | 
             
                  @id = id
         | 
| 63 | 
            -
                  @ | 
| 63 | 
            +
                  @triggers = []
         | 
| 64 64 | 
             
                  @props = {}
         | 
| 65 65 | 
             
                end
         | 
| 66 66 |  | 
| @@ -78,15 +78,13 @@ module Epi | |
| 78 78 | 
             
                end
         | 
| 79 79 |  | 
| 80 80 | 
             
                def reconfigure
         | 
| 81 | 
            -
                  @ | 
| 81 | 
            +
                  @triggers = []
         | 
| 82 82 | 
             
                  yield self
         | 
| 83 | 
            +
                  # TODO: trigger an update of existing/running jobs
         | 
| 83 84 | 
             
                end
         | 
| 84 85 |  | 
| 85 | 
            -
                def on( | 
| 86 | 
            -
                   | 
| 87 | 
            -
                      args: args,
         | 
| 88 | 
            -
                      handler: handler
         | 
| 89 | 
            -
                  }
         | 
| 86 | 
            +
                def on(trigger_name, *args, &handler)
         | 
| 87 | 
            +
                  @triggers << [trigger_name, args, handler]
         | 
| 90 88 | 
             
                end
         | 
| 91 89 |  | 
| 92 90 | 
             
                def pid_key(proc_id)
         | 
    
        data/lib/epi/jobs.rb
    CHANGED
    
    | @@ -8,13 +8,14 @@ module Epi | |
| 8 8 | 
             
                class << self
         | 
| 9 9 | 
             
                  extend Forwardable
         | 
| 10 10 |  | 
| 11 | 
            -
                  delegate [:[], :[]=, :delete, :each_value, :map] => :@jobs
         | 
| 11 | 
            +
                  delegate [:[], :[]=, :delete, :each_value, :map, :find, :count] => :@jobs
         | 
| 12 12 |  | 
| 13 | 
            -
                  attr_reader :configuration_files
         | 
| 13 | 
            +
                  attr_reader :configuration_files, :by_pid
         | 
| 14 14 |  | 
| 15 15 | 
             
                  def reset!
         | 
| 16 16 | 
             
                    @configuration_files = {}
         | 
| 17 17 | 
             
                    @jobs = {}
         | 
| 18 | 
            +
                    @by_pid = {}
         | 
| 18 19 | 
             
                  end
         | 
| 19 20 |  | 
| 20 21 | 
             
                  def beat!
         | 
| @@ -37,6 +38,13 @@ module Epi | |
| 37 38 | 
             
                    # Sync each job with its expectations
         | 
| 38 39 | 
             
                    each_value &:sync!
         | 
| 39 40 |  | 
| 41 | 
            +
                    # Snapshot processes again, so that triggers have access to
         | 
| 42 | 
            +
                    # newly-spawned processes
         | 
| 43 | 
            +
                    ProcessStatus.take!
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    # Run job triggers
         | 
| 46 | 
            +
                    each_value &:run_triggers!
         | 
| 47 | 
            +
             | 
| 40 48 | 
             
                    # Write state of each job to data file
         | 
| 41 49 | 
             
                    Data.jobs = map { |id, job| [id.to_s, job.state] }.to_h
         | 
| 42 50 | 
             
                    Data.save
         | 
| @@ -45,6 +53,26 @@ module Epi | |
| 45 53 | 
             
                    @next_beat = EventMachine.add_timer(5) { beat! } # TODO: make interval configurable
         | 
| 46 54 | 
             
                  end
         | 
| 47 55 |  | 
| 56 | 
            +
                  def shutdown!(&callback)
         | 
| 57 | 
            +
                    EventMachine.cancel_timer @next_beat if @next_beat
         | 
| 58 | 
            +
                    ProcessStatus.take!
         | 
| 59 | 
            +
                    remaining = count
         | 
| 60 | 
            +
                    if remaining > 0
         | 
| 61 | 
            +
                      each_value do |job|
         | 
| 62 | 
            +
                        job.shutdown! do
         | 
| 63 | 
            +
                          remaining -= 1
         | 
| 64 | 
            +
                          callback.call if callback && remaining == 0
         | 
| 65 | 
            +
                        end
         | 
| 66 | 
            +
                      end
         | 
| 67 | 
            +
                    else
         | 
| 68 | 
            +
                      callback.call if callback
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  def running_process_count
         | 
| 73 | 
            +
                    each_value.map(&:running_count).reduce(:+) || 0
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 48 76 | 
             
                  def job_descriptions
         | 
| 49 77 | 
             
                    configuration_files.values.inject({}) { |all, conf_file| all.merge! conf_file.job_descriptions }
         | 
| 50 78 | 
             
                  end
         | 
    
        data/lib/epi/launch.rb
    CHANGED
    
    | @@ -53,7 +53,7 @@ module Epi | |
| 53 53 |  | 
| 54 54 | 
             
                # Run the command and read the resulting PID from its STDOUT
         | 
| 55 55 | 
             
                IO.popen(env, cmd) { |p| p.read }.to_i.tap do |pid|
         | 
| 56 | 
            -
                  logger. | 
| 56 | 
            +
                  logger.info "Process #{pid} started: #{`ps -p #{pid} -o command=`.chomp}"
         | 
| 57 57 | 
             
                end
         | 
| 58 58 | 
             
              end
         | 
| 59 59 | 
             
            end
         | 
    
        data/lib/epi/logging.rb
    ADDED
    
    | @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module Epi
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              class << self
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                DEFAULT_LEVEL = 'info'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def logger
         | 
| 8 | 
            +
                  @logger ||= make_logger
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def logger=(value)
         | 
| 12 | 
            +
                  @logger = Logger === value ? value : make_logger(value)
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                private
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def make_logger(target = nil)
         | 
| 18 | 
            +
                  Logger.new(target || ENV['EPI_LOG'] || default_log_path).tap do |logger|
         | 
| 19 | 
            +
                    logger.level = log_level
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def default_log_path
         | 
| 24 | 
            +
                  Data.home.join('epi.log').to_s
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def log_level
         | 
| 28 | 
            +
                  Logger::Severity.const_get (ENV['EPI_LOG_LEVEL'] || DEFAULT_LEVEL).upcase
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            end
         | 
    
        data/lib/epi/process_status.rb
    CHANGED
    
    
    
        data/lib/epi/running_process.rb
    CHANGED
    
    | @@ -42,10 +42,11 @@ module Epi | |
| 42 42 | 
             
                  Epi.logger
         | 
| 43 43 | 
             
                end
         | 
| 44 44 |  | 
| 45 | 
            -
                def initialize(pid, ps_line  | 
| 46 | 
            -
                  @pid = pid
         | 
| 45 | 
            +
                def initialize(pid, ps_line: nil, job: nil)
         | 
| 46 | 
            +
                  @pid = pid.to_i
         | 
| 47 47 | 
             
                  @ps_line = ps_line
         | 
| 48 48 | 
             
                  @props = {}
         | 
| 49 | 
            +
                  @job = job
         | 
| 49 50 | 
             
                  reload! unless ps_line
         | 
| 50 51 | 
             
                end
         | 
| 51 52 |  | 
| @@ -96,6 +97,12 @@ module Epi | |
| 96 97 | 
             
                  @started_at ||= Time.parse parts[5..9].join ' '
         | 
| 97 98 | 
             
                end
         | 
| 98 99 |  | 
| 100 | 
            +
                # Duration the process has been running (in seconds)
         | 
| 101 | 
            +
                # @return [Float]
         | 
| 102 | 
            +
                def uptime
         | 
| 103 | 
            +
                  Time.now - started_at
         | 
| 104 | 
            +
                end
         | 
| 105 | 
            +
             | 
| 99 106 | 
             
                # Name of the user that owns the process
         | 
| 100 107 | 
             
                # @return [String]
         | 
| 101 108 | 
             
                def user
         | 
| @@ -136,17 +143,28 @@ module Epi | |
| 136 143 | 
             
                  else
         | 
| 137 144 | 
             
                    signal = timeout ? 'KILL' : 'TERM'
         | 
| 138 145 | 
             
                    logger.info "Sending #{signal} to process #{pid}"
         | 
| 139 | 
            -
                    Process.kill signal, pid
         | 
| 146 | 
            +
                    Process.kill signal, pid rescue Errno::ESRCH false
         | 
| 140 147 | 
             
                    sleep 0.2 while `ps -p #{pid} > /dev/null 2>&1; echo $?`.chomp.to_i == 0
         | 
| 141 148 | 
             
                    logger.info "Process #{pid} terminated by signal #{signal}"
         | 
| 142 149 | 
             
                  end
         | 
| 143 150 | 
             
                  self
         | 
| 144 151 | 
             
                end
         | 
| 145 152 |  | 
| 153 | 
            +
                # Kill a running process immediately and synchronously with kill -9
         | 
| 154 | 
            +
                # @return [RunningProcess]
         | 
| 146 155 | 
             
                def kill!
         | 
| 147 156 | 
             
                  kill true
         | 
| 148 157 | 
             
                end
         | 
| 149 158 |  | 
| 159 | 
            +
                def job
         | 
| 160 | 
            +
                  @job ||= Jobs.by_pid[pid]
         | 
| 161 | 
            +
                end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                def restart!
         | 
| 164 | 
            +
                  raise Exceptions::Fatal, 'Cannot restart this process because it is not managed by a job' unless job
         | 
| 165 | 
            +
                  job.replace pid
         | 
| 166 | 
            +
                end
         | 
| 167 | 
            +
             | 
| 150 168 | 
             
                private
         | 
| 151 169 |  | 
| 152 170 | 
             
                def parts
         | 
    
        data/lib/epi/trigger.rb
    ADDED
    
    | @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            module Epi
         | 
| 2 | 
            +
              class Trigger
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                def self.make(job, name, args, handler)
         | 
| 5 | 
            +
                  klass_name = name.camelize
         | 
| 6 | 
            +
                  klass = Triggers.const_defined?(klass_name) && Triggers.const_get(klass_name)
         | 
| 7 | 
            +
                  raise Exceptions::Fatal, "No trigger exists named #{name}" unless Class === klass && klass < self
         | 
| 8 | 
            +
                  klass.new job, handler, *args
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                attr_reader :job, :args
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def initialize(job, handler, *args)
         | 
| 14 | 
            +
                  @job = job
         | 
| 15 | 
            +
                  @handler = handler
         | 
| 16 | 
            +
                  @args = args
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def logger
         | 
| 20 | 
            +
                  Epi.logger
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def message
         | 
| 24 | 
            +
                  nil
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def try
         | 
| 28 | 
            +
                  case self
         | 
| 29 | 
            +
                    when ProcessTrigger then job.running_processes.each_value { |process| try_with process }
         | 
| 30 | 
            +
                    when JobTrigger then try_with nil
         | 
| 31 | 
            +
                    else nil
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def try_with(process)
         | 
| 36 | 
            +
                  args = [process].reject(&:nil?)
         | 
| 37 | 
            +
                  if test *args
         | 
| 38 | 
            +
                    text = "Trigger on job #{job.id}"
         | 
| 39 | 
            +
                    text << " (PID #{process.pid})" if process
         | 
| 40 | 
            +
                    text << ": " << message if message
         | 
| 41 | 
            +
                    logger.info text
         | 
| 42 | 
            +
                    @handler.call process || job
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                class JobTrigger < self; end
         | 
| 47 | 
            +
                class ProcessTrigger < self; end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            Dir[File.expand_path '../triggers/concerns/*.rb', __FILE__].each { |f| require f }
         | 
| 53 | 
            +
            Dir[File.expand_path '../triggers/*.rb', __FILE__].each { |f| require f }
         | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            module Epi
         | 
| 2 | 
            +
              module Triggers
         | 
| 3 | 
            +
                module Concerns
         | 
| 4 | 
            +
                  module Comparison
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                    def compare(subject)
         | 
| 7 | 
            +
                      tester.call subject, object
         | 
| 8 | 
            +
                    end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    private
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    def tester
         | 
| 13 | 
            +
                      @tester ||= choose_tester
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    def op
         | 
| 17 | 
            +
                      @op ||= args[0]
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    def object
         | 
| 21 | 
            +
                      @object ||= args[1]
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    def choose_tester
         | 
| 25 | 
            +
                      case op
         | 
| 26 | 
            +
                        when :gt then -> a, b { a > b }
         | 
| 27 | 
            +
                        when :lt then -> a, b { a < b }
         | 
| 28 | 
            +
                        when :gte then -> a, b { a >= b }
         | 
| 29 | 
            +
                        when :lte then -> a, b { a <= b }
         | 
| 30 | 
            +
                        when :eq then -> a, b { a == b }
         | 
| 31 | 
            +
                        when :not_eq then -> a, b { a != b}
         | 
| 32 | 
            +
                        when :match then -> a, b { a =~ b }
         | 
| 33 | 
            +
                        when :not_match then -> a, b { a !~ b }
         | 
| 34 | 
            +
                        when :like then -> a, b { a === b }
         | 
| 35 | 
            +
                        when :not_like then -> a, b { !(a === b) }
         | 
| 36 | 
            +
                        else raise Exceptions::Fatal, "Unknown operation #{op}"
         | 
| 37 | 
            +
                      end
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
            end
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            module Epi
         | 
| 2 | 
            +
              module Triggers
         | 
| 3 | 
            +
                class Memory < Trigger::ProcessTrigger
         | 
| 4 | 
            +
                  include Concerns::Comparison
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def test(process)
         | 
| 7 | 
            +
                    compare process.physical_memory
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def message
         | 
| 11 | 
            +
                    "Physical memory exceeded #{object} bytes"
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            module Epi
         | 
| 2 | 
            +
              module Triggers
         | 
| 3 | 
            +
                class Touch < Trigger::JobTrigger
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  def initialize(*args)
         | 
| 6 | 
            +
                    super *args
         | 
| 7 | 
            +
                    update
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def test
         | 
| 11 | 
            +
                    ino = @ino; mtime = @mtime
         | 
| 12 | 
            +
                    update
         | 
| 13 | 
            +
                    ino != @ino || mtime != @mtime
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def message
         | 
| 17 | 
            +
                    "Path '#{path}' was touched"
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  private
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def path
         | 
| 23 | 
            +
                    @path ||= Pathname args.first
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def update
         | 
| 27 | 
            +
                    @ino, @mtime = begin
         | 
| 28 | 
            +
                      stat = path.stat
         | 
| 29 | 
            +
                      [stat.ino, stat.mtime]
         | 
| 30 | 
            +
                    rescue Errno::ENOENT
         | 
| 31 | 
            +
                      [nil, nil]
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
            end
         | 
    
        data/lib/epi/version.rb
    CHANGED
    
    
    
        data/lib/epi.rb
    CHANGED
    
    | @@ -3,10 +3,12 @@ require 'logger' | |
| 3 3 | 
             
            require 'shellwords'
         | 
| 4 4 |  | 
| 5 5 | 
             
            require 'epi/core_ext'
         | 
| 6 | 
            +
            require 'epi/logging'
         | 
| 6 7 | 
             
            require 'epi/exceptions'
         | 
| 7 8 | 
             
            require 'epi/version'
         | 
| 8 9 | 
             
            require 'epi/cli'
         | 
| 9 | 
            -
            require 'epi/ | 
| 10 | 
            +
            require 'epi/connection'
         | 
| 11 | 
            +
            require 'epi/daemon'
         | 
| 10 12 | 
             
            require 'epi/data'
         | 
| 11 13 | 
             
            require 'epi/process_status'
         | 
| 12 14 | 
             
            require 'epi/running_process'
         | 
| @@ -15,38 +17,18 @@ require 'epi/jobs' | |
| 15 17 | 
             
            require 'epi/configuration_file'
         | 
| 16 18 | 
             
            require 'epi/job_description'
         | 
| 17 19 | 
             
            require 'epi/launch'
         | 
| 20 | 
            +
            require 'epi/trigger'
         | 
| 18 21 |  | 
| 19 22 | 
             
            module Epi
         | 
| 20 23 | 
             
              ROOT = Pathname File.expand_path('../..', __FILE__)
         | 
| 21 24 |  | 
| 22 25 | 
             
              class << self
         | 
| 23 26 |  | 
| 24 | 
            -
                def logger
         | 
| 25 | 
            -
                  @logger ||= make_logger
         | 
| 26 | 
            -
                end
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                def logger=(value)
         | 
| 29 | 
            -
                  @logger = Logger === value ? value : make_logger(value)
         | 
| 30 | 
            -
                end
         | 
| 31 | 
            -
             | 
| 32 27 | 
             
                def root?
         | 
| 33 28 | 
             
                  @is_root = `whoami`.chomp == 'root' if @is_root.nil?
         | 
| 34 29 | 
             
                  @is_root
         | 
| 35 30 | 
             
                end
         | 
| 36 31 |  | 
| 37 | 
            -
                private
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                def make_logger(target = nil)
         | 
| 40 | 
            -
                  Logger.new(target || ENV['EPI_LOG'] || STDOUT).tap do |logger|
         | 
| 41 | 
            -
                    logger.level = Logger::Severity::WARN
         | 
| 42 | 
            -
                    level = ENV['EPI_LOG_LEVEL']
         | 
| 43 | 
            -
                    if level
         | 
| 44 | 
            -
                      level = level.upcase
         | 
| 45 | 
            -
                      logger.level = Logger::Severity.const_get(level) if Logger::Severity.const_defined? level
         | 
| 46 | 
            -
                    end
         | 
| 47 | 
            -
                  end
         | 
| 48 | 
            -
                end
         | 
| 49 | 
            -
             | 
| 50 32 | 
             
              end
         | 
| 51 33 |  | 
| 52 34 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: epi
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0 | 
| 4 | 
            +
              version: 0.1.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Neil E. Pearson
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2014-10- | 
| 11 | 
            +
            date: 2014-10-23 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: eventmachine
         | 
| @@ -24,20 +24,6 @@ dependencies: | |
| 24 24 | 
             
                - - "~>"
         | 
| 25 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 26 | 
             
                    version: '1.0'
         | 
| 27 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            -
              name: bson
         | 
| 29 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            -
                requirements:
         | 
| 31 | 
            -
                - - "~>"
         | 
| 32 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            -
                    version: '2.3'
         | 
| 34 | 
            -
              type: :runtime
         | 
| 35 | 
            -
              prerelease: false
         | 
| 36 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            -
                requirements:
         | 
| 38 | 
            -
                - - "~>"
         | 
| 39 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            -
                    version: '2.3'
         | 
| 41 27 | 
             
            description: Manage your background processes
         | 
| 42 28 | 
             
            email:
         | 
| 43 29 | 
             
            - neil@helium.net.au
         | 
| @@ -50,13 +36,29 @@ files: | |
| 50 36 | 
             
            - lib/epi.rb
         | 
| 51 37 | 
             
            - lib/epi/cli.rb
         | 
| 52 38 | 
             
            - lib/epi/cli/command.rb
         | 
| 39 | 
            +
            - lib/epi/cli/commands/concerns/daemon.rb
         | 
| 53 40 | 
             
            - lib/epi/cli/commands/config.rb
         | 
| 41 | 
            +
            - lib/epi/cli/commands/daemon.rb
         | 
| 42 | 
            +
            - lib/epi/cli/commands/help.rb
         | 
| 54 43 | 
             
            - lib/epi/cli/commands/job.rb
         | 
| 55 | 
            -
            - lib/epi/cli/commands/ | 
| 44 | 
            +
            - lib/epi/cli/commands/restart.rb
         | 
| 45 | 
            +
            - lib/epi/cli/commands/start.rb
         | 
| 56 46 | 
             
            - lib/epi/cli/commands/status.rb
         | 
| 47 | 
            +
            - lib/epi/cli/commands/stop.rb
         | 
| 57 48 | 
             
            - lib/epi/configuration_file.rb
         | 
| 49 | 
            +
            - lib/epi/connection.rb
         | 
| 58 50 | 
             
            - lib/epi/core_ext.rb
         | 
| 59 51 | 
             
            - lib/epi/core_ext/inflector.rb
         | 
| 52 | 
            +
            - lib/epi/daemon.rb
         | 
| 53 | 
            +
            - lib/epi/daemon/receiver.rb
         | 
| 54 | 
            +
            - lib/epi/daemon/responder.rb
         | 
| 55 | 
            +
            - lib/epi/daemon/responders/config.rb
         | 
| 56 | 
            +
            - lib/epi/daemon/responders/job.rb
         | 
| 57 | 
            +
            - lib/epi/daemon/responders/shutdown.rb
         | 
| 58 | 
            +
            - lib/epi/daemon/responders/start.rb
         | 
| 59 | 
            +
            - lib/epi/daemon/responders/status.rb
         | 
| 60 | 
            +
            - lib/epi/daemon/responders/stop_all.rb
         | 
| 61 | 
            +
            - lib/epi/daemon/sender.rb
         | 
| 60 62 | 
             
            - lib/epi/data.rb
         | 
| 61 63 | 
             
            - lib/epi/exceptions.rb
         | 
| 62 64 | 
             
            - lib/epi/exceptions/base.rb
         | 
| @@ -67,17 +69,14 @@ files: | |
| 67 69 | 
             
            - lib/epi/job_description.rb
         | 
| 68 70 | 
             
            - lib/epi/jobs.rb
         | 
| 69 71 | 
             
            - lib/epi/launch.rb
         | 
| 72 | 
            +
            - lib/epi/logging.rb
         | 
| 70 73 | 
             
            - lib/epi/process_status.rb
         | 
| 71 74 | 
             
            - lib/epi/running_process.rb
         | 
| 72 | 
            -
            - lib/epi/ | 
| 73 | 
            -
            - lib/epi/ | 
| 74 | 
            -
            - lib/epi/ | 
| 75 | 
            -
            - lib/epi/ | 
| 76 | 
            -
            - lib/epi/ | 
| 77 | 
            -
            - lib/epi/server/responders/job.rb
         | 
| 78 | 
            -
            - lib/epi/server/responders/shutdown.rb
         | 
| 79 | 
            -
            - lib/epi/server/responders/status.rb
         | 
| 80 | 
            -
            - lib/epi/server/sender.rb
         | 
| 75 | 
            +
            - lib/epi/trigger.rb
         | 
| 76 | 
            +
            - lib/epi/triggers/concerns/comparison.rb
         | 
| 77 | 
            +
            - lib/epi/triggers/memory.rb
         | 
| 78 | 
            +
            - lib/epi/triggers/touch.rb
         | 
| 79 | 
            +
            - lib/epi/triggers/uptime.rb
         | 
| 81 80 | 
             
            - lib/epi/version.rb
         | 
| 82 81 | 
             
            homepage: https://github.com/hx/epi
         | 
| 83 82 | 
             
            licenses:
         | 
| @@ -104,3 +103,4 @@ signing_key: | |
| 104 103 | 
             
            specification_version: 4
         | 
| 105 104 | 
             
            summary: Epinephrine
         | 
| 106 105 | 
             
            test_files: []
         | 
| 106 | 
            +
            has_rdoc: 
         | 
| @@ -1,38 +0,0 @@ | |
| 1 | 
            -
            module Epi
         | 
| 2 | 
            -
              module Cli
         | 
| 3 | 
            -
                module Commands
         | 
| 4 | 
            -
                  class Server < Command
         | 
| 5 | 
            -
             | 
| 6 | 
            -
                    def run
         | 
| 7 | 
            -
                      process = Epi::Server.process
         | 
| 8 | 
            -
                      raise Exceptions::Fatal, 'You need root privileges to manage this server' if
         | 
| 9 | 
            -
                          process && process.was_alive? && process.root? && !Epi.root?
         | 
| 10 | 
            -
                      case args.first
         | 
| 11 | 
            -
                        when nil, 'start' then startup
         | 
| 12 | 
            -
                        when 'run' then run_server
         | 
| 13 | 
            -
                        when 'stop' then shutdown
         | 
| 14 | 
            -
                        else raise Exceptions::Fatal, 'Unknown server command, use [ start | stop | restart ]'
         | 
| 15 | 
            -
                      end
         | 
| 16 | 
            -
                    end
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                    private
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                    def startup
         | 
| 21 | 
            -
                      Epi::Server.ensure_running
         | 
| 22 | 
            -
                      puts 'Server is running'
         | 
| 23 | 
            -
                    end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                    def shutdown
         | 
| 26 | 
            -
                      Epi::Server.shutdown
         | 
| 27 | 
            -
                      puts 'Server has shut down'
         | 
| 28 | 
            -
                    end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                    def run_server
         | 
| 31 | 
            -
                      Epi::Server.run
         | 
| 32 | 
            -
                      puts 'Server is running'
         | 
| 33 | 
            -
                    end
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                  end
         | 
| 36 | 
            -
                end
         | 
| 37 | 
            -
              end
         | 
| 38 | 
            -
            end
         |