invoker 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +30 -0
  4. data/.travis.yml +1 -0
  5. data/Gemfile +1 -0
  6. data/bin/invoker +4 -8
  7. data/invoker.gemspec +10 -11
  8. data/lib/invoker.rb +95 -21
  9. data/lib/invoker/cli.rb +126 -0
  10. data/lib/invoker/cli/pinger.rb +23 -0
  11. data/lib/invoker/cli/question.rb +15 -0
  12. data/lib/invoker/cli/tail.rb +34 -0
  13. data/lib/invoker/cli/tail_watcher.rb +34 -0
  14. data/lib/invoker/command_worker.rb +28 -2
  15. data/lib/invoker/commander.rb +34 -236
  16. data/lib/invoker/config.rb +5 -0
  17. data/lib/invoker/daemon.rb +126 -0
  18. data/lib/invoker/dns_cache.rb +23 -0
  19. data/lib/invoker/errors.rb +1 -0
  20. data/lib/invoker/ipc.rb +45 -0
  21. data/lib/invoker/ipc/add_command.rb +12 -0
  22. data/lib/invoker/ipc/add_http_command.rb +10 -0
  23. data/lib/invoker/ipc/base_command.rb +24 -0
  24. data/lib/invoker/ipc/client_handler.rb +26 -0
  25. data/lib/invoker/ipc/dns_check_command.rb +16 -0
  26. data/lib/invoker/ipc/list_command.rb +11 -0
  27. data/lib/invoker/ipc/message.rb +170 -0
  28. data/lib/invoker/ipc/message/list_response.rb +33 -0
  29. data/lib/invoker/ipc/message/tail_response.rb +10 -0
  30. data/lib/invoker/ipc/ping_command.rb +10 -0
  31. data/lib/invoker/ipc/reload_command.rb +12 -0
  32. data/lib/invoker/ipc/remove_command.rb +12 -0
  33. data/lib/invoker/{command_listener → ipc}/server.rb +6 -11
  34. data/lib/invoker/ipc/tail_command.rb +11 -0
  35. data/lib/invoker/ipc/unix_client.rb +60 -0
  36. data/lib/invoker/parsers/config.rb +1 -0
  37. data/lib/invoker/power/balancer.rb +17 -7
  38. data/lib/invoker/power/config.rb +6 -3
  39. data/lib/invoker/power/dns.rb +22 -21
  40. data/lib/invoker/power/http_response.rb +1 -1
  41. data/lib/invoker/power/power.rb +3 -0
  42. data/lib/invoker/power/powerup.rb +3 -2
  43. data/lib/invoker/power/setup.rb +6 -4
  44. data/lib/invoker/process_manager.rb +187 -0
  45. data/lib/invoker/process_printer.rb +27 -38
  46. data/lib/invoker/reactor.rb +19 -38
  47. data/lib/invoker/reactor/reader.rb +53 -0
  48. data/lib/invoker/version.rb +1 -1
  49. data/readme.md +1 -1
  50. data/spec/invoker/cli/pinger_spec.rb +22 -0
  51. data/spec/invoker/cli/tail_watcher_spec.rb +39 -0
  52. data/spec/invoker/cli_spec.rb +27 -0
  53. data/spec/invoker/command_worker_spec.rb +30 -0
  54. data/spec/invoker/commander_spec.rb +57 -127
  55. data/spec/invoker/config_spec.rb +21 -0
  56. data/spec/invoker/daemon_spec.rb +34 -0
  57. data/spec/invoker/invoker_spec.rb +31 -0
  58. data/spec/invoker/ipc/client_handler_spec.rb +44 -0
  59. data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
  60. data/spec/invoker/ipc/message/list_response_spec.rb +22 -0
  61. data/spec/invoker/ipc/message_spec.rb +45 -0
  62. data/spec/invoker/ipc/unix_client_spec.rb +29 -0
  63. data/spec/invoker/power/setup_spec.rb +1 -1
  64. data/spec/invoker/process_manager_spec.rb +98 -0
  65. data/spec/invoker/reactor_spec.rb +6 -0
  66. data/spec/spec_helper.rb +15 -24
  67. metadata +107 -77
  68. data/lib/invoker/command_listener/client.rb +0 -45
  69. data/lib/invoker/parsers/option_parser.rb +0 -106
  70. data/lib/invoker/power.rb +0 -7
  71. data/lib/invoker/runner.rb +0 -98
  72. data/spec/invoker/command_listener/client_spec.rb +0 -52
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 36f535d2a652c9164fbe92e7df1e9aff140223d2
4
- data.tar.gz: f84168b2628f0f40024de2b46c1de5017a9069d1
3
+ metadata.gz: 17e01fb0ade988c21a62f385d51e3b07e58f9016
4
+ data.tar.gz: 2e4e0c36f2df111493b7c05dea7e0ae99244de2c
5
5
  SHA512:
6
- metadata.gz: b0fb344263af24e67273104073e872b070bfeb73f2a9e782953f29d355216ab5d2c18c254b5d175619ba0e6bd7bd4461e2956e01b5d022574005f54d38640854
7
- data.tar.gz: 1e758cf427cd35b11773c06896d3b3fc2ebd9fab50ac8c60ff06379cab2369fc41eb5353f9a53e6e34c5a5b5e04664bb9570ffa001ed6dc7b9fd867aa305fa6b
6
+ metadata.gz: a7601b89cd0db7b1cc0e780885d14f27ec652739702b5c2a59cf961b677db347bfd9e764ada4a53057de80a418ca3d486949c1a6909ee414505a8b5323593708
7
+ data.tar.gz: 552b15ffb14c384d02f3ec972ccd8f3a2191afedab5bfa899f19c9fb1ecf0e4df86c65cdbfa475df8babfc746401bc6d914bcb26f0abda2f6fd04283dab092c3
data/.gitignore CHANGED
@@ -8,3 +8,8 @@ multi.ini
8
8
  tags
9
9
  .rvmrc
10
10
  vendor/
11
+ _site
12
+ .bundle
13
+ coverage
14
+ invoker_profile/
15
+ *.pid
@@ -0,0 +1,30 @@
1
+ AllCops:
2
+ RunRailsCops: false
3
+ Excludes:
4
+ - db/**
5
+
6
+ HashSyntax:
7
+ Description: 'Use either hash rocket or 1.9 styled hashes.'
8
+ Enabled: false
9
+
10
+ LineLength:
11
+ Max: 100
12
+
13
+ # Disable Certain Tests
14
+
15
+ Documentation:
16
+ Description: 'Document classes and non-namespace modules.'
17
+ Enabled: false
18
+
19
+ StringLiterals:
20
+ Description: 'Checks if uses of quotes match the configured preference.'
21
+ Enabled: false
22
+
23
+ Encoding:
24
+ Description: 'Use UTF-8 as the source file encoding.'
25
+ Enabled: false
26
+
27
+ SignalException:
28
+ Description: 'Do not enforce use of fail when raising exceptions.'
29
+ # Valid values are: semantic, only_raise and only_fail
30
+ EnforcedStyle: only_raise
@@ -2,6 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
+ - 2.1.0
5
6
 
6
7
  script: bundle exec rake spec
7
8
 
data/Gemfile CHANGED
@@ -5,3 +5,4 @@ gemspec
5
5
  gem 'pry'
6
6
 
7
7
  gem 'coveralls', require: false
8
+ gem "simplecov", require: false
@@ -1,11 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- begin
4
- require "invoker"
5
- rescue LoadError
6
- invoker_lib_path = File.expand_path('../../lib', __FILE__)
7
- $:.unshift(invoker_lib_path)
8
- require "invoker"
9
- end
3
+ invoker_lib_path = File.expand_path('../../lib', __FILE__)
4
+ $:.unshift(invoker_lib_path)
5
+ require "invoker"
10
6
 
11
- Invoker::Runner.run(ARGV)
7
+ Invoker::CLI.start(ARGV)
@@ -25,17 +25,16 @@ Gem::Specification.new do |s|
25
25
  s.licenses = ["MIT"]
26
26
  s.require_paths = ["lib"]
27
27
  s.summary = %q{Something small for Process management}
28
- s.add_dependency("slop", "~> 3.4.6")
29
- s.add_dependency("rainbow", "~> 1.1.4")
30
- s.add_dependency("iniparse", "~> 1.1.6")
31
- s.add_dependency("formatador", "~> 0.2.4")
32
- s.add_dependency("eventmachine", "~> 1.0.3")
33
- s.add_dependency("em-proxy", "~> 0.1.8")
34
- s.add_dependency("rubydns", "~> 0.6.5")
35
- s.add_dependency("uuid", "~> 2.3.7")
36
- s.add_dependency("highline", "~> 1.6.19")
37
- s.add_dependency("http-parser-lite", "~> 0.6.0")
38
- s.add_dependency("dotenv", "~> 0.9.0")
28
+ s.add_dependency("thor", "~> 0.19")
29
+ s.add_dependency("rainbow", "~> 2.0")
30
+ s.add_dependency("iniparse", "~> 1.1")
31
+ s.add_dependency("formatador", "~> 0.2")
32
+ s.add_dependency("eventmachine", "~> 1.0")
33
+ s.add_dependency("em-proxy", "~> 0.1")
34
+ s.add_dependency("rubydns", "~> 0.7")
35
+ s.add_dependency("uuid", "~> 2.3")
36
+ s.add_dependency("http-parser-lite", "~> 0.6")
37
+ s.add_dependency("dotenv", "~> 0.9")
39
38
  s.add_development_dependency("rspec")
40
39
  s.add_development_dependency("mocha")
41
40
  s.add_development_dependency("rake")
@@ -2,44 +2,118 @@ $: << File.dirname(__FILE__) unless $:.include?(File.expand_path(File.dirname(__
2
2
 
3
3
  require "fileutils"
4
4
  require "formatador"
5
- require 'rubydns'
6
- require 'em-proxy'
7
- require 'http-parser'
5
+
8
6
  require "ostruct"
9
7
  require "uuid"
10
- require "highline"
8
+ require "json"
9
+ require "rainbow"
10
+ require "rainbow/ext/string"
11
+
11
12
  require "invoker/version"
12
13
  require "invoker/logger"
13
- require "invoker/runner"
14
- require "invoker/command_listener/server"
15
- require "invoker/command_listener/client"
16
- require "invoker/power"
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/powerup"
17
22
  require "invoker/errors"
18
23
  require "invoker/parsers/procfile"
19
24
  require "invoker/parsers/config"
20
- require "invoker/parsers/option_parser"
21
25
  require "invoker/commander"
26
+ require "invoker/process_manager"
22
27
  require "invoker/command_worker"
23
28
  require "invoker/reactor"
24
29
  require "invoker/event/manager"
25
30
  require "invoker/process_printer"
26
31
 
27
32
  module Invoker
28
- def self.darwin?
29
- ruby_platform.downcase.include?("darwin")
30
- end
33
+ class << self
34
+ attr_accessor :config, :tail_watchers, :commander
35
+ attr_accessor :dns_cache, :daemonize
31
36
 
32
- def self.ruby_platform
33
- RUBY_PLATFORM
34
- end
37
+ alias_method :daemonize?, :daemonize
38
+
39
+ def darwin?
40
+ ruby_platform.downcase.include?("darwin")
41
+ end
42
+
43
+ def ruby_platform
44
+ RUBY_PLATFORM
45
+ end
46
+
47
+ def load_invoker_config(file, port)
48
+ @config = Invoker::Parsers::Config.new(file, port)
49
+ @dns_cache = Invoker::DNSCache.new(@invoker_config)
50
+ @tail_watchers = Invoker::CLI::TailWatcher.new
51
+ @commander = Invoker::Commander.new
52
+ end
53
+
54
+ def close_socket(socket)
55
+ socket.close
56
+ rescue StandardError => error
57
+ Invoker::Logger.puts "Error removing socket #{error}"
58
+ end
59
+
60
+ def daemon
61
+ @daemon ||= Invoker::Daemon.new
62
+ end
35
63
 
36
- def self.can_run_balancer?(throw_warning = true)
37
- return false unless darwin?
38
- return true if File.exists?(Invoker::Power::Config::CONFIG_LOCATION)
64
+ def can_run_balancer?(throw_warning = true)
65
+ return false unless darwin?
66
+ return true if File.exist?(Invoker::Power::Config::CONFIG_LOCATION)
67
+
68
+ if throw_warning
69
+ Invoker::Logger.puts("Invoker has detected setup has not been run. Domain feature will not work without running setup command.".color(:red))
70
+ end
71
+ false
72
+ end
73
+
74
+ def setup_config_location
75
+ config_location = File.join(Dir.home, '.invoker')
76
+ return config_location if Dir.exist?(config_location)
77
+
78
+ if File.exist?(config_location)
79
+ old_config = File.read(config_location)
80
+ FileUtils.rm_f(config_location)
81
+ end
82
+
83
+ FileUtils.mkdir(config_location)
84
+
85
+ migrate_old_config(old_config, config_location) if old_config
86
+ config_location
87
+ end
88
+
89
+ def run_without_bundler
90
+ if defined?(Bundler)
91
+ Bundler.with_clean_env do
92
+ yield
93
+ end
94
+ else
95
+ yield
96
+ end
97
+ end
98
+
99
+ def notify_user(message)
100
+ run_without_bundler { check_and_notify_with_terminal_notifier(message) }
101
+ end
102
+
103
+ def check_and_notify_with_terminal_notifier(message)
104
+ return unless Invoker.darwin?
105
+
106
+ command_path = `which terminal-notifier`
107
+ if command_path && !command_path.empty?
108
+ system("terminal-notifier -message '#{message}' -title Invoker")
109
+ end
110
+ end
39
111
 
40
- if throw_warning
41
- Invoker::Logger.puts("Invoker has detected setup has not been run. Domain feature will not work without running setup command.".color(:red))
112
+ def migrate_old_config(old_config, config_location)
113
+ new_config = File.join(config_location, 'config')
114
+ File.open(new_config, 'w') do |file|
115
+ file.write(old_config)
116
+ end
42
117
  end
43
- false
44
118
  end
45
119
  end
@@ -0,0 +1,126 @@
1
+ require "socket"
2
+ require "thor"
3
+
4
+ module Invoker
5
+ class CLI < Thor
6
+ def self.start(*args)
7
+ cli_args = args.flatten
8
+ # If it is not a valid task, it is probably file argument
9
+ if default_start_command?(cli_args)
10
+ args = [cli_args.unshift("start")]
11
+ end
12
+ super(*args)
13
+ end
14
+
15
+ desc "setup", "Run Invoker setup"
16
+ def setup
17
+ Invoker::Power::Setup.install
18
+ end
19
+
20
+ desc "version", "Print Invoker version"
21
+ def version
22
+ Invoker::Logger.puts Invoker::VERSION
23
+ end
24
+ map %w(-v --version) => :version
25
+
26
+ desc "uninstall", "Uninstall Invoker and all installed files"
27
+ def uninstall
28
+ Invoker::Power::Setup.uninstall
29
+ end
30
+
31
+ desc "start CONFIG_FILE", "Start Invoker Server"
32
+ option :port, type: :numeric, banner: "Port series to be used for starting rack servers"
33
+ option :daemon,
34
+ type: :boolean,
35
+ banner: "Daemonize the server into the background",
36
+ aliases: [:d]
37
+ def start(file)
38
+ Invoker.setup_config_location
39
+ port = options[:port] || 9000
40
+ Invoker.daemonize = options[:daemon]
41
+ Invoker.load_invoker_config(file, port)
42
+ warn_about_terminal_notifier
43
+ pinger = Invoker::CLI::Pinger.new(unix_socket)
44
+ abort("Invoker is already running".color(:red)) if pinger.invoker_running?
45
+ Invoker.commander.start_manager
46
+ end
47
+
48
+ desc "add process", "Add a program to Invoker server"
49
+ def add(name)
50
+ unix_socket.send_command('add', process_name: name)
51
+ end
52
+
53
+ desc "add_http process_name port", "Add an external http process to Invoker DNS server"
54
+ def add_http(name, port)
55
+ unix_socket.send_command('add_http', process_name: name, port: port)
56
+ end
57
+
58
+ desc "tail process1 process2", "Tail a particular process"
59
+ def tail(*names)
60
+ tailer = Invoker::CLI::Tail.new(names)
61
+ tailer.run
62
+ end
63
+
64
+ desc "reload process", "Reload a process managed by Invoker"
65
+ option :signal,
66
+ banner: "Signal to send for killing the process, default is SIGINT",
67
+ aliases: [:s]
68
+ def reload(name)
69
+ signal = options[:signal] || 'INT'
70
+ unix_socket.send_command('reload', process_name: name, signal: signal)
71
+ end
72
+
73
+ desc "list", "List all running processes"
74
+ def list
75
+ unix_socket.send_command('list') do |response_object|
76
+ Invoker::ProcessPrinter.new(response_object).tap { |printer| printer.print_table }
77
+ end
78
+ end
79
+
80
+ desc "remove process", "Stop a process managed by Invoker"
81
+ option :signal,
82
+ banner: "Signal to send for killing the process, default is SIGINT",
83
+ aliases: [:s]
84
+ def remove(name)
85
+ signal = options[:signal] || 'INT'
86
+ unix_socket.send_command('remove', process_name: name, signal: signal)
87
+ end
88
+
89
+ desc "stop", "Stop Invoker daemon"
90
+ def stop
91
+ Invoker.daemon.stop
92
+ end
93
+
94
+ private
95
+
96
+ def self.default_start_command?(args)
97
+ command_name = args.first
98
+ command_name &&
99
+ !command_name.match(/^-/) &&
100
+ !valid_tasks.include?(command_name)
101
+ end
102
+
103
+ def self.valid_tasks
104
+ tasks.keys + ["help"]
105
+ end
106
+
107
+ def unix_socket
108
+ Invoker::IPC::UnixClient.new
109
+ end
110
+
111
+ def warn_about_terminal_notifier
112
+ if RUBY_PLATFORM.downcase.include?("darwin")
113
+ command_path = `which terminal-notifier`
114
+ if !command_path || command_path.empty?
115
+ Invoker::Logger.puts "You can enable OSX notification for processes "\
116
+ "by installing terminal-notifier gem".color(:red)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ require "invoker/cli/question"
124
+ require "invoker/cli/tail_watcher"
125
+ require "invoker/cli/tail"
126
+ require "invoker/cli/pinger"
@@ -0,0 +1,23 @@
1
+ require "timeout"
2
+
3
+ module Invoker
4
+ class CLI::Pinger
5
+ attr_accessor :unix_client
6
+ def initialize(unix_client)
7
+ @unix_client = unix_client
8
+ end
9
+
10
+ def invoker_running?
11
+ response = send_ping_and_read_response
12
+ response && response.status == 'pong'
13
+ end
14
+
15
+ private
16
+
17
+ def send_ping_and_read_response
18
+ Timeout.timeout(2) { unix_client.send_and_receive('ping') }
19
+ rescue Timeout::Error
20
+ nil
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ module Invoker
2
+ class CLI::Question
3
+ def self.agree(question_text)
4
+ $stdout.print(question_text)
5
+ answer = $stdin.gets
6
+ answer.strip!
7
+ if answer =~ /\Ay(?:es)?|no?\Z/i
8
+ answer =~ /\Ay(?:es)?\Z/i
9
+ else
10
+ $stdout.puts "Please enter 'yes' or 'no'."
11
+ agree(question_text)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ module Invoker
2
+ class CLI::Tail
3
+ attr_accessor :process_names
4
+ def initialize(process_names)
5
+ verify_process_name(process_names)
6
+ @process_names = process_names
7
+ @unix_socket = Invoker::IPC::UnixClient.new
8
+ end
9
+
10
+ def run
11
+ socket = @unix_socket.send_and_wait('tail', process_names: process_names)
12
+ trap('INT') { socket.close }
13
+ loop do
14
+ message = read_next_line(socket)
15
+ break unless message
16
+ puts message.tail_line
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def verify_process_name(process_names)
23
+ if process_names.empty?
24
+ abort("Tail command requires one or more process name")
25
+ end
26
+ end
27
+
28
+ def read_next_line(socket)
29
+ Invoker::IPC.message_from_io(socket)
30
+ rescue
31
+ nil
32
+ end
33
+ end
34
+ end