invoker 0.1.2 → 1.0.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 +14 -6
 - data/.gitignore +3 -0
 - data/Gemfile +0 -1
 - data/TODO +5 -0
 - data/bin/invoker +0 -3
 - data/invoker.gemspec +6 -1
 - data/lib/invoker.rb +26 -1
 - data/lib/invoker/command_listener/client.rb +1 -1
 - data/lib/invoker/command_worker.rb +2 -2
 - data/lib/invoker/commander.rb +34 -18
 - data/lib/invoker/errors.rb +1 -0
 - data/lib/invoker/parsers/config.rb +68 -4
 - data/lib/invoker/parsers/option_parser.rb +18 -5
 - data/lib/invoker/power.rb +6 -0
 - data/lib/invoker/power/balancer.rb +118 -0
 - data/lib/invoker/power/config.rb +53 -0
 - data/lib/invoker/power/dns.rb +37 -0
 - data/lib/invoker/power/port_finder.rb +49 -0
 - data/lib/invoker/power/powerup.rb +26 -0
 - data/lib/invoker/power/setup.rb +134 -0
 - data/lib/invoker/runner.rb +8 -2
 - data/lib/invoker/version.rb +1 -1
 - data/readme.md +29 -0
 - data/spec/invoker/commander_spec.rb +2 -2
 - data/spec/invoker/config_spec.rb +106 -2
 - data/spec/invoker/invoker_spec.rb +53 -0
 - data/spec/invoker/power/config_spec.rb +24 -0
 - data/spec/invoker/power/port_finder_spec.rb +16 -0
 - data/spec/invoker/power/setup_spec.rb +87 -0
 - metadata +104 -18
 
| 
         @@ -0,0 +1,53 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "yaml"
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 3 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 4 
     | 
    
         
            +
                # Save and Load Invoker::Power config
         
     | 
| 
      
 5 
     | 
    
         
            +
                class ConfigExists < StandardError; end
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Config
         
     | 
| 
      
 7 
     | 
    
         
            +
                  CONFIG_LOCATION = File.join(ENV['HOME'], ".invoker")
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def self.has_config?
         
     | 
| 
      
 9 
     | 
    
         
            +
                    File.exists?(CONFIG_LOCATION)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def self.create(options = {})
         
     | 
| 
      
 13 
     | 
    
         
            +
                    if has_config?
         
     | 
| 
      
 14 
     | 
    
         
            +
                      raise ConfigExists, "Config file already exists at location #{CONFIG_LOCATION}"
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
                    config = new(options)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    config.save
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def initialize(options = {})
         
     | 
| 
      
 21 
     | 
    
         
            +
                    @config = options
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def self.load_config
         
     | 
| 
      
 25 
     | 
    
         
            +
                    config_hash = File.open(CONFIG_LOCATION, "r") { |fl| YAML.load(fl) }
         
     | 
| 
      
 26 
     | 
    
         
            +
                    new(config_hash)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def dns_port=(dns_port)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @config[:dns_port] = dns_port
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def http_port=(http_port)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    @config[:http_port] = http_port
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  def ipfw_rule_number=(ipfw_rule_number)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    @config[:ipfw_rule_number] = ipfw_rule_number
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  def dns_port; @config[:dns_port]; end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  def http_port; @config[:http_port]; end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  def ipfw_rule_number; @config[:ipfw_rule_number]; end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  def save
         
     | 
| 
      
 46 
     | 
    
         
            +
                    File.open(CONFIG_LOCATION, "w") do |fl|
         
     | 
| 
      
 47 
     | 
    
         
            +
                      YAML.dump(@config, fl)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
                    self
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "logger"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                class DNS
         
     | 
| 
      
 8 
     | 
    
         
            +
                  IN = Resolv::DNS::Resource::IN
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def self.server_ports
         
     | 
