invoker 1.4.1 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.rubocop.yml +0 -1
  4. data/.travis.yml +4 -2
  5. data/Dockerfile +7 -0
  6. data/Gemfile +3 -0
  7. data/Rakefile +8 -0
  8. data/examples/hello_sinatra.rb +26 -0
  9. data/examples/sample.ini +3 -0
  10. data/invoker.gemspec +2 -1
  11. data/lib/invoker.rb +20 -3
  12. data/lib/invoker/cli.rb +32 -6
  13. data/lib/invoker/dns_cache.rb +2 -2
  14. data/lib/invoker/ipc/add_http_command.rb +1 -1
  15. data/lib/invoker/ipc/dns_check_command.rb +2 -1
  16. data/lib/invoker/ipc/message.rb +2 -2
  17. data/lib/invoker/parsers/config.rb +4 -1
  18. data/lib/invoker/power/balancer.rb +16 -4
  19. data/lib/invoker/power/config.rb +3 -2
  20. data/lib/invoker/power/dns.rb +1 -1
  21. data/lib/invoker/power/pf_migrate.rb +1 -1
  22. data/lib/invoker/power/setup.rb +43 -4
  23. data/lib/invoker/power/setup/distro/arch.rb +1 -4
  24. data/lib/invoker/power/setup/distro/base.rb +23 -21
  25. data/lib/invoker/power/setup/distro/debian.rb +1 -1
  26. data/lib/invoker/power/setup/distro/opensuse.rb +11 -0
  27. data/lib/invoker/power/setup/distro/redhat.rb +1 -7
  28. data/lib/invoker/power/setup/files/invoker_forwarder.sh.erb +17 -0
  29. data/lib/invoker/power/setup/files/socat_invoker.service +12 -0
  30. data/lib/invoker/power/setup/linux_setup.rb +46 -29
  31. data/lib/invoker/power/setup/osx_setup.rb +16 -28
  32. data/lib/invoker/power/url_rewriter.rb +6 -3
  33. data/lib/invoker/process_manager.rb +13 -7
  34. data/lib/invoker/version.rb +1 -1
  35. data/readme.md +4 -4
  36. data/spec/invoker/commander_spec.rb +19 -8
  37. data/spec/invoker/config_spec.rb +22 -27
  38. data/spec/invoker/invoker_spec.rb +2 -1
  39. data/spec/invoker/ipc/client_handler_spec.rb +11 -1
  40. data/spec/invoker/power/config_spec.rb +2 -1
  41. data/spec/invoker/power/pf_migrate_spec.rb +7 -0
  42. data/spec/invoker/power/setup/linux_setup_spec.rb +57 -9
  43. data/spec/invoker/power/setup/osx_setup_spec.rb +22 -8
  44. data/spec/invoker/power/url_rewriter_spec.rb +33 -1
  45. data/spec/invoker/power/web_sockets_spec.rb +61 -0
  46. data/spec/invoker/process_manager_spec.rb +34 -2
  47. data/spec/spec_helper.rb +12 -16
  48. metadata +27 -35
  49. data/spec/support/mock_setup_file.rb +0 -64
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6fe046d5488f7cd2fe7b7aa6b10df51a0e7b3801
4
- data.tar.gz: d9e3cf26e7bab5677a9fa6d6739ada78a6d79304
3
+ metadata.gz: 35e208abbd4ff8979f1d1ecc0c5896ef3b460799
4
+ data.tar.gz: 6e2e65d581a9705f5358bf8076654a9b34185683
5
5
  SHA512:
6
- metadata.gz: 6119c3f2f8c620956833ffa74af58161d8480dbe296c616cd2366242d3b145c62e8c7b88fb514440bd7772fc56b8c86e84888cc7455e3e1d068dc1bd4cd2e190
7
- data.tar.gz: 65351289ad3f5f5fea61ed8600ffaa03a6306c3592fbe504eb76604d1d0939692c35fc7aea014f11a36dec120e976c90c0ad8de04257a45d8fd4796e6a35c3f6
6
+ metadata.gz: 44eecb467482053d584410fbc279c192e1dc25b7c97f245bf058f20e7bac0a12d9c0c73487d0ab0652d632030ef3f39741449dd5d30391104ef4ec9cc165091f
7
+ data.tar.gz: a4108aab6742c8b02886423a341c2d59c47deba5645d73b671686aff4cac4f7af668677645bb5444b8d37fe260afa8e05631ca249ba9d41f6bc9396426534a30
data/.gitignore CHANGED
@@ -2,7 +2,6 @@ invoker.ini
2
2
  config/
