itrg-invoker 1.6.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 +7 -0
 - data/bin/invoker +7 -0
 - data/lib/invoker/cli/pinger.rb +23 -0
 - data/lib/invoker/cli/question.rb +15 -0
 - data/lib/invoker/cli/tail.rb +34 -0
 - data/lib/invoker/cli/tail_watcher.rb +34 -0
 - data/lib/invoker/cli.rb +197 -0
 - data/lib/invoker/command_worker.rb +64 -0
 - data/lib/invoker/commander.rb +101 -0
 - data/lib/invoker/daemon.rb +126 -0
 - data/lib/invoker/dns_cache.rb +23 -0
 - data/lib/invoker/errors.rb +17 -0
 - data/lib/invoker/event/manager.rb +79 -0
 - data/lib/invoker/ipc/add_command.rb +12 -0
 - data/lib/invoker/ipc/add_http_command.rb +10 -0
 - data/lib/invoker/ipc/base_command.rb +24 -0
 - data/lib/invoker/ipc/client_handler.rb +26 -0
 - data/lib/invoker/ipc/dns_check_command.rb +17 -0
 - data/lib/invoker/ipc/list_command.rb +11 -0
 - data/lib/invoker/ipc/message/list_response.rb +35 -0
 - data/lib/invoker/ipc/message/tail_response.rb +10 -0
 - data/lib/invoker/ipc/message.rb +170 -0
 - data/lib/invoker/ipc/ping_command.rb +10 -0
 - data/lib/invoker/ipc/reload_command.rb +12 -0
 - data/lib/invoker/ipc/remove_command.rb +12 -0
 - data/lib/invoker/ipc/server.rb +26 -0
 - data/lib/invoker/ipc/tail_command.rb +11 -0
 - data/lib/invoker/ipc/unix_client.rb +60 -0
 - data/lib/invoker/ipc.rb +45 -0
 - data/lib/invoker/logger.rb +13 -0
 - data/lib/invoker/parsers/config.rb +184 -0
 - data/lib/invoker/parsers/procfile.rb +86 -0
 - data/lib/invoker/power/balancer.rb +133 -0
 - data/lib/invoker/power/config.rb +77 -0
 - data/lib/invoker/power/dns.rb +38 -0
 - data/lib/invoker/power/http_parser.rb +68 -0
 - data/lib/invoker/power/http_response.rb +81 -0
 - data/lib/invoker/power/port_finder.rb +49 -0
 - data/lib/invoker/power/power.rb +3 -0
 - data/lib/invoker/power/powerup.rb +29 -0
 - data/lib/invoker/power/setup/distro/arch.rb +15 -0
 - data/lib/invoker/power/setup/distro/base.rb +80 -0
 - data/lib/invoker/power/setup/distro/debian.rb +11 -0
 - data/lib/invoker/power/setup/distro/opensuse.rb +11 -0
 - data/lib/invoker/power/setup/distro/redhat.rb +11 -0
 - data/lib/invoker/power/setup/distro/ubuntu.rb +46 -0
 - data/lib/invoker/power/setup/files/invoker_forwarder.sh.erb +17 -0
 - data/lib/invoker/power/setup/files/socat_invoker.service +12 -0
 - data/lib/invoker/power/setup/linux_setup.rb +97 -0
 - data/lib/invoker/power/setup/osx_setup.rb +137 -0
 - data/lib/invoker/power/setup.rb +93 -0
 - data/lib/invoker/power/templates/400.html +40 -0
 - data/lib/invoker/power/templates/404.html +40 -0
 - data/lib/invoker/power/templates/503.html +40 -0
 - data/lib/invoker/power/url_rewriter.rb +40 -0
 - data/lib/invoker/process_manager.rb +201 -0
 - data/lib/invoker/process_printer.rb +59 -0
 - data/lib/invoker/reactor/reader.rb +65 -0
 - data/lib/invoker/reactor.rb +37 -0
 - data/lib/invoker/version.rb +47 -0
 - data/lib/invoker.rb +151 -0
 - data/spec/invoker/cli/pinger_spec.rb +22 -0
 - data/spec/invoker/cli/tail_watcher_spec.rb +39 -0
 - data/spec/invoker/cli_spec.rb +27 -0
 - data/spec/invoker/command_worker_spec.rb +45 -0
 - data/spec/invoker/commander_spec.rb +152 -0
 - data/spec/invoker/config_spec.rb +361 -0
 - data/spec/invoker/daemon_spec.rb +34 -0
 - data/spec/invoker/event/manager_spec.rb +67 -0
 - data/spec/invoker/invoker_spec.rb +71 -0
 - data/spec/invoker/ipc/client_handler_spec.rb +54 -0
 - data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
 - data/spec/invoker/ipc/message/list_response_spec.rb +24 -0
 - data/spec/invoker/ipc/message_spec.rb +49 -0
 - data/spec/invoker/ipc/unix_client_spec.rb +29 -0
 - data/spec/invoker/power/balancer_spec.rb +53 -0
 - data/spec/invoker/power/config_spec.rb +18 -0
 - data/spec/invoker/power/http_parser_spec.rb +32 -0
 - data/spec/invoker/power/http_response_spec.rb +34 -0
 - data/spec/invoker/power/port_finder_spec.rb +16 -0
 - data/spec/invoker/power/setup/linux_setup_spec.rb +166 -0
 - data/spec/invoker/power/setup/osx_setup_spec.rb +105 -0
 - data/spec/invoker/power/setup_spec.rb +4 -0
 - data/spec/invoker/power/url_rewriter_spec.rb +69 -0
 - data/spec/invoker/power/web_sockets_spec.rb +61 -0
 - data/spec/invoker/process_manager_spec.rb +130 -0
 - data/spec/invoker/reactor_spec.rb +6 -0
 - data/spec/spec_helper.rb +43 -0
 - metadata +376 -0
 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <!doctype html>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <html>
         
     | 