| 
      
 10 
     | 
    
         
            +
                    [
         
     | 
| 
      
 11 
     | 
    
         
            +
                      [:udp, '127.0.0.1', Invoker::CONFIG.dns_port],
         
     | 
| 
      
 12 
     | 
    
         
            +
                      [:tcp, '127.0.0.1', Invoker::CONFIG.dns_port]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def self.run_dns
         
     | 
| 
      
 17 
     | 
    
         
            +
                    RubyDNS::run_server(:listen => server_ports) do
         
     | 
| 
      
 18 
     | 
    
         
            +
                      on(:start) do
         
     | 
| 
      
 19 
     | 
    
         
            +
                        @logger.level = ::Logger::WARN
         
     | 
| 
      
 20 
     | 
    
         
            +
                      end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                      # For this exact address record, return an IP address
         
     | 
| 
      
 23 
     | 
    
         
            +
                      match(/.*\.dev/, IN::A) do |transaction|
         
     | 
| 
      
 24 
     | 
    
         
            +
                        transaction.respond!("127.0.0.1")
         
     | 
| 
      
 25 
     | 
    
         
            +
                      end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                      # Default DNS handler
         
     | 
| 
      
 28 
     | 
    
         
            +
                      otherwise do |transaction|
         
     | 
| 
      
 29 
     | 
    
         
            +
                        transaction.failure!(:NXDomain)
         
     | 
| 
      
 30 
     | 
    
         
            +
                      end
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,49 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 3 
     | 
    
         
            +
                class PortFinder
         
     | 
| 
      
 4 
     | 
    
         
            +
                  STARTING_PORT = 23400
         
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_accessor :dns_port, :http_port, :starting_port
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @starting_port = STARTING_PORT
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @ports = []
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @dns_port = nil
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @http_port = nil
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def find_ports
         
     | 
| 
      
 14 
     | 
    
         
            +
                    STARTING_PORT.upto(STARTING_PORT + 100) do |port|
         
     | 
| 
      
 15 
     | 
    
         
            +
                      break if @ports.size > 2
         
     | 
| 
      
 16 
     | 
    
         
            +
                      if check_if_port_is_open(port)
         
     | 
| 
      
 17 
     | 
    
         
            +
                        @ports << port
         
     | 
| 
      
 18 
     | 
    
         
            +
                      else
         
     | 
| 
      
 19 
     | 
    
         
            +
                        next
         
     | 
| 
      
 20 
     | 
    
         
            +
                      end
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @dns_port = @ports[0]
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @http_port = @ports[1]
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  private
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def check_if_port_is_open(port)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    socket_flag = true
         
     | 
| 
      
 29 
     | 
    
         
            +
                    sockets = nil
         
     | 
| 
      
 30 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 31 
     | 
    
         
            +
                      sockets = Socket.tcp_server_sockets(port)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      socket_flag = false if sockets.size <= 1
         
     | 
| 
      
 33 
     | 
    
         
            +
                    rescue Errno::EADDRINUSE
         
     | 
| 
      
 34 
     | 
    
         
            +
                      socket_flag = false
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
                    sockets && close_socket_pairs(sockets)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    socket_flag
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def close_socket_pairs(sockets)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    sockets.each { |s| s.close }
         
     | 
| 
      
 42 
     | 
    
         
            +
                  rescue
         
     | 
| 
      
 43 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 2 
     | 
    
         
            +
              # power is really a stupid pun on pow.
         
     | 
| 
      
 3 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 4 
     | 
    
         
            +
                class Powerup
         
     | 
| 
      
 5 
     | 
    
         
            +
                  def self.fork_and_start
         
     | 
| 
      
 6 
     | 
    
         
            +
                    powerup = new()
         
     | 
| 
      
 7 
     | 
    
         
            +
                    fork { powerup.run }
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def run
         
     | 
| 
      
 11 
     | 
    
         
            +
                    EM.epoll
         
     | 
| 
      
 12 
     | 
    
         
            +
                    EM.run {
         
     | 
| 
      
 13 
     | 
    
         
            +
                      trap("TERM") { stop }
         
     | 
| 
      
 14 
     | 
    
         
            +
                      trap("INT") { stop }
         
     | 
| 
      
 15 
     | 
    
         
            +
                      DNS.run_dns()
         
     | 
| 
      
 16 
     | 
    
         
            +
                      Balancer.run()
         
     | 
| 
      
 17 
     | 
    
         
            +
                    }
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def stop
         
     | 
| 
      
 21 
     | 
    
         
            +
                    Invoker::Logger.puts "Terminating Proxy/Server"
         
     | 
| 
      
 22 
     | 
    
         
            +
                    EventMachine.stop
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,134 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "highline/import"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Setup
         
     | 
| 
      
 6 
     | 
    
         
            +
                  RESOLVER_FILE = "/etc/resolver/dev"
         
     | 
| 
      
 7 
     | 
    
         
            +
                  FIREWALL_PLIST_FILE = "/Library/LaunchDaemons/com.codemancers.invoker.firewall.plist"
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def self.install
         
     | 
| 
      
 9 
     | 
    
         
            +
                    installer = new
         
     | 
| 
      
 10 
     | 
    
         
            +
                    unless installer.check_if_already_setup?
         
     | 
| 
      
 11 
     | 
    
         
            +
                      installer.setup_invoker
         
     | 
| 
      
 12 
     | 
    
         
            +
                    else
         
     | 
| 
      
 13 
     | 
    
         
            +
                      Invoker::Logger.puts("The setup has been already run.".color(:red))
         
     | 
| 
      
 14 
     | 
    
         
            +
                    end
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def setup_invoker
         
     | 
| 
      
 18 
     | 
    
         
            +
                    if setup_resolver_file
         
     | 
| 
      
 19 
     | 
    
         
            +
                      find_open_ports
         
     | 
| 
      
 20 
     | 
    
         
            +
                      install_resolver(port_finder.dns_port)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      install_firewall(port_finder.http_port)
         
     | 
| 
      
 22 
     | 
    
         
            +
                      flush_dns_rules()
         
     | 
| 
      
 23 
     | 
    
         
            +
                      # Before writing the config file, drop down to a normal user
         
     | 
| 
      
 24 
     | 
    
         
            +
                      drop_to_normal_user()
         
     | 
| 
      
 25 
     | 
    
         
            +
                      create_config_file()
         
     | 
| 
      
 26 
     | 
    
         
            +
                    else
         
     | 
| 
      
 27 
     | 
    
         
            +
                      Invoker::Logger.puts("Invoker is not configured to serve from subdomains".color(:red))
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
                    self
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def drop_to_normal_user
         
     | 
| 
      
 33 
     | 
    
         
            +
                    EventMachine.set_effective_user(ENV["SUDO_USER"])
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  def flush_dns_rules
         
     | 
| 
      
 37 
     | 
    
         
            +
                    system("dscacheutil -flushcache")
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def create_config_file
         
     | 
| 
      
 41 
     | 
    
         
            +
                    Invoker::Power::Config.create(
         
     | 
| 
      
 42 
     | 
    
         
            +
                      dns_port: port_finder.dns_port,
         
     | 
| 
      
 43 
     | 
    
         
            +
                      http_port: port_finder.http_port
         
     | 
| 
      
 44 
     | 
    
         
            +
                    )
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  def find_open_ports
         
     | 
| 
      
 48 
     | 
    
         
            +
                    port_finder.find_ports()
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def port_finder
         
     | 
| 
      
 52 
     | 
    
         
            +
                    @port_finder ||= Invoker::Power::PortFinder.new()
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def install_resolver(dns_port)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    File.open(RESOLVER_FILE, "w") { |fl|
         
     | 
| 
      
 57 
     | 
    
         
            +
                      fl.write(resolve_string(dns_port))
         
     | 
| 
      
 58 
     | 
    
         
            +
                    }
         
     | 
| 
      
 59 
     | 
    
         
            +
                  rescue Errno::EACCES
         
     | 
| 
      
 60 
     | 
    
         
            +
                    Invoker::Logger.puts("Running setup requires root access, please rerun it with sudo".color(:red))
         
     | 
| 
      
 61 
     | 
    
         
            +
                    raise
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  def check_if_already_setup?
         
     | 
| 
      
 65 
     | 
    
         
            +
                    File.exists?(Invoker::Power::Config::CONFIG_LOCATION)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  def install_firewall(balancer_port)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    File.open(FIREWALL_PLIST_FILE, "w") { |fl|
         
     | 
| 
      
 70 
     | 
    
         
            +
                      fl.write(plist_string(balancer_port))
         
     | 
| 
      
 71 
     | 
    
         
            +
                    }
         
     | 
| 
      
 72 
     | 
    
         
            +
                    system("launchctl unload -w #{FIREWALL_PLIST_FILE} 2>/dev/null")
         
     | 
| 
      
 73 
     | 
    
         
            +
                    system("launchctl load -Fw #{FIREWALL_PLIST_FILE} 2>/dev/null")
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  # Ripped from POW code
         
     | 
| 
      
 77 
     | 
    
         
            +
                  def plist_string(balancer_port)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    plist =<<-EOD
         
     | 
| 
      
 79 
     | 
    
         
            +
            <?xml version="1.0" encoding="UTF-8"?>
         
     | 
| 
      
 80 
     | 
    
         
            +
            <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
         
     | 
| 
      
 81 
     | 
    
         
            +
            <plist version="1.0">
         
     | 
| 
      
 82 
     | 
    
         
            +
            <dict>
         
     | 
| 
      
 83 
     | 
    
         
            +
            <key>Label</key>
         
     | 
| 
      
 84 
     | 
    
         
            +
            <string>com.codemancers.invoker</string>
         
     | 
| 
      
 85 
     | 
    
         
            +
            <key>ProgramArguments</key>
         
     | 
| 
      
 86 
     | 
    
         
            +
            <array>
         
     | 
| 
      
 87 
     | 
    
         
            +
            <string>sh</string>
         
     | 
| 
      
 88 
     | 
    
         
            +
            <string>-c</string>
         
     | 
| 
      
 89 
     | 
    
         
            +
            <string>#{firewall_command(balancer_port)}</string>
         
     | 
| 
      
 90 
     | 
    
         
            +
            </array>
         
     | 
| 
      
 91 
     | 
    
         
            +
            <key>RunAtLoad</key>
         
     | 
| 
      
 92 
     | 
    
         
            +
            <true/>
         
     | 
| 
      
 93 
     | 
    
         
            +
            <key>UserName</key>
         
     | 
| 
      
 94 
     | 
    
         
            +
            <string>root</string>
         
     | 
| 
      
 95 
     | 
    
         
            +
            </dict>
         
     | 
| 
      
 96 
     | 
    
         
            +
            </plist>
         
     | 
| 
      
 97 
     | 
    
         
            +
                    EOD
         
     | 
| 
      
 98 
     | 
    
         
            +
                    plist
         
     | 
| 
      
 99 
     | 
    
         
            +
                  end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                  def resolve_string(dns_port)
         
     | 
| 
      
 102 
     | 
    
         
            +
                    string =<<-EOD
         
     | 
| 
      
 103 
     | 
    
         
            +
            nameserver 127.0.0.1
         
     | 
| 
      
 104 
     | 
    
         
            +
            port #{dns_port}
         
     | 
| 
      
 105 
     | 
    
         
            +
                    EOD
         
     | 
| 
      
 106 
     | 
    
         
            +
                    string
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  # Ripped from Pow code
         
     | 
| 
      
 110 
     | 
    
         
            +
                  def firewall_command(balancer_port)
         
     | 
| 
      
 111 
     | 
    
         
            +
                    "ipfw add fwd 127.0.0.1,#{balancer_port} tcp from any to me dst-port 80 in"\
         
     | 
| 
      
 112 
     | 
    
         
            +
                      "&& sysctl -w net.inet.ip.forwarding=1"
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                  def setup_resolver_file
         
     | 
| 
      
 116 
     | 
    
         
            +
                    return true unless File.exists?(RESOLVER_FILE)
         
     | 
| 
      
 117 
     | 
    
         
            +
                    Invoker::Logger.puts "Invoker has detected an existing Pow installation. We recommend "\
         
     | 
| 
      
 118 
     | 
    
         
            +
                      "that you uninstall pow and rerun this setup.".color(:red)
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                    Invoker::Logger.puts "If you have already uninstalled Pow, proceed with installation"\
         
     | 
| 
      
 121 
     | 
    
         
            +
                      " by pressing y/n."
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                    replace_resolver_flag = agree("Replace Pow configuration (y/n) : ")
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    if replace_resolver_flag
         
     | 
| 
      
 126 
     | 
    
         
            +
                      Invoker::Logger.puts "Invoker has overwritten one or more files created by Pow. "\
         
     | 
| 
      
 127 
     | 
    
         
            +
                      "If .dev domains still don't resolve locally. Try turning off the wi-fi"\
         
     | 
| 
      
 128 
     | 
    
         
            +
                      " and turning it on. It will force OSX to reload network configuration".color(:green)
         
     | 
| 
      
 129 
     | 
    
         
            +
                    end
         
     | 
| 
      
 130 
     | 
    
         
            +
                    replace_resolver_flag
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
              end
         
     | 
| 
      
 134 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/invoker/runner.rb
    CHANGED
    
    | 
         @@ -14,6 +14,8 @@ module Invoker 
     | 
|
| 
       14 
14 
     | 
    
         
             
                def self.run_command(selected_command)
         
     | 
| 
       15 
15 
     | 
    
         
             
                  return unless selected_command
         
     | 
| 
       16 
16 
     | 
    
         
             
                  case selected_command.command
         
     | 
| 
      
 17 
     | 
    
         
            +
                  when 'setup'
         
     | 
| 
      
 18 
     | 
    
         
            +
                    setup_pow(selected_command)
         
     | 
| 
       17 
19 
     | 
    
         
             
                  when 'start'
         
     | 
| 
       18 
20 
     | 
    
         
             
                    start_server(selected_command)
         
     | 
| 
       19 
21 
     | 
    
         
             
                  when 'add'
         
     | 
| 
         @@ -29,8 +31,12 @@ module Invoker 
     | 
|
| 
       29 
31 
     | 
    
         
             
                  end
         
     | 
| 
       30 
32 
     | 
    
         
             
                end
         
     | 
| 
       31 
33 
     | 
    
         | 
| 
      
 34 
     | 
    
         
            +
                def self.setup_pow(selected_command)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  Invoker::Power::Setup.install()
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
       32 
38 
     | 
    
         
             
                def self.start_server(selected_command)
         
     | 
| 
       33 
     | 
    
         
            -
                  config = Invoker::Parsers::Config.new(selected_command.file)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  config = Invoker::Parsers::Config.new(selected_command.file, selected_command.port)
         
     | 
| 
       34 
40 
     | 
    
         
             
                  Invoker.const_set(:CONFIG, config)
         
     | 
| 
       35 
41 
     | 
    
         
             
                  warn_about_terminal_notifier()
         
     | 
| 
       36 
42 
     | 
    
         
             
                  commander = Invoker::Commander.new()
         
     | 
| 
         @@ -71,7 +77,7 @@ module Invoker 
     | 
|
| 
       71 
77 
     | 
    
         
             
                  if RUBY_PLATFORM.downcase.include?("darwin")
         
     | 
| 
       72 
78 
     | 
    
         
             
                    command_path = `which terminal-notifier`
         
     | 
| 
       73 
79 
     | 
    
         
             
                    if !command_path || command_path.empty?
         
     | 
| 
       74 
     | 
    
         
            -
                      Invoker::Logger.puts("You can enable OSX notification for processes by installing terminal-notifier gem".red)
         
     | 
| 
      
 80 
     | 
    
         
            +
                      Invoker::Logger.puts("You can enable OSX notification for processes by installing terminal-notifier gem".color(:red))
         
     | 
| 
       75 
81 
     | 
    
         
             
                    end
         
     | 
| 
       76 
82 
     | 
    
         
             
                  end
         
     | 
| 
       77 
83 
     | 
    
         
             
                end
         
     | 
    
        data/lib/invoker/version.rb
    CHANGED
    
    
    
        data/readme.md
    CHANGED
    
    | 
         @@ -26,6 +26,35 @@ You need to start by creating a `ini` file which will define processes you want 
     | 
|
| 
       26 
26 
     | 
    
         
             
                directory = /home/gnufied/god_particle
         
     | 
| 
       27 
27 
     | 
    
         
             
                command = zsh -c 'bundle exec ruby script/event_server'
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
      
 29 
     | 
    
         
            +
            ## Invoker as Pow alternative
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            Invoker now supports pow like `.dev` subdomain. It will automatically
         
     | 
| 
      
 32 
     | 
    
         
            +
            make all your app servers defined on subdomain `label.dev`.
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            To make it work though, you need to run following command, just once from anywhere:
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                ~> sudo invoker setup
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            Now because invoker is making your app server available on a subdomain. It requires
         
     | 
| 
      
 39 
     | 
    
         
            +
            control over port on which your applications will be listening. This can be simply
         
     | 
| 
      
 40 
     | 
    
         
            +
            done by replacing specific port number in `ini` file with `$PORT`. For example:
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                [spree]
         
     | 
| 
      
 43 
     | 
    
         
            +
                directory = /home/gnufied/spree
         
     | 
| 
      
 44 
     | 
    
         
            +
                command = zsh -c 'bundle exec rails s -p $PORT'
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                [api]
         
     | 
| 
      
 47 
     | 
    
         
            +
                directory = /home/gnufied/api
         
     | 
| 
      
 48 
     | 
    
         
            +
                command = zsh -c 'bundle exec rails s -p $PORT'
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                [events]
         
     | 
| 
      
 51 
     | 
    
         
            +
                directory = /home/gnufied/god_particle
         
     | 
| 
      
 52 
     | 
    
         
            +
                command = zsh -c 'bundle exec ruby script/event_server'
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            The subdomain feature won't work for apps which don't use `$PORT` placeholder.
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            ## Running Invoker
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
       29 
58 
     | 
    
         
             
            After that you can start process manager via:
         
     | 
| 
       30 
59 
     | 
    
         | 
| 
       31 
60 
     | 
    
         
             
                ~> invoker start invoker.ini
         
     | 
| 
         @@ -23,6 +23,7 @@ describe "Invoker::Commander" do 
     | 
|
| 
       23 
23 
     | 
    
         | 
| 
       24 
24 
     | 
    
         
             
                it "should find command by label and start it, if found" do
         
     | 
| 
       25 
25 
     | 
    
         
             
                  invoker_config.stubs(:processes).returns([OpenStruct.new(:label => "resque", :cmd => "foo", :dir => "bar")])
         
     | 
| 
      
 26 
     | 
    
         
            +
                  invoker_config.expects(:process).returns(OpenStruct.new(:label => "resque", :cmd => "foo", :dir => "bar"))
         
     | 
| 
       26 
27 
     | 
    
         
             
                  @commander.expects(:add_command).returns(true)
         
     | 
| 
       27 
28 
     | 
    
         | 
| 
       28 
29 
     | 
    
         
             
                  @commander.add_command_by_label("resque")
         
     | 
| 
         @@ -97,8 +98,7 @@ describe "Invoker::Commander" do 
     | 
|
| 
       97 
98 
     | 
    
         | 
| 
       98 
99 
     | 
    
         
             
                  worker.should.not.equal nil
         
     | 
| 
       99 
100 
     | 
    
         
             
                  worker.command_label.should.equal "sleep"
         
     | 
| 
       100 
     | 
    
         
            -
                  worker.color.should.equal  
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
      
 101 
     | 
    
         
            +
                  worker.color.should.equal :green
         
     | 
| 
       102 
102 
     | 
    
         | 
| 
       103 
103 
     | 
    
         
             
                  pipe_end_worker = @commander.open_pipes[worker.pipe_end.fileno]
         
     | 
| 
       104 
104 
     | 
    
         
             
                  pipe_end_worker.should.not.equal nil
         
     | 
    
        data/spec/invoker/config_spec.rb
    CHANGED
    
    | 
         @@ -16,12 +16,116 @@ command = ruby try_sleep.rb 
     | 
|
| 
       16 
16 
     | 
    
         
             
                    file.write(config_data)
         
     | 
| 
       17 
17 
     | 
    
         
             
                    file.close
         
     | 
| 
       18 
18 
     | 
    
         
             
                    lambda {
         
     | 
| 
       19 
     | 
    
         
            -
                      Invoker::Parsers::Config.new(file.path)
         
     | 
| 
      
 19 
     | 
    
         
            +
                      Invoker::Parsers::Config.new(file.path, 9000)
         
     | 
| 
       20 
20 
     | 
    
         
             
                    }.should.raise(Invoker::Errors::InvalidConfig)
         
     | 
| 
       21 
21 
     | 
    
         
             
                  ensure
         
     | 
| 
       22 
22 
     | 
    
         
             
                    file.unlink()
         
     | 
| 
       23 
23 
     | 
    
         
             
                  end
         
     | 
| 
       24 
24 
     | 
    
         
             
                end
         
     | 
| 
       25 
25 
     | 
    
         
             
              end
         
     | 
| 
       26 
     | 
    
         
            -
            end
         
     | 
| 
       27 
26 
     | 
    
         | 
| 
      
 27 
     | 
    
         
            +
              describe "for ports" do
         
     | 
| 
      
 28 
     | 
    
         
            +
                it "should replace port in commands" do
         
     | 
| 
      
 29 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 30 
     | 
    
         
            +
                    file = Tempfile.new("invalid_config.ini")
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    config_data =<<-EOD
         
     | 
| 
      
 33 
     | 
    
         
            +
            [try_sleep]
         
     | 
| 
      
 34 
     | 
    
         
            +
            directory = /tmp
         
     | 
| 
      
 35 
     | 
    
         
            +
            command = ruby try_sleep.rb -p $PORT
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            [ls]
         
     | 
| 
      
 38 
     | 
    
         
            +
            directory = /tmp
         
     | 
| 
      
 39 
     | 
    
         
            +
            command = ls -p $PORT
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            [noport]
         
     | 
| 
      
 42 
     | 
    
         
            +
            directory = /tmp
         
     | 
| 
      
 43 
     | 
    
         
            +
            command = ls
         
     | 
| 
      
 44 
     | 
    
         
            +
                  EOD
         
     | 
| 
      
 45 
     | 
    
         
            +
                    file.write(config_data)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    file.close
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    config = Invoker::Parsers::Config.new(file.path, 9000)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    command1 = config.processes.first
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    command1.port.should == 9001
         
     | 
| 
      
 52 
     | 
    
         
            +
                    command1.cmd.should =~ /9001/
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    command2 = config.processes[1]
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    command2.port.should == 9002
         
     | 
| 
      
 57 
     | 
    
         
            +
                    command2.cmd.should =~ /9002/
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    command2 = config.processes[2]
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    command2.port.should == nil
         
     | 
| 
      
 62 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 63 
     | 
    
         
            +
                    file.unlink()
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                it "should use port from separate option" do
         
     | 
| 
      
 68 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 69 
     | 
    
         
            +
                    file = Tempfile.new("invalid_config.ini")
         
     | 
| 
      
 70 
     | 
    
         
            +
                    config_data =<<-EOD
         
     | 
| 
      
 71 
     | 
    
         
            +
            [try_sleep]
         
     | 
| 
      
 72 
     | 
    
         
            +
            directory = /tmp
         
     | 
| 
      
 73 
     | 
    
         
            +
            command = ruby try_sleep.rb -p $PORT
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            [ls]
         
     | 
| 
      
 76 
     | 
    
         
            +
            directory = /tmp
         
     | 
| 
      
 77 
     | 
    
         
            +
            port = 3000
         
     | 
| 
      
 78 
     | 
    
         
            +
            command = pwd
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
            [noport]
         
     | 
| 
      
 81 
     | 
    
         
            +
            directory = /tmp
         
     | 
| 
      
 82 
     | 
    
         
            +
            command = ls
         
     | 
| 
      
 83 
     | 
    
         
            +
                  EOD
         
     | 
| 
      
 84 
     | 
    
         
            +
                    file.write(config_data)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    file.close
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                    config = Invoker::Parsers::Config.new(file.path, 9000)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    command1 = config.processes.first
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                    command1.port.should == 9001
         
     | 
| 
      
 91 
     | 
    
         
            +
                    command1.cmd.should =~ /9001/
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                    command2 = config.processes[1]
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                    command2.port.should == 3000
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                    command2 = config.processes[2]
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    command2.port.should == nil
         
     | 
| 
      
 100 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 101 
     | 
    
         
            +
                    file.unlink()
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
              end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
              describe "loading power config" do
         
     | 
| 
      
 107 
     | 
    
         
            +
                before do
         
     | 
| 
      
 108 
     | 
    
         
            +
                  @file = Tempfile.new("config.ini")
         
     | 
| 
      
 109 
     | 
    
         
            +
                end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                it "does not load config if platform is not darwin" do
         
     | 
| 
      
 112 
     | 
    
         
            +
                  Invoker.expects(:darwin?).returns(false)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  Invoker::Power::Config.expects(:load_config).never
         
     | 
| 
      
 114 
     | 
    
         
            +
                  Invoker::Parsers::Config.new(@file.path, 9000)
         
     | 
| 
      
 115 
     | 
    
         
            +
                end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                it "does not load config if platform is darwin but there is no power config file" do
         
     | 
| 
      
 118 
     | 
    
         
            +
                  Invoker.expects(:darwin?).returns(true)
         
     | 
| 
      
 119 
     | 
    
         
            +
                  File.expects(:exists?).with(Invoker::Power::Config::CONFIG_LOCATION).returns(false)
         
     | 
| 
      
 120 
     | 
    
         
            +
                  Invoker::Power::Config.expects(:load_config).never
         
     | 
| 
      
 121 
     | 
    
         
            +
                  Invoker::Parsers::Config.new(@file.path, 9000)
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                it "loads config if platform is darwin and power config file exists" do
         
     | 
| 
      
 125 
     | 
    
         
            +
                  Invoker.expects(:darwin?).returns(true)
         
     | 
| 
      
 126 
     | 
    
         
            +
                  File.expects(:exists?).with(Invoker::Power::Config::CONFIG_LOCATION).returns(true)
         
     | 
| 
      
 127 
     | 
    
         
            +
                  Invoker::Power::Config.expects(:load_config).once
         
     | 
| 
      
 128 
     | 
    
         
            +
                  Invoker::Parsers::Config.new(@file.path, 9000)
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
              end
         
     | 
| 
      
 131 
     | 
    
         
            +
            end
         
     |