3
3
  *.gem
4
4
  pkg/
5
- *.ini
6
5
  tags
7
6
  .rvmrc
8
7
  vendor/
@@ -1,5 +1,4 @@
1
1
  AllCops:
2
- RunRailsCops: false
3
2
  Excludes:
4
3
  - db/**
5
4
 
@@ -1,8 +1,10 @@
1
1
  language: ruby
2
+ cache: bundler
3
+ sudo: false
2
4
  rvm:
3
- - 1.9.3
4
5
  - 2.0.0
5
6
  - 2.1.0
7
+ - 2.2.0
8
+ - 2.3.0
6
9
 
7
10
  script: bundle exec rake spec
8
-
@@ -0,0 +1,7 @@
1
+ FROM ruby:2.3
2
+
3
+ MAINTAINER hemant@codemancers.com
4
+
5
+ RUN apt-get update && apt-get -y install dnsmasq socat
6
+
7
+ CMD cd /invoker && bundle install --path vendor/ && bundle exec rake spec
data/Gemfile CHANGED
@@ -2,6 +2,9 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
+ gem 'websocket-eventmachine-server', require: false
6
+ gem 'websocket-eventmachine-client', require: false
7
+
5
8
  group :development do
6
9
  gem 'pry'
7
10
  gem 'tins', '~>1.4.0'
data/Rakefile CHANGED
@@ -5,3 +5,11 @@ RSpec::Core::RakeTask.new
5
5
 
6
6
  task :default => :spec
7
7
  task :test => :spec
8
+
9
+ current_directory = File.expand_path(File.dirname(__FILE__))
10
+
11
+ desc "run specs inside docker"
12
+ task :docker_spec do
13
+ system("docker build -t invoker-ruby . ")
14
+ system("docker run --name invoker-rspec --rm -v #{current_directory}:/invoker -t invoker-ruby")
15
+ end
@@ -0,0 +1,26 @@
1
+ # myapp.rb
2
+ require 'sinatra'
3
+
4
+ get '/' do
5
+ 'Hello world!'
6
+ end
7
+
8
+ get "/emacs" do
9
+ redirect to("/vim")
10
+ end
11
+
12
+ get "/vim" do
13
+ "vim rules"
14
+ end
15
+
16
+
17
+ post '/foo' do
18
+ puts request.env
19
+ "done"
20
+ end
21
+
22
+
23
+ post "/api/v1/datapoints" do
24
+ puts request.env
25
+ "done"
26
+ end
@@ -0,0 +1,3 @@
1
+ [rails]
2
+ directory = ./examples
3
+ command = ruby hello_sinatra.rb -p $PORT
@@ -35,8 +35,9 @@ Gem::Specification.new do |s|
35
35
  s.add_dependency("uuid", "~> 2.3")
36
36
  s.add_dependency("facter", "~> 2.2")
37
37
  s.add_dependency("http-parser-lite", "~> 0.6")
38
- s.add_dependency("dotenv", "~> 1.0.2")
38
+ s.add_dependency("dotenv", "~> 2.0")
39
39
  s.add_development_dependency("rspec", "~> 3.0")
40
40
  s.add_development_dependency("mocha")
41
41
  s.add_development_dependency("rake")
42
+ s.add_development_dependency('fakefs')
42
43
  end
@@ -44,6 +44,10 @@ module Invoker
44
44
  ruby_platform.downcase.include?("darwin")
45
45
  end
46
46
 
47
+ def linux?
48
+ ruby_platform.downcase.include?("linux")
49
+ end
50
+
47
51
  def ruby_platform
48
52
  RUBY_PLATFORM
49
53
  end
@@ -100,18 +104,27 @@ module Invoker
100
104
  end
101
105
 
102
106
  def notify_user(message)
103
- run_without_bundler { check_and_notify_with_terminal_notifier(message) }
107
+ if Invoker.darwin?
108
+ run_without_bundler { check_and_notify_with_terminal_notifier(message) }
109
+ elsif Invoker.linux?
110
+ notify_with_libnotify(message)
111
+ end
104
112
  end
105
113
 
106
114
  def check_and_notify_with_terminal_notifier(message)
107
- return unless Invoker.darwin?
108
-
109
115
  command_path = `which terminal-notifier`
110
116
  if command_path && !command_path.empty?
111
117
  system("terminal-notifier -message '#{message}' -title Invoker")
112
118
  end
113
119
  end
114
120
 
121
+ def notify_with_libnotify(message)
122
+ begin
123
+ require "libnotify"
124
+ Libnotify.show(body: message, summary: "Invoker", timeout: 2.5)
125
+ rescue LoadError; end
126
+ end
127
+
115
128
  def migrate_old_config(old_config, config_location)
116
129
  new_config = File.join(config_location, 'config')
117
130
  File.open(new_config, 'w') do |file|
@@ -131,5 +144,9 @@ module Invoker
131
144
  Etc.getpwuid(Process.uid).dir
132
145
  end
133
146
  end
147
+
148
+ def default_tld
149
+ 'dev'
150
+ end
134
151
  end
135
152
  end
@@ -13,8 +13,11 @@ module Invoker
13
13
  end
14
14
 
15
15
  desc "setup", "Run Invoker setup"
16
+ option :tld,
17
+ type: :string,
18
+ banner: 'Configure invoker to use a different top level domain'
16
19
  def setup
17
- Invoker::Power::Setup.install
20
+ Invoker::Power::Setup.install(get_tld(options))
18
21
  end
19
22
 
20
23
  desc "version", "Print Invoker version"
@@ -39,7 +42,7 @@ module Invoker
39
42
  port = options[:port] || 9000
40
43
  Invoker.daemonize = options[:daemon]
41
44
  Invoker.load_invoker_config(file, port)
42
- warn_about_terminal_notifier
45
+ warn_about_notification
43
46
  warn_about_old_configuration
44
47
  pinger = Invoker::CLI::Pinger.new(unix_socket)
45
48
  abort("Invoker is already running".color(:red)) if pinger.invoker_running?
@@ -51,9 +54,9 @@ module Invoker
51
54
  unix_socket.send_command('add', process_name: name)
52
55
  end
53
56
 
54
- desc "add_http process_name port", "Add an external http process to Invoker DNS server"
55
- def add_http(name, port)
56
- unix_socket.send_command('add_http', process_name: name, port: port)
57
+ desc "add_http process_name port [IP]", "Add an external http process to Invoker DNS server"
58
+ def add_http(name, port, ip = nil)
59
+ unix_socket.send_command('add_http', process_name: name, port: port, ip: ip)
57
60
  end
58
61
 
59
62
  desc "tail process1 process2", "Tail a particular process"
@@ -105,12 +108,35 @@ module Invoker
105
108
  tasks.keys + ["help"]
106
109
  end
107
110
 
111
+ def get_tld(options)
112
+ if options[:tld] && !options[:tld].empty?
113
+ options[:tld]
114
+ else
115
+ 'dev'
116
+ end
117
+ end
118
+
108
119
  def unix_socket
109
120
  Invoker::IPC::UnixClient.new
110
121
  end
111
122
 
123
+ def warn_about_notification
124
+ if Invoker.darwin?
125
+ warn_about_terminal_notifier
126
+ else
127
+ warn_about_libnotify
128
+ end
129
+ end
130
+
131
+ def warn_about_libnotify
132
+ require "libnotify"
133
+ rescue LoadError
134
+ Invoker::Logger.puts "You can install libnotify gem for Invoker notifications "\
135
+ "via system tray".color(:red)
136
+ end
137
+
112
138
  def warn_about_terminal_notifier
113
- if RUBY_PLATFORM.downcase.include?("darwin")
139
+ if Invoker.darwin?
114
140
  command_path = `which terminal-notifier`
115
141
  if !command_path || command_path.empty?
116
142
  Invoker::Logger.puts "You can enable OSX notification for processes "\
@@ -16,8 +16,8 @@ module Invoker
16
16
  @dns_mutex.synchronize { dns_data[process_name] }
17
17
  end
18
18
 
19
- def add(name, port)
20
- @dns_mutex.synchronize { dns_data[name] = { 'port' => port } }
19
+ def add(name, port, ip = nil)
20
+ @dns_mutex.synchronize { dns_data[name] = { 'port' => port, 'ip' => ip } }
21
21
  end
22
22
  end
23
23
  end
@@ -2,7 +2,7 @@ module Invoker
2
2
  module IPC
3
3
  class AddHttpCommand < BaseCommand
4
4
  def run_command(message_object)
5
- Invoker.dns_cache.add(message_object.process_name, message_object.port)
5
+ Invoker.dns_cache.add(message_object.process_name, message_object.port, message_object.ip)
6
6
  true
7
7
  end
8
8
  end
@@ -6,7 +6,8 @@ module Invoker
6
6
 
7
7
  dns_check_response = Invoker::IPC::Message::DnsCheckResponse.new(
8
8
  process_name: message_object.process_name,
9
- port: process_detail ? process_detail['port'] : nil
9
+ port: process_detail ? process_detail['port'] : nil,
10
+ ip: process_detail && process_detail['ip'] ? process_detail['ip'] : '0.0.0.0'
10
11
  )
11
12
  send_data(dns_check_response)
12
13
  true
@@ -118,7 +118,7 @@ module Invoker
118
118
 
119
119
  class AddHttp < Base
120
120
  include Serialization
121
- message_attributes :process_name, :port
121
+ message_attributes :process_name, :port, :ip
122
122
  end
123
123
 
124
124
  class Reload < Base
@@ -151,7 +151,7 @@ module Invoker
151
151
 
152
152
  class DnsCheckResponse < Base
153
153
  include Serialization
154
- message_attributes :process_name, :port
154
+ message_attributes :process_name, :port, :ip
155
155
  end
156
156
 
157
157
  class Ping < Base
@@ -16,7 +16,6 @@ module Invoker
16
16
  # value by 1, that way generating different ports for different commands.
17
17
  def initialize(filename, port)
18
18
  @filename = filename || autodetect_config_file
19
-
20
19
  print_message_and_abort if invalid_config_file?
21
20
 
22
21
  @port = port - 1
@@ -38,6 +37,10 @@ module Invoker
38
37
  power_config && power_config.https_port
39
38
  end
40
39
 
40
+ def tld
41
+ power_config && power_config.tld
42
+ end
43
+
41
44
  def autorunnable_processes
42
45
  processes.reject(&:disable_autorun)
43
46
  end
@@ -22,7 +22,7 @@ module Invoker
22
22
  end
23
23
 
24
24
  class Balancer
25
- attr_accessor :connection, :http_parser, :session, :protocol
25
+ attr_accessor :connection, :http_parser, :session, :protocol, :upgraded_to
26
26
 
27
27
  def self.run(options = {})
28
28
  start_http_proxy(InvokerHttpProxy, 'http', options)
@@ -43,6 +43,7 @@ module Invoker
43
43
  @protocol = protocol
44
44
  @http_parser = HttpParser.new(protocol)
45
45
  @session = nil
46
+ @upgraded_to = nil
46
47
  @buffer = []
47
48
  end
48
49
 
@@ -71,7 +72,7 @@ module Invoker
71
72
 
72
73
  dns_check_response = UrlRewriter.new.select_backend_config(headers['Host'])
73
74
  if dns_check_response && dns_check_response.port
74
- connection.server(session, host: '0.0.0.0', port: dns_check_response.port)
75
+ connection.server(session, host: dns_check_response.ip, port: dns_check_response.port)
75
76
  else
76
77
  return_error_page(404)
77
78
  http_parser.reset
@@ -80,8 +81,12 @@ module Invoker
80
81
  end
81
82
 
82
83
  def upstream_data(data)
83
- append_for_http_parsing(data)
84
- nil
84
+ if upgraded_to == "websocket"
85
+ data
86
+ else
87
+ append_for_http_parsing(data)
88
+ nil
89
+ end
85
90
  end
86
91
 
87
92
  def append_for_http_parsing(data)
@@ -93,6 +98,13 @@ module Invoker
93
98
 
94
99
  def backend_data(backend, data)
95
100
  @backend_data = true
101
+
102
+ # check backend data for websockets connection. check for upgrade headers
103
+ # - Upgarde: websocket\r\n
104
+ if data =~ /Upgrade: websocket/
105
+ @upgraded_to = "websocket"
106
+ end
107
+
96
108
  data
97
109
  end
98
110
 
@@ -60,9 +60,10 @@ module Invoker
60
60
  def dns_port; @config[:dns_port]; end
61
61
  def http_port; @config[:http_port]; end
62
62
  def ipfw_rule_number; @config[:ipfw_rule_number]; end
63
+ def https_port; @config[:https_port]; end
63
64
 
64
- def https_port
65
- @config[:https_port]
65
+ def tld
66
+ @config[:tld] || Invoker.default_tld
66
67
  end
67
68
 
68
69
  def save
@@ -31,7 +31,7 @@ module Invoker
31
31
  end
32
32
 
33
33
  def name_matches?(name)
34
- name =~ /.*\.dev/
34
+ name =~ /.*\.#{Invoker.config.tld}/
35
35
  end
36
36
  end
37
37
  end
@@ -13,7 +13,7 @@ module Invoker
13
13
  def migrate
14
14
  if firewall_config_requires_migration? && ask_user_for_migration
15
15
  sudome
16
- osx_setup = Invoker::Power::OsxSetup.new()
16
+ osx_setup = Invoker::Power::OsxSetup.new(Invoker.config.tld)
17
17
  osx_setup.install_firewall(Invoker.config.http_port, Invoker.config.https_port)
18
18
  drop_to_normal_user
19
19
  Invoker::Logger.puts "Invoker has updated its configuration for yosemite."\
@@ -3,15 +3,17 @@ require "eventmachine"
3
3
  module Invoker
4
4
  module Power
5
5
  class Setup
6
- attr_accessor :port_finder
7
- def self.install
6
+ attr_accessor :port_finder, :tld
7
+
8
+ def self.install(tld)
8
9
  selected_installer_klass = installer_klass
9
- selected_installer_klass.new.install
10
+ selected_installer_klass.new(tld).install
10
11
  end
11
12
 
12
13
  def self.uninstall
14
+ power_config = Invoker::Power::Config.load_config
13
15
  selected_installer_klass = installer_klass
14
- selected_installer_klass.new.uninstall_invoker
16
+ selected_installer_klass.new(power_config.tld).uninstall_invoker
15
17
  end
16
18
 
17
19
  def self.installer_klass
@@ -22,6 +24,14 @@ module Invoker
22
24
  end
23
25
  end
24
26
 
27
+ def initialize(tld)
28
+ if tld !~ /^[a-z]+$/
29
+ Invoker::Logger.puts("Please specify valid tld".color(:red))
30
+ exit(1)
31
+ end
32
+ self.tld = tld
33
+ end
34
+
25
35
  def install
26
36
  if check_if_setup_can_run?
27
37
  setup_invoker
@@ -46,6 +56,35 @@ module Invoker
46
56
  def check_if_setup_can_run?
47
57
  !File.exists?(Invoker::Power::Config.config_file)
48
58
  end
59
+
60
+ def create_config_file
61
+ Invoker.setup_config_location
62
+ config = build_power_config
63
+ Invoker::Power::Config.create(config)
64
+ end
65
+
66
+ # Builds and returns power config hash. Override this method in subclasses if necessary.
67
+ def build_power_config
68
+ config = {
69
+ http_port: port_finder.http_port,
70
+ https_port: port_finder.https_port,
71
+ tld: tld
72
+ }
73
+ config
74
+ end
75
+
76
+ def remove_resolver_file
77
+ begin
78
+ safe_remove_file(resolver_file)
79
+ rescue Errno::EACCES
80
+ Invoker::Logger.puts("Running uninstall requires root access, please rerun it with sudo".color(:red))
81
+ raise
82
+ end
83
+ end
84
+
85
+ def safe_remove_file(file)
86
+ File.delete(file) if File.exists?(file)
87
+ end
49
88
  end
50
89
  end
51
90
  end