| 
      
 3 
     | 
    
         
            +
            <head>
         
     | 
| 
      
 4 
     | 
    
         
            +
              <meta charset="utf-8">
         
     | 
| 
      
 5 
     | 
    
         
            +
              <title>Invoker</title>
         
     | 
| 
      
 6 
     | 
    
         
            +
              <style>
         
     | 
| 
      
 7 
     | 
    
         
            +
                body {
         
     | 
| 
      
 8 
     | 
    
         
            +
                  margin: 0;
         
     | 
| 
      
 9 
     | 
    
         
            +
                  padding: 0;
         
     | 
| 
      
 10 
     | 
    
         
            +
                  background: #fff;
         
     | 
| 
      
 11 
     | 
    
         
            +
                  line-height: 18px;
         
     | 
| 
      
 12 
     | 
    
         
            +
                }
         
     | 
| 
      
 13 
     | 
    
         
            +
                div.page {
         
     | 
| 
      
 14 
     | 
    
         
            +
                  padding: 36px 90px;
         
     | 
| 
      
 15 
     | 
    
         
            +
                }
         
     | 
| 
      
 16 
     | 
    
         
            +
                h1, h2, p, li {
         
     | 
| 
      
 17 
     | 
    
         
            +
                  font-family: Helvetica, sans-serif;
         
     | 
| 
      
 18 
     | 
    
         
            +
                  font-size: 13px;
         
     | 
| 
      
 19 
     | 
    
         
            +
                }
         
     | 
| 
      
 20 
     | 
    
         
            +
                h1 {
         
     | 
| 
      
 21 
     | 
    
         
            +
                  line-height: 45px;
         
     | 
| 
      
 22 
     | 
    
         
            +
                  font-size: 36px;
         
     | 
| 
      
 23 
     | 
    
         
            +
                  margin: 0;
         
     | 
| 
      
 24 
     | 
    
         
            +
                }
         
     | 
| 
      
 25 
     | 
    
         
            +
                h2 {
         
     | 
| 
      
 26 
     | 
    
         
            +
                  line-height: 27px;
         
     | 
| 
      
 27 
     | 
    
         
            +
                  font-size: 18px;
         
     | 
| 
      
 28 
     | 
    
         
            +
                  font-weight: normal;
         
     | 
| 
      
 29 
     | 
    
         
            +
                  margin: 0;
         
     | 
| 
      
 30 
     | 
    
         
            +
                }
         
     | 
| 
      
 31 
     | 
    
         
            +
              </style>
         
     | 
| 
      
 32 
     | 
    
         
            +
            </head>
         
     | 
| 
      
 33 
     | 
    
         
            +
            <body class="">
         
     | 
| 
      
 34 
     | 
    
         
            +
              <div class="page">
         
     | 
| 
      
 35 
     | 
    
         
            +
                <h1>Application not found</h1>
         
     | 
| 
      
 36 
     | 
    
         
            +
                <hr>
         
     | 
| 
      
 37 
     | 
    
         
            +
                <h2>Invoker could not find the application. Please check the configuration file.<h2>
         
     | 
| 
      
 38 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 39 
     | 
    
         
            +
            </body>
         
     | 
| 
      
 40 
     | 
    
         
            +
            </html>
         
     | 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <!doctype html>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <html>
         
     | 
| 
      
 3 
     | 
    
         
            +
            <head>
         
     | 
| 
      
 4 
     | 
    
         
            +
              <meta charset="utf-8">
         
     | 
| 
      
 5 
     | 
    
         
            +
              <title>Invoker</title>
         
     | 
| 
      
 6 
     | 
    
         
            +
              <style>
         
     | 
| 
      
 7 
     | 
    
         
            +
                body {
         
     | 
| 
      
 8 
     | 
    
         
            +
                  margin: 0;
         
     | 
| 
      
 9 
     | 
    
         
            +
                  padding: 0;
         
     | 
| 
      
 10 
     | 
    
         
            +
                  background: #fff;
         
     | 
| 
      
 11 
     | 
    
         
            +
                  line-height: 18px;
         
     | 
| 
      
 12 
     | 
    
         
            +
                }
         
     | 
| 
      
 13 
     | 
    
         
            +
                div.page {
         
     | 
| 
      
 14 
     | 
    
         
            +
                  padding: 36px 90px;
         
     | 
| 
      
 15 
     | 
    
         
            +
                }
         
     | 
| 
      
 16 
     | 
    
         
            +
                h1, h2, p, li {
         
     | 
| 
      
 17 
     | 
    
         
            +
                  font-family: Helvetica, sans-serif;
         
     | 
| 
      
 18 
     | 
    
         
            +
                  font-size: 13px;
         
     | 
| 
      
 19 
     | 
    
         
            +
                }
         
     | 
| 
      
 20 
     | 
    
         
            +
                h1 {
         
     | 
| 
      
 21 
     | 
    
         
            +
                  line-height: 45px;
         
     | 
| 
      
 22 
     | 
    
         
            +
                  font-size: 36px;
         
     | 
| 
      
 23 
     | 
    
         
            +
                  margin: 0;
         
     | 
| 
      
 24 
     | 
    
         
            +
                }
         
     | 
| 
      
 25 
     | 
    
         
            +
                h2 {
         
     | 
| 
      
 26 
     | 
    
         
            +
                  line-height: 27px;
         
     | 
| 
      
 27 
     | 
    
         
            +
                  font-size: 18px;
         
     | 
| 
      
 28 
     | 
    
         
            +
                  font-weight: normal;
         
     | 
| 
      
 29 
     | 
    
         
            +
                  margin: 0;
         
     | 
| 
      
 30 
     | 
    
         
            +
                }
         
     | 
| 
      
 31 
     | 
    
         
            +
              </style>
         
     | 
| 
      
 32 
     | 
    
         
            +
            </head>
         
     | 
| 
      
 33 
     | 
    
         
            +
            <body class="">
         
     | 
| 
      
 34 
     | 
    
         
            +
              <div class="page">
         
     | 
| 
      
 35 
     | 
    
         
            +
                <h1>Application not running</h1>
         
     | 
| 
      
 36 
     | 
    
         
            +
                <hr>
         
     | 
| 
      
 37 
     | 
    
         
            +
                <h2>Invoker did not get any response. Please check if the application is running.<h2>
         
     | 
| 
      
 38 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 39 
     | 
    
         
            +
            </body>
         
     | 
| 
      
 40 
     | 
    
         
            +
            </html>
         
     | 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 3 
     | 
    
         
            +
                class UrlRewriter
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def select_backend_config(complete_path)
         
     | 
| 
      
 5 
     | 
    
         
            +
                    possible_matches = extract_host_from_domain(complete_path)
         
     | 
| 
      
 6 
     | 
    
         
            +
                    exact_match = nil
         
     | 
| 
      
 7 
     | 
    
         
            +
                    possible_matches.each do |match|
         
     | 
| 
      
 8 
     | 
    
         
            +
                      if match
         
     | 
| 
      
 9 
     | 
    
         
            +
                        exact_match = dns_check(process_name: match)
         
     | 
| 
      
 10 
     | 
    
         
            +
                        break if exact_match.port
         
     | 
| 
      
 11 
     | 
    
         
            +
                      end
         
     | 
| 
      
 12 
     | 
    
         
            +
                    end
         
     | 
| 
      
 13 
     | 
    
         
            +
                    exact_match
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def extract_host_from_domain(complete_path)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    matching_strings = []
         
     | 
| 
      
 18 
     | 
    
         
            +
                    tld_match_regex.map do |regexp|
         
     | 
| 
      
 19 
     | 
    
         
            +
                      if (match_result = complete_path.match(regexp))
         
     | 
| 
      
 20 
     | 
    
         
            +
                        matching_strings << match_result[1]
         
     | 
| 
      
 21 
     | 
    
         
            +
                      end
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                    matching_strings.uniq
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  private
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def tld_match_regex
         
     | 
| 
      
 29 
     | 
    
         
            +
                    tld = Invoker.config.tld
         
     | 
| 
      
 30 
     | 
    
         
            +
                    [/([\w.-]+)\.#{tld}(\:\d+)?$/, /([\w-]+)\.#{tld}(\:\d+)?$/]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def dns_check(dns_args)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    Invoker::IPC::UnixClient.send_command("dns_check", dns_args) do |dns_response|
         
     | 
| 
      
 35 
     | 
    
         
            +
                      dns_response
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,201 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Class is responsible for managing all the processes Invoker is supposed
         
     | 
| 
      
 3 
     | 
    
         
            +
              # to manage. Takes care of starting, stopping and restarting processes.
         
     | 
| 
      
 4 
     | 
    
         
            +
              class ProcessManager
         
     | 
| 
      
 5 
     | 
    
         
            +
                LABEL_COLORS = [:green, :yellow, :blue, :magenta, :cyan]
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_accessor :open_pipes, :workers
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @open_pipes = {}
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @workers = {}
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @worker_mutex = Mutex.new
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @thread_group = ThreadGroup.new
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def start_process(process_info)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  m, s = PTY.open
         
     | 
| 
      
 17 
     | 
    
         
            +
                  s.raw! # disable newline conversion.
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  pid = run_command(process_info, s)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  s.close
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  worker = CommandWorker.new(process_info.label, m, pid, select_color)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  add_worker(worker)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  wait_on_pid(process_info.label, pid)
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                # Start a process given their name
         
     | 
| 
      
 30 
     | 
    
         
            +
                # @param process_name [String] Command label of process specified in config file.
         
     | 
| 
      
 31 
     | 
    
         
            +
                def start_process_by_name(process_name)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  if process_running?(process_name)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    Invoker::Logger.puts "\nProcess '#{process_name}' is already running".colorize(:red)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    return false
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  process_info = Invoker.config.process(process_name)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  start_process(process_info) if process_info
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                # Remove a process from list of processes managed by invoker supervisor.It also
         
     | 
| 
      
 42 
     | 
    
         
            +
                # kills the process before removing it from the list.
         
     | 
| 
      
 43 
     | 
    
         
            +
                #
         
     | 
| 
      
 44 
     | 
    
         
            +
                # @param remove_message [Invoker::IPC::Message::Remove]
         
     | 
| 
      
 45 
     | 
    
         
            +
                # @return [Boolean] if process existed and was removed else false
         
     | 
| 
      
 46 
     | 
    
         
            +
                def stop_process(remove_message)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  worker = workers[remove_message.process_name]
         
     | 
| 
      
 48 
     | 
    
         
            +
                  command_label = remove_message.process_name
         
     | 
| 
      
 49 
     | 
    
         
            +
                  return false unless worker
         
     | 
| 
      
 50 
     | 
    
         
            +
                  signal_to_use = remove_message.signal || 'INT'
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  Invoker::Logger.puts("Removing #{command_label} with signal #{signal_to_use}".colorize(:red))
         
     | 
| 
      
 53 
     | 
    
         
            +
                  kill_or_remove_process(worker.pid, signal_to_use, command_label)
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                # Receive a message from user to restart a Process
         
     | 
| 
      
 57 
     | 
    
         
            +
                # @param [Invoker::IPC::Message::Reload]
         
     | 
| 
      
 58 
     | 
    
         
            +
                def restart_process(reload_message)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  command_label = reload_message.process_name
         
     | 
| 
      
 60 
     | 
    
         
            +
                  if stop_process(reload_message.remove_message)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    Invoker.commander.schedule_event(command_label, :worker_removed) do
         
     | 
| 
      
 62 
     | 
    
         
            +
                      start_process_by_name(command_label)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
                  else
         
     | 
| 
      
 65 
     | 
    
         
            +
                    start_process_by_name(command_label)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                def run_power_server
         
     | 
| 
      
 70 
     | 
    
         
            +
                  return unless Invoker.can_run_balancer?(false)
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  powerup_id = Invoker::Power::Powerup.fork_and_start
         
     | 
| 
      
 73 
     | 
    
         
            +
                  wait_on_pid("powerup_manager", powerup_id)
         
     | 
| 
      
 74 
     | 
    
         
            +
                  at_exit do
         
     | 
| 
      
 75 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 76 
     | 
    
         
            +
                      Process.kill("INT", powerup_id)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    rescue Errno::ESRCH; end
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                # Given a file descriptor returns the worker object
         
     | 
| 
      
 82 
     | 
    
         
            +
                #
         
     | 
| 
      
 83 
     | 
    
         
            +
                # @param fd [IO] an IO object with valid file descriptor
         
     | 
| 
      
 84 
     | 
    
         
            +
                # @return [Invoker::CommandWorker] The worker object which is associated with this fd
         
     | 
| 
      
 85 
     | 
    
         
            +
                def get_worker_from_fd(fd)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  open_pipes[fd.fileno]
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                def load_env(directory = nil)
         
     | 
| 
      
 90 
     | 
    
         
            +
                  directory ||= ENV['PWD']
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  if !directory || directory.empty? || !Dir.exist?(directory)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    return {}
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                  default_env = File.join(directory, '.env')
         
     | 
| 
      
 97 
     | 
    
         
            +
                  local_env = File.join(directory, '.env.local')
         
     | 
| 
      
 98 
     | 
    
         
            +
                  env = {}
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                  if File.exist?(default_env)
         
     | 
| 
      
 101 
     | 
    
         
            +
                    env.merge!(Dotenv::Environment.new(default_env))
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                  if File.exist?(local_env)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    env.merge!(Dotenv::Environment.new(local_env))
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  env
         
     | 
| 
      
 109 
     | 
    
         
            +
                end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                def kill_workers
         
     | 
| 
      
 112 
     | 
    
         
            +
                  @workers.each do |key, worker|
         
     | 
| 
      
 113 
     | 
    
         
            +
                    kill_or_remove_process(worker.pid, "INT", worker.command_label)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
                  @workers = {}
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                # List currently running commands
         
     | 
| 
      
 119 
     | 
    
         
            +
                def process_list
         
     | 
| 
      
 120 
     | 
    
         
            +
                  Invoker::IPC::Message::ListResponse.from_workers(workers)
         
     | 
| 
      
 121 
     | 
    
         
            +
                end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                private
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                def wait_on_pid(command_label, pid)
         
     | 
| 
      
 126 
     | 
    
         
            +
                  raise Invoker::Errors::ToomanyOpenConnections if @thread_group.enclosed?
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                  thread = Thread.new do
         
     | 
| 
      
 129 
     | 
    
         
            +
                    Process.wait(pid)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    message = "Process with command #{command_label} exited with status #{$?.exitstatus}"
         
     | 
| 
      
 131 
     | 
    
         
            +
                    Invoker::Logger.puts("\n#{message}".colorize(:red))
         
     | 
| 
      
 132 
     | 
    
         
            +
                    Invoker.notify_user(message)
         
     | 
| 
      
 133 
     | 
    
         
            +
                    Invoker.commander.trigger(command_label, :exit)
         
     | 
| 
      
 134 
     | 
    
         
            +
                  end
         
     | 
| 
      
 135 
     | 
    
         
            +
                  @thread_group.add(thread)
         
     | 
| 
      
 136 
     | 
    
         
            +
                end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                def select_color
         
     | 
| 
      
 139 
     | 
    
         
            +
                  selected_color = LABEL_COLORS.shift
         
     | 
| 
      
 140 
     | 
    
         
            +
                  LABEL_COLORS.push(selected_color)
         
     | 
| 
      
 141 
     | 
    
         
            +
                  selected_color
         
     | 
| 
      
 142 
     | 
    
         
            +
                end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                def process_running?(command_label)
         
     | 
| 
      
 145 
     | 
    
         
            +
                  !!workers[command_label]
         
     | 
| 
      
 146 
     | 
    
         
            +
                end
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                def kill_or_remove_process(pid, signal_to_use, command_label)
         
     | 
| 
      
 149 
     | 
    
         
            +
                  process_kill(pid, signal_to_use)
         
     | 
| 
      
 150 
     | 
    
         
            +
                  true
         
     | 
| 
      
 151 
     | 
    
         
            +
                rescue Errno::ESRCH
         
     | 
| 
      
 152 
     | 
    
         
            +
                  Invoker::Logger.puts("Killing process with #{pid} and name #{command_label} failed".colorize(:red))
         
     | 
| 
      
 153 
     | 
    
         
            +
                  remove_worker(command_label, false)
         
     | 
| 
      
 154 
     | 
    
         
            +
                  false
         
     | 
| 
      
 155 
     | 
    
         
            +
                end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                def process_kill(pid, signal_to_use)
         
     | 
| 
      
 158 
     | 
    
         
            +
                  if signal_to_use.to_i == 0
         
     | 
| 
      
 159 
     | 
    
         
            +
                    Process.kill(signal_to_use, -Process.getpgid(pid))
         
     | 
| 
      
 160 
     | 
    
         
            +
                  else
         
     | 
| 
      
 161 
     | 
    
         
            +
                    Process.kill(signal_to_use.to_i, -Process.getpgid(pid))
         
     | 
| 
      
 162 
     | 
    
         
            +
                  end
         
     | 
| 
      
 163 
     | 
    
         
            +
                end
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                # Remove worker from all collections
         
     | 
| 
      
 166 
     | 
    
         
            +
                def remove_worker(command_label, trigger_event = true)
         
     | 
| 
      
 167 
     | 
    
         
            +
                  worker = @workers[command_label]
         
     | 
| 
      
 168 
     | 
    
         
            +
                  if worker
         
     | 
| 
      
 169 
     | 
    
         
            +
                    @open_pipes.delete(worker.pipe_end.fileno)
         
     | 
| 
      
 170 
     | 
    
         
            +
                    @workers.delete(command_label)
         
     | 
| 
      
 171 
     | 
    
         
            +
                    # Move label color to front of array so it's reused first
         
     | 
| 
      
 172 
     | 
    
         
            +
                    LABEL_COLORS.delete(worker.color)
         
     | 
| 
      
 173 
     | 
    
         
            +
                    LABEL_COLORS.unshift(worker.color)
         
     | 
| 
      
 174 
     | 
    
         
            +
                  end
         
     | 
| 
      
 175 
     | 
    
         
            +
                  if trigger_event
         
     | 
| 
      
 176 
     | 
    
         
            +
                    Invoker.commander.trigger(command_label, :worker_removed)
         
     | 
| 
      
 177 
     | 
    
         
            +
                  end
         
     | 
| 
      
 178 
     | 
    
         
            +
                end
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
                # add worker to global collections
         
     | 
| 
      
 181 
     | 
    
         
            +
                def add_worker(worker)
         
     | 
| 
      
 182 
     | 
    
         
            +
                  @open_pipes[worker.pipe_end.fileno] = worker
         
     | 
| 
      
 183 
     | 
    
         
            +
                  @workers[worker.command_label] = worker
         
     | 
| 
      
 184 
     | 
    
         
            +
                  Invoker.commander.watch_for_read(worker.pipe_end)
         
     | 
| 
      
 185 
     | 
    
         
            +
                end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                def run_command(process_info, write_pipe)
         
     | 
| 
      
 188 
     | 
    
         
            +
                  command_label = process_info.label
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
                  Invoker.commander.schedule_event(command_label, :exit) { remove_worker(command_label) }
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                  env_options = load_env(process_info.dir)
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                  spawn_options = {
         
     | 
| 
      
 195 
     | 
    
         
            +
                    :chdir => process_info.dir || ENV['PWD'], :out => write_pipe, :err => write_pipe,
         
     | 
| 
      
 196 
     | 
    
         
            +
                    :pgroup => true, :close_others => true, :in => :close
         
     | 
| 
      
 197 
     | 
    
         
            +
                  }
         
     | 
| 
      
 198 
     | 
    
         
            +
                  Invoker.run_without_bundler { spawn(env_options, process_info.cmd, spawn_options) }
         
     | 
| 
      
 199 
     | 
    
         
            +
                end
         
     | 
| 
      
 200 
     | 
    
         
            +
              end
         
     | 
| 
      
 201 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,59 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 2 
     | 
    
         
            +
              class ProcessPrinter
         
     | 
| 
      
 3 
     | 
    
         
            +
                MAX_COLUMN_WIDTH = 40
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_accessor :list_response
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def initialize(list_response)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  self.list_response = list_response
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def print_table
         
     | 
| 
      
 11 
     | 
    
         
            +
                  hash_with_colors = []
         
     | 
| 
      
 12 
     | 
    
         
            +
                  list_response.processes.each do |process|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    if process.pid
         
     | 
| 
      
 14 
     | 
    
         
            +
                      hash_with_colors << colorize_hash(process, "green")
         
     | 
| 
      
 15 
     | 
    
         
            +
                    else
         
     | 
| 
      
 16 
     | 
    
         
            +
                      hash_with_colors << colorize_hash(process, "light_black")
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  Formatador.display_compact_table(hash_with_colors)
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def print_raw_text
         
     | 
| 
      
 23 
     | 
    
         
            +
                  list_response.processes.each do |process|
         
     | 
| 
      
 24 
     | 
    
         
            +
                    Formatador.display_line("[bold]Process Name : #{process.process_name}[/]")
         
     | 
| 
      
 25 
     | 
    
         
            +
                    Formatador.indent {
         
     | 
| 
      
 26 
     | 
    
         
            +
                      Formatador.display_line("Dir : #{process.dir}")
         
     | 
| 
      
 27 
     | 
    
         
            +
                      if process.pid
         
     | 
| 
      
 28 
     | 
    
         
            +
                        Formatador.display_line("PID : #{process.pid}")
         
     | 
| 
      
 29 
     | 
    
         
            +
                      else
         
     | 
| 
      
 30 
     | 
    
         
            +
                        Formatador.display_line("PID : Not Running")
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
                      Formatador.display_line("Port : #{process.port}")
         
     | 
| 
      
 33 
     | 
    
         
            +
                      Formatador.display_line("Command : #{process.shell_command}")
         
     | 
| 
      
 34 
     | 
    
         
            +
                    }
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                private
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def colorize_hash(process, color)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  hash_with_colors = {}
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  hash_with_colors['dir'] = colored_string(process.dir, color)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  hash_with_colors['pid'] = colored_string(process.pid || 'Not Running', color)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  hash_with_colors['port'] = colored_string(process.port, color)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  hash_with_colors['shell_command'] = colored_string(process.shell_command, color)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  hash_with_colors['process_name'] = colored_string(process.process_name, color)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  hash_with_colors
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def colored_string(string, color)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  string = string.to_s
         
     | 
| 
      
 53 
     | 
    
         
            +
                  if string.length > MAX_COLUMN_WIDTH
         
     | 
| 
      
 54 
     | 
    
         
            +
                    string = "#{string[0..MAX_COLUMN_WIDTH]}.."
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                  "[#{color}]#{string}[/]"
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,65 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Reactor::Reader
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_accessor :read_array
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @read_array = []
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def watch_for_read(socket)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @read_array << socket
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def handle_read_event(read_ready_fds)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  ready_fds = read_ready_fds.flatten.compact
         
     | 
| 
      
 15 
     | 
    
         
            +
                  ready_fds.each { |ready_fd| process_read(ready_fd) }
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                private
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def process_read(ready_fd)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  command_worker = Invoker.commander.get_worker_from_fd(ready_fd)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 23 
     | 
    
         
            +
                    data = read_data(ready_fd)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    send_data_to_worker(data, command_worker)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  rescue Invoker::Errors::ProcessTerminated
         
     | 
| 
      
 26 
     | 
    
         
            +
                    remove_from_read_monitoring(command_worker, ready_fd)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def send_data_to_worker(data, command_worker)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  if command_worker
         
     | 
| 
      
 32 
     | 
    
         
            +
                    command_worker.receive_data(data)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  else
         
     | 
| 
      
 34 
     | 
    
         
            +
                    Invoker::Logger.puts("No reader found for incoming data")
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def remove_from_read_monitoring(command_worker, ready_fd)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  if command_worker
         
     | 
| 
      
 40 
     | 
    
         
            +
                    read_array.delete(command_worker.pipe_end)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    command_worker.unbind
         
     | 
| 
      
 42 
     | 
    
         
            +
                  else
         
     | 
| 
      
 43 
     | 
    
         
            +
                    read_array.delete(ready_fd)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                rescue StandardError => error
         
     | 
| 
      
 46 
     | 
    
         
            +
                  Invoker::Logger.puts(error.message)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  Invoker::Logger.puts(error.backtrace)
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def read_data(ready_fd)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  sock_data = []
         
     | 
| 
      
 52 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 53 
     | 
    
         
            +
                    while(t_data = ready_fd.read_nonblock(64))
         
     | 
| 
      
 54 
     | 
    
         
            +
                      sock_data << t_data
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
                  rescue Errno::EAGAIN
         
     | 
| 
      
 57 
     | 
    
         
            +
                    return sock_data.join
         
     | 
| 
      
 58 
     | 
    
         
            +
                  rescue Errno::EWOULDBLOCK
         
     | 
| 
      
 59 
     | 
    
         
            +
                    return sock_data.join
         
     | 
| 
      
 60 
     | 
    
         
            +
                  rescue
         
     | 
| 
      
 61 
     | 
    
         
            +
                    raise Invoker::Errors::ProcessTerminated.new(ready_fd,sock_data.join)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Reactor
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_accessor :reader
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @reader = Invoker::Reactor::Reader.new
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def watch_for_read(fd)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  reader.watch_for_read(fd)
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                # Writes data to client socket and raises error if errors
         
     | 
| 
      
 14 
     | 
    
         
            +
                # while writing
         
     | 
| 
      
 15 
     | 
    
         
            +
                def send_data(socket, data)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  socket.write(data)
         
     | 
| 
      
 17 
     | 
    
         
            +
                rescue
         
     | 
| 
      
 18 
     | 
    
         
            +
                  raise Invoker::Errors::ClientDisconnected
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def monitor_for_fd_events
         
     | 
| 
      
 22 
     | 
    
         
            +
                  ready_read_fds, _ , _ = select(*options_for_select)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  if ready_read_fds && !ready_read_fds.empty?
         
     | 
| 
      
 25 
     | 
    
         
            +
                    reader.handle_read_event(ready_read_fds)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                private
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def options_for_select
         
     | 
| 
      
 32 
     | 
    
         
            +
                  [reader.read_array, [], [], 0.05]
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            require "invoker/reactor/reader"
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Version
         
     | 
| 
      
 3 
     | 
    
         
            +
                include Comparable
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_reader :major, :minor, :patch
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def initialize(number)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  t_major, t_minor, t_patch = number.split('.')
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @major = t_major.to_i
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @minor = t_minor.to_i
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @patch = t_patch.to_i
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def to_a
         
     | 
| 
      
 14 
     | 
    
         
            +
                  [major, minor, patch].compact
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def <=>(version)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  (major.to_i <=> version.major.to_i).nonzero? ||
         
     | 
| 
      
 19 
     | 
    
         
            +
                    (minor.to_i <=> version.minor.to_i).nonzero? ||
         
     | 
| 
      
 20 
     | 
    
         
            +
                    patch.to_i <=> version.patch.to_i
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def matches?(operator, number)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  version = Version.new(number)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  self == version
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  return self == version if operator == '='
         
     | 
| 
      
 28 
     | 
    
         
            +
                  return self > version  if operator == '>'
         
     | 
| 
      
 29 
     | 
    
         
            +
                  return self < version  if operator == '<'
         
     | 
| 
      
 30 
     | 
    
         
            +
                  return version <= self && version.next > self if operator  == '~>'
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def next
         
     | 
| 
      
 34 
     | 
    
         
            +
                  next_splits = to_a
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  if next_splits.length == 1
         
     | 
| 
      
 37 
     | 
    
         
            +
                    next_splits[0] += 1
         
     | 
| 
      
 38 
     | 
    
         
            +
                  else
         
     | 
| 
      
 39 
     | 
    
         
            +
                    next_splits[-2] += 1
         
     | 
| 
      
 40 
     | 
    
         
            +
                    next_splits[-1] = 0
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  Version.new(next_splits.join('.'))
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
              VERSION = "1.6.0"
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/invoker.rb
    ADDED
    
    | 
         @@ -0,0 +1,151 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            $: << File.dirname(__FILE__) unless $:.include?(File.expand_path(File.dirname(__FILE__)))
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "fileutils"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "formatador"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require "ostruct"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require "uuid"
         
     | 
| 
      
 8 
     | 
    
         
            +
            require "json"
         
     | 
| 
      
 9 
     | 
    
         
            +
            require "colorize"
         
     | 
| 
      
 10 
     | 
    
         
            +
            require "etc"
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            require "invoker/version"
         
     | 
| 
      
 13 
     | 
    
         
            +
            require "invoker/logger"
         
     | 
| 
      
 14 
     | 
    
         
            +
            require "invoker/daemon"
         
     | 
| 
      
 15 
     | 
    
         
            +
            require "invoker/cli"
         
     | 
| 
      
 16 
     | 
    
         
            +
            require "invoker/dns_cache"
         
     | 
| 
      
 17 
     | 
    
         
            +
            require "invoker/ipc"
         
     | 
| 
      
 18 
     | 
    
         
            +
            require "invoker/power/config"
         
     | 
| 
      
 19 
     | 
    
         
            +
            require "invoker/power/port_finder"
         
     | 
| 
      
 20 
     | 
    
         
            +
            require "invoker/power/setup"
         
     | 
| 
      
 21 
     | 
    
         
            +
            require "invoker/power/setup/linux_setup"
         
     | 
| 
      
 22 
     | 
    
         
            +
            require "invoker/power/setup/osx_setup"
         
     | 
| 
      
 23 
     | 
    
         
            +
            require "invoker/power/powerup"
         
     | 
| 
      
 24 
     | 
    
         
            +
            require "invoker/errors"
         
     | 
| 
      
 25 
     | 
    
         
            +
            require "invoker/parsers/procfile"
         
     | 
| 
      
 26 
     | 
    
         
            +
            require "invoker/parsers/config"
         
     | 
| 
      
 27 
     | 
    
         
            +
            require "invoker/commander"
         
     | 
| 
      
 28 
     | 
    
         
            +
            require "invoker/process_manager"
         
     | 
| 
      
 29 
     | 
    
         
            +
            require "invoker/command_worker"
         
     | 
| 
      
 30 
     | 
    
         
            +
            require "invoker/reactor"
         
     | 
| 
      
 31 
     | 
    
         
            +
            require "invoker/event/manager"
         
     | 
| 
      
 32 
     | 
    
         
            +
            require "invoker/process_printer"
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 35 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 36 
     | 
    
         
            +
                attr_accessor :config, :tail_watchers, :commander
         
     | 
| 
      
 37 
     | 
    
         
            +
                attr_accessor :dns_cache, :daemonize, :nocolors, :certificate, :private_key
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                alias_method :daemonize?, :daemonize
         
     | 
| 
      
 40 
     | 
    
         
            +
                alias_method :nocolors?, :nocolors
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def darwin?
         
     | 
| 
      
 43 
     | 
    
         
            +
                  ruby_platform.downcase.include?("darwin")
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def linux?
         
     | 
| 
      
 47 
     | 
    
         
            +
                  ruby_platform.downcase.include?("linux")
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def ruby_platform
         
     | 
| 
      
 51 
     | 
    
         
            +
                  RUBY_PLATFORM
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def load_invoker_config(file, port)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @config = Invoker::Parsers::Config.new(file, port)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  @dns_cache = Invoker::DNSCache.new(@invoker_config)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  @tail_watchers = Invoker::CLI::TailWatcher.new
         
     | 
| 
      
 58 
     | 
    
         
            +
                  @commander = Invoker::Commander.new
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                def close_socket(socket)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  socket.close
         
     | 
| 
      
 63 
     | 
    
         
            +
                rescue StandardError => error
         
     | 
| 
      
 64 
     | 
    
         
            +
                  Invoker::Logger.puts "Error removing socket #{error}"
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                def daemon
         
     | 
| 
      
 68 
     | 
    
         
            +
                  @daemon ||= Invoker::Daemon.new
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                def can_run_balancer?(throw_warning = true)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  return true if File.exist?(Invoker::Power::Config.config_file)
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  if throw_warning
         
     | 
| 
      
 75 
     | 
    
         
            +
                    Invoker::Logger.puts("Invoker has detected setup has not been run. Domain feature will not work without running setup command.".colorize(:red))
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  false
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def setup_config_location
         
     | 
| 
      
 81 
     | 
    
         
            +
                  config_dir = Invoker::Power::Config.config_dir
         
     | 
| 
      
 82 
     | 
    
         
            +
                  return config_dir if Dir.exist?(config_dir)
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  if File.exist?(config_dir)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    old_config = File.read(config_dir)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    FileUtils.rm_f(config_dir)
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                  FileUtils.mkdir(config_dir)
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  migrate_old_config(old_config, config_dir) if old_config
         
     | 
| 
      
 92 
     | 
    
         
            +
                  config_dir
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                def run_without_bundler
         
     | 
| 
      
 96 
     | 
    
         
            +
                  if defined?(Bundler)
         
     | 
| 
      
 97 
     | 
    
         
            +
                    Bundler.with_clean_env do
         
     | 
| 
      
 98 
     | 
    
         
            +
                      yield
         
     | 
| 
      
 99 
     | 
    
         
            +
                    end
         
     | 
| 
      
 100 
     | 
    
         
            +
                  else
         
     | 
| 
      
 101 
     | 
    
         
            +
                    yield
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                def notify_user(message)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  if Invoker.darwin?
         
     | 
| 
      
 107 
     | 
    
         
            +
                    run_without_bundler { check_and_notify_with_terminal_notifier(message) }
         
     | 
| 
      
 108 
     | 
    
         
            +
                  elsif Invoker.linux?
         
     | 
| 
      
 109 
     | 
    
         
            +
                    notify_with_libnotify(message)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                def check_and_notify_with_terminal_notifier(message)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  command_path = `which terminal-notifier`
         
     | 
| 
      
 115 
     | 
    
         
            +
                  if command_path && !command_path.empty?
         
     | 
| 
      
 116 
     | 
    
         
            +
                    system("terminal-notifier -message '#{message}' -title Invoker")
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                def notify_with_libnotify(message)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 122 
     | 
    
         
            +
                    require "libnotify"
         
     | 
| 
      
 123 
     | 
    
         
            +
                    Libnotify.show(body: message, summary: "Invoker", timeout: 2.5)
         
     | 
| 
      
 124 
     | 
    
         
            +
                  rescue LoadError; end
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                def migrate_old_config(old_config, config_location)
         
     | 
| 
      
 128 
     | 
    
         
            +
                  new_config = File.join(config_location, 'config')
         
     | 
| 
      
 129 
     | 
    
         
            +
                  File.open(new_config, 'w') do |file|
         
     | 
| 
      
 130 
     | 
    
         
            +
                    file.write(old_config)
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                # On some platforms `Dir.home` or `ENV['HOME']` does not return home directory of user.
         
     | 
| 
      
 135 
     | 
    
         
            +
                # this is especially true, after effective and real user id of process
         
     | 
| 
      
 136 
     | 
    
         
            +
                # has been changed.
         
     | 
| 
      
 137 
     | 
    
         
            +
                #
         
     | 
| 
      
 138 
     | 
    
         
            +
                # @return [String] home directory of the user
         
     | 
| 
      
 139 
     | 
    
         
            +
                def home
         
     | 
| 
      
 140 
     | 
    
         
            +
                  if File.writable?(Dir.home)
         
     | 
| 
      
 141 
     | 
    
         
            +
                    Dir.home
         
     | 
| 
      
 142 
     | 
    
         
            +
                  else
         
     | 
| 
      
 143 
     | 
    
         
            +
                    Etc.getpwuid(Process.uid).dir
         
     | 
| 
      
 144 
     | 
    
         
            +
                  end
         
     | 
| 
      
 145 
     | 
    
         
            +
                end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                def default_tld
         
     | 
| 
      
 148 
     | 
    
         
            +
                  'test'
         
     | 
| 
      
 149 
     | 
    
         
            +
                end
         
     | 
| 
      
 150 
     | 
    
         
            +
              end
         
     | 
| 
      
 151 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "spec_helper"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Invoker::CLI::Pinger do
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:unix_client) { Invoker::IPC::UnixClient.new }
         
     | 
| 
      
 5 
     | 
    
         
            +
              let(:pinger) { Invoker::CLI::Pinger.new(unix_client) }
         
     | 
| 
      
 6 
     | 
    
         
            +
              let(:pong) { MM::Pong.new(status: 'pong') }
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              context "If Invoker is running" do
         
     | 
| 
      
 9 
     | 
    
         
            +
                it "should return true" do
         
     | 
| 
      
 10 
     | 
    
         
            +
                  unix_client.expects(:send_and_receive).returns(pong)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  expect(pinger.invoker_running?).to be_truthy
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              context "if Invoker is not running" do
         
     | 
| 
      
 16 
     | 
    
         
            +
                it "should return false" do
         
     | 
| 
      
 17 
     | 
    
         
            +
                  unix_client.expects(:send_and_receive).returns(nil)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  unix_client.expects(:abort).never
         
     | 
| 
      
 19 
     | 
    
         
            +
                  expect(pinger.invoker_running?).to be_falsey
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     |