invoker 1.0.4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.rubocop.yml +30 -0
- data/.travis.yml +1 -0
- data/Gemfile +1 -0
- data/bin/invoker +4 -8
- data/invoker.gemspec +10 -11
- data/lib/invoker.rb +95 -21
- data/lib/invoker/cli.rb +126 -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/command_worker.rb +28 -2
- data/lib/invoker/commander.rb +34 -236
- data/lib/invoker/config.rb +5 -0
- data/lib/invoker/daemon.rb +126 -0
- data/lib/invoker/dns_cache.rb +23 -0
- data/lib/invoker/errors.rb +1 -0
- data/lib/invoker/ipc.rb +45 -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 +16 -0
- data/lib/invoker/ipc/list_command.rb +11 -0
- data/lib/invoker/ipc/message.rb +170 -0
- data/lib/invoker/ipc/message/list_response.rb +33 -0
- data/lib/invoker/ipc/message/tail_response.rb +10 -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/{command_listener → ipc}/server.rb +6 -11
- data/lib/invoker/ipc/tail_command.rb +11 -0
- data/lib/invoker/ipc/unix_client.rb +60 -0
- data/lib/invoker/parsers/config.rb +1 -0
- data/lib/invoker/power/balancer.rb +17 -7
- data/lib/invoker/power/config.rb +6 -3
- data/lib/invoker/power/dns.rb +22 -21
- data/lib/invoker/power/http_response.rb +1 -1
- data/lib/invoker/power/power.rb +3 -0
- data/lib/invoker/power/powerup.rb +3 -2
- data/lib/invoker/power/setup.rb +6 -4
- data/lib/invoker/process_manager.rb +187 -0
- data/lib/invoker/process_printer.rb +27 -38
- data/lib/invoker/reactor.rb +19 -38
- data/lib/invoker/reactor/reader.rb +53 -0
- data/lib/invoker/version.rb +1 -1
- data/readme.md +1 -1
- 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 +30 -0
- data/spec/invoker/commander_spec.rb +57 -127
- data/spec/invoker/config_spec.rb +21 -0
- data/spec/invoker/daemon_spec.rb +34 -0
- data/spec/invoker/invoker_spec.rb +31 -0
- data/spec/invoker/ipc/client_handler_spec.rb +44 -0
- data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
- data/spec/invoker/ipc/message/list_response_spec.rb +22 -0
- data/spec/invoker/ipc/message_spec.rb +45 -0
- data/spec/invoker/ipc/unix_client_spec.rb +29 -0
- data/spec/invoker/power/setup_spec.rb +1 -1
- data/spec/invoker/process_manager_spec.rb +98 -0
- data/spec/invoker/reactor_spec.rb +6 -0
- data/spec/spec_helper.rb +15 -24
- metadata +107 -77
- data/lib/invoker/command_listener/client.rb +0 -45
- data/lib/invoker/parsers/option_parser.rb +0 -106
- data/lib/invoker/power.rb +0 -7
- data/lib/invoker/runner.rb +0 -98
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17e01fb0ade988c21a62f385d51e3b07e58f9016
|
4
|
+
data.tar.gz: 2e4e0c36f2df111493b7c05dea7e0ae99244de2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7601b89cd0db7b1cc0e780885d14f27ec652739702b5c2a59cf961b677db347bfd9e764ada4a53057de80a418ca3d486949c1a6909ee414505a8b5323593708
|
7
|
+
data.tar.gz: 552b15ffb14c384d02f3ec972ccd8f3a2191afedab5bfa899f19c9fb1ecf0e4df86c65cdbfa475df8babfc746401bc6d914bcb26f0abda2f6fd04283dab092c3
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -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
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/bin/invoker
CHANGED
@@ -1,11 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
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::
|
7
|
+
Invoker::CLI.start(ARGV)
|
data/invoker.gemspec
CHANGED
@@ -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("
|
29
|
-
s.add_dependency("rainbow", "~>
|
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.
|
35
|
-
s.add_dependency("uuid", "~> 2.3
|
36
|
-
s.add_dependency("
|
37
|
-
s.add_dependency("
|
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")
|
data/lib/invoker.rb
CHANGED
@@ -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
|
-
|
6
|
-
require 'em-proxy'
|
7
|
-
require 'http-parser'
|
5
|
+
|
8
6
|
require "ostruct"
|
9
7
|
require "uuid"
|
10
|
-
require "
|
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/
|
14
|
-
require "invoker/
|
15
|
-
require "invoker/
|
16
|
-
require "invoker/
|
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
|
-
|
29
|
-
|
30
|
-
|
33
|
+
class << self
|
34
|
+
attr_accessor :config, :tail_watchers, :commander
|
35
|
+
attr_accessor :dns_cache, :daemonize
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
41
|
-
|
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
|
data/lib/invoker/cli.rb
ADDED
@@ -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
|