itrg-invoker 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/invoker +7 -0
- data/lib/invoker/cli/pinger.rb +23 -0
- data/lib/invoker/cli/question.rb +15 -0
- data/lib/invoker/cli/tail.rb +34 -0
- data/lib/invoker/cli/tail_watcher.rb +34 -0
- data/lib/invoker/cli.rb +197 -0
- data/lib/invoker/command_worker.rb +64 -0
- data/lib/invoker/commander.rb +101 -0
- data/lib/invoker/daemon.rb +126 -0
- data/lib/invoker/dns_cache.rb +23 -0
- data/lib/invoker/errors.rb +17 -0
- data/lib/invoker/event/manager.rb +79 -0
- data/lib/invoker/ipc/add_command.rb +12 -0
- data/lib/invoker/ipc/add_http_command.rb +10 -0
- data/lib/invoker/ipc/base_command.rb +24 -0
- data/lib/invoker/ipc/client_handler.rb +26 -0
- data/lib/invoker/ipc/dns_check_command.rb +17 -0
- data/lib/invoker/ipc/list_command.rb +11 -0
- data/lib/invoker/ipc/message/list_response.rb +35 -0
- data/lib/invoker/ipc/message/tail_response.rb +10 -0
- data/lib/invoker/ipc/message.rb +170 -0
- data/lib/invoker/ipc/ping_command.rb +10 -0
- data/lib/invoker/ipc/reload_command.rb +12 -0
- data/lib/invoker/ipc/remove_command.rb +12 -0
- data/lib/invoker/ipc/server.rb +26 -0
- data/lib/invoker/ipc/tail_command.rb +11 -0
- data/lib/invoker/ipc/unix_client.rb +60 -0
- data/lib/invoker/ipc.rb +45 -0
- data/lib/invoker/logger.rb +13 -0
- data/lib/invoker/parsers/config.rb +184 -0
- data/lib/invoker/parsers/procfile.rb +86 -0
- data/lib/invoker/power/balancer.rb +133 -0
- data/lib/invoker/power/config.rb +77 -0
- data/lib/invoker/power/dns.rb +38 -0
- data/lib/invoker/power/http_parser.rb +68 -0
- data/lib/invoker/power/http_response.rb +81 -0
- data/lib/invoker/power/port_finder.rb +49 -0
- data/lib/invoker/power/power.rb +3 -0
- data/lib/invoker/power/powerup.rb +29 -0
- data/lib/invoker/power/setup/distro/arch.rb +15 -0
- data/lib/invoker/power/setup/distro/base.rb +80 -0
- data/lib/invoker/power/setup/distro/debian.rb +11 -0
- data/lib/invoker/power/setup/distro/opensuse.rb +11 -0
- data/lib/invoker/power/setup/distro/redhat.rb +11 -0
- data/lib/invoker/power/setup/distro/ubuntu.rb +46 -0
- data/lib/invoker/power/setup/files/invoker_forwarder.sh.erb +17 -0
- data/lib/invoker/power/setup/files/socat_invoker.service +12 -0
- data/lib/invoker/power/setup/linux_setup.rb +97 -0
- data/lib/invoker/power/setup/osx_setup.rb +137 -0
- data/lib/invoker/power/setup.rb +93 -0
- data/lib/invoker/power/templates/400.html +40 -0
- data/lib/invoker/power/templates/404.html +40 -0
- data/lib/invoker/power/templates/503.html +40 -0
- data/lib/invoker/power/url_rewriter.rb +40 -0
- data/lib/invoker/process_manager.rb +201 -0
- data/lib/invoker/process_printer.rb +59 -0
- data/lib/invoker/reactor/reader.rb +65 -0
- data/lib/invoker/reactor.rb +37 -0
- data/lib/invoker/version.rb +47 -0
- data/lib/invoker.rb +151 -0
- data/spec/invoker/cli/pinger_spec.rb +22 -0
- data/spec/invoker/cli/tail_watcher_spec.rb +39 -0
- data/spec/invoker/cli_spec.rb +27 -0
- data/spec/invoker/command_worker_spec.rb +45 -0
- data/spec/invoker/commander_spec.rb +152 -0
- data/spec/invoker/config_spec.rb +361 -0
- data/spec/invoker/daemon_spec.rb +34 -0
- data/spec/invoker/event/manager_spec.rb +67 -0
- data/spec/invoker/invoker_spec.rb +71 -0
- data/spec/invoker/ipc/client_handler_spec.rb +54 -0
- data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
- data/spec/invoker/ipc/message/list_response_spec.rb +24 -0
- data/spec/invoker/ipc/message_spec.rb +49 -0
- data/spec/invoker/ipc/unix_client_spec.rb +29 -0
- data/spec/invoker/power/balancer_spec.rb +53 -0
- data/spec/invoker/power/config_spec.rb +18 -0
- data/spec/invoker/power/http_parser_spec.rb +32 -0
- data/spec/invoker/power/http_response_spec.rb +34 -0
- data/spec/invoker/power/port_finder_spec.rb +16 -0
- data/spec/invoker/power/setup/linux_setup_spec.rb +166 -0
- data/spec/invoker/power/setup/osx_setup_spec.rb +105 -0
- data/spec/invoker/power/setup_spec.rb +4 -0
- data/spec/invoker/power/url_rewriter_spec.rb +69 -0
- data/spec/invoker/power/web_sockets_spec.rb +61 -0
- data/spec/invoker/process_manager_spec.rb +130 -0
- data/spec/invoker/reactor_spec.rb +6 -0
- data/spec/spec_helper.rb +43 -0
- metadata +376 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Invoker::Power::OsxSetup, fakefs: true do
|
4
|
+
before do
|
5
|
+
FileUtils.mkdir_p(inv_conf_dir)
|
6
|
+
FileUtils.mkdir_p(Invoker::Power::OsxSetup::RESOLVER_DIR)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "when no setup exists" do
|
10
|
+
it "should create a config file with port etc" do
|
11
|
+
setup = Invoker::Power::OsxSetup.new('dev')
|
12
|
+
setup.expects(:install_resolver).returns(true)
|
13
|
+
setup.expects(:drop_to_normal_user).returns(true)
|
14
|
+
setup.expects(:install_firewall).once
|
15
|
+
|
16
|
+
setup.setup_invoker
|
17
|
+
|
18
|
+
config = Invoker::Power::Config.load_config
|
19
|
+
expect(config.http_port).not_to be_nil
|
20
|
+
expect(config.dns_port).not_to be_nil
|
21
|
+
expect(config.https_port).not_to be_nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "when a setup file exists" do
|
26
|
+
it "should throw error about existing file" do
|
27
|
+
File.open(Invoker::Power::Config.config_file, "w") {|fl|
|
28
|
+
fl.write("foo test")
|
29
|
+
}
|
30
|
+
Invoker::Power::Setup.any_instance.expects(:setup_invoker).never
|
31
|
+
Invoker::Power::Setup.install('dev')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "when pow like setup exists" do
|
36
|
+
before {
|
37
|
+
File.open(File.join(Invoker::Power::OsxSetup::RESOLVER_DIR, "dev"), "w") { |fl|
|
38
|
+
fl.write("hello")
|
39
|
+
}
|
40
|
+
@setup = Invoker::Power::OsxSetup.new('dev')
|
41
|
+
}
|
42
|
+
|
43
|
+
describe "when user selects to overwrite it" do
|
44
|
+
it "should run setup normally" do
|
45
|
+
@setup.expects(:setup_resolver_file).returns(true)
|
46
|
+
@setup.expects(:drop_to_normal_user).returns(true)
|
47
|
+
@setup.expects(:install_resolver).returns(true)
|
48
|
+
@setup.expects(:install_firewall).once()
|
49
|
+
|
50
|
+
@setup.setup_invoker
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "when user chose not to overwrite it" do
|
55
|
+
it "should abort the setup process" do
|
56
|
+
@setup.expects(:setup_resolver_file).returns(false)
|
57
|
+
|
58
|
+
@setup.expects(:install_resolver).never
|
59
|
+
@setup.expects(:install_firewall).never
|
60
|
+
|
61
|
+
@setup.setup_invoker
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "uninstalling firewall rules" do
|
67
|
+
it "should uninstall firewall rules and remove all files created by setup" do
|
68
|
+
setup = Invoker::Power::OsxSetup.new('dev')
|
69
|
+
|
70
|
+
Invoker::CLI::Question.expects(:agree).returns(true)
|
71
|
+
setup.expects(:remove_resolver_file).once
|
72
|
+
setup.expects(:unload_firewall_rule).with(true).once
|
73
|
+
Invoker::Power::Config.expects(:delete).once
|
74
|
+
|
75
|
+
setup.uninstall_invoker
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "setup on fresh osx install" do
|
80
|
+
context "when resolver directory does not exist" do
|
81
|
+
before do
|
82
|
+
@setup = Invoker::Power::OsxSetup.new('dev')
|
83
|
+
FileUtils.rm_rf(Invoker::Power::OsxSetup::RESOLVER_DIR)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should create the directory and install" do
|
87
|
+
@setup.expects(:setup_resolver_file).returns(true)
|
88
|
+
@setup.expects(:drop_to_normal_user).returns(true)
|
89
|
+
@setup.expects(:install_firewall).once()
|
90
|
+
|
91
|
+
@setup.setup_invoker
|
92
|
+
expect(Dir.exist?(Invoker::Power::OsxSetup::RESOLVER_DIR)).to be_truthy
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '.resolver_file' do
|
98
|
+
context 'user sets up a custom top level domain' do
|
99
|
+
it 'should create the correct resolver file' do
|
100
|
+
setup = Invoker::Power::OsxSetup.new('local')
|
101
|
+
expect(setup.resolver_file).to eq('/etc/resolver/local')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Invoker::Power::UrlRewriter do
|
4
|
+
let(:rewriter) { Invoker::Power::UrlRewriter.new }
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
@original_invoker_config = Invoker.config
|
8
|
+
end
|
9
|
+
|
10
|
+
def mock_invoker_tld_as(domain)
|
11
|
+
Invoker.config = mock
|
12
|
+
Invoker.config.stubs(:tld).returns(domain)
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:all) do
|
16
|
+
Invoker.config = @original_invoker_config
|
17
|
+
end
|
18
|
+
|
19
|
+
context "matching domain part of incoming request" do
|
20
|
+
before(:each) do
|
21
|
+
mock_invoker_tld_as("test")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should match foo.test" do
|
25
|
+
match = rewriter.extract_host_from_domain("foo.test")
|
26
|
+
expect(match).to_not be_empty
|
27
|
+
|
28
|
+
matching_string = match[0]
|
29
|
+
expect(matching_string).to eq("foo")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should match foo.test:1080" do
|
33
|
+
match = rewriter.extract_host_from_domain("foo.test:1080")
|
34
|
+
expect(match).to_not be_empty
|
35
|
+
|
36
|
+
matching_string = match[0]
|
37
|
+
expect(matching_string).to eq("foo")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should match emacs.bar.test" do
|
41
|
+
match = rewriter.extract_host_from_domain("emacs.bar.test")
|
42
|
+
expect(match).to_not be_empty
|
43
|
+
|
44
|
+
expect(match[0]).to eq("emacs.bar")
|
45
|
+
expect(match[1]).to eq("bar")
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should match hello-world.test" do
|
49
|
+
match = rewriter.extract_host_from_domain("hello-world.test")
|
50
|
+
expect(match).to_not be_nil
|
51
|
+
|
52
|
+
expect(match[0]).to eq("hello-world")
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'user sets up a custom top level domain' do
|
56
|
+
before(:each) do
|
57
|
+
mock_invoker_tld_as("local")
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should match domain part of incoming request correctly' do
|
61
|
+
match = rewriter.extract_host_from_domain("foo.local")
|
62
|
+
expect(match).to_not be_empty
|
63
|
+
|
64
|
+
matching_string = match[0]
|
65
|
+
expect(matching_string).to eq("foo")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Full integration test. Start a server, and client. Let client interact with
|
4
|
+
# server do a ping-pong. Client checks whether ping pong is successful or not.
|
5
|
+
# Also, mock rewriter so that it returns valid port for request proxying.
|
6
|
+
# - Server will run on port 28080.
|
7
|
+
# - Balancer will run on port 28081 proxying to 28080
|
8
|
+
# - Client will connect to 28081 performing ping-pong
|
9
|
+
|
10
|
+
def websocket_server
|
11
|
+
require 'websocket-eventmachine-server'
|
12
|
+
|
13
|
+
EM.run do
|
14
|
+
WebSocket::EventMachine::Server.start(host: "0.0.0.0", port: 28080) do |ws|
|
15
|
+
ws.onerror { |e| p e }
|
16
|
+
ws.onmessage { ws.send "pong" }
|
17
|
+
end
|
18
|
+
|
19
|
+
EM.add_timer(2) { EM.stop }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def websocket_client
|
24
|
+
require 'websocket-eventmachine-client'
|
25
|
+
|
26
|
+
@message = ""
|
27
|
+
|
28
|
+
EM.run do
|
29
|
+
ws = WebSocket::EventMachine::Client.connect(uri: 'ws://0.0.0.0:28081')
|
30
|
+
ws.onerror { |e| p e }
|
31
|
+
ws.onopen { ws.send("ping") }
|
32
|
+
ws.onmessage { |m, _| @message = m }
|
33
|
+
|
34
|
+
EM.add_timer(2) do
|
35
|
+
expect(@message).to eq "pong"
|
36
|
+
EM.stop
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
describe 'Web sockets support' do
|
43
|
+
it 'can ping pong via balancer' do
|
44
|
+
dns_response = Struct.new(:port, :ip).new(28080, "0.0.0.0")
|
45
|
+
Invoker::Power::UrlRewriter.any_instance
|
46
|
+
.stubs(:select_backend_config)
|
47
|
+
.returns(dns_response)
|
48
|
+
|
49
|
+
EM.run do
|
50
|
+
EM.start_server("0.0.0.0", 28081, EM::ProxyServer::Connection, {}) do |conn|
|
51
|
+
Invoker::Power::Balancer.new(conn, "http").install_callbacks
|
52
|
+
end
|
53
|
+
|
54
|
+
fork { websocket_server }
|
55
|
+
fork { websocket_client }
|
56
|
+
EM.add_timer(3) { EM.stop }
|
57
|
+
end
|
58
|
+
|
59
|
+
Process.waitall
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Invoker::ProcessManager do
|
4
|
+
let(:process_manager) { Invoker::ProcessManager.new }
|
5
|
+
|
6
|
+
describe "#start_process_by_name" do
|
7
|
+
it "should find command by label and start it, if found" do
|
8
|
+
@original_invoker_config = Invoker.config
|
9
|
+
Invoker.config = mock
|
10
|
+
|
11
|
+
Invoker.config.stubs(:processes).returns([OpenStruct.new(:label => "resque", :cmd => "foo", :dir => "bar")])
|
12
|
+
Invoker.config.expects(:process).returns(OpenStruct.new(:label => "resque", :cmd => "foo", :dir => "bar"))
|
13
|
+
process_manager.expects(:start_process).returns(true)
|
14
|
+
|
15
|
+
process_manager.start_process_by_name("resque")
|
16
|
+
|
17
|
+
Invoker.config = @original_invoker_config
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should not start already running process" do
|
21
|
+
process_manager.workers.expects(:[]).returns(OpenStruct.new(:pid => "bogus"))
|
22
|
+
expect(process_manager.start_process_by_name("resque")).to be_falsey
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#stop_process" do
|
27
|
+
let(:message) { MM::Remove.new(options) }
|
28
|
+
describe "when a worker is found" do
|
29
|
+
before do
|
30
|
+
process_manager.workers.expects(:[]).returns(OpenStruct.new(:pid => "bogus"))
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "if a signal is specified" do
|
34
|
+
let(:options) { { process_name: 'bogus', signal: 'HUP' } }
|
35
|
+
it "should use that signal to kill the worker" do
|
36
|
+
process_manager.expects(:process_kill).with("bogus", "HUP").returns(true)
|
37
|
+
expect(process_manager.stop_process(message)).to be_truthy
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "if no signal is specified" do
|
42
|
+
let(:options) { { process_name: 'bogus' } }
|
43
|
+
it "should use INT signal" do
|
44
|
+
process_manager.expects(:process_kill).with("bogus", "INT").returns(true)
|
45
|
+
expect(process_manager.stop_process(message)).to be_truthy
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "when no worker is found" do
|
51
|
+
let(:options) { { process_name: 'bogus', signal: 'HUP' } }
|
52
|
+
before do
|
53
|
+
process_manager.workers.expects(:[]).returns(nil)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should not kill anything" do
|
57
|
+
process_manager.expects(:process_kill).never
|
58
|
+
process_manager.stop_process(message)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#load_env" do
|
64
|
+
it "should load .env file from the specified directory" do
|
65
|
+
dir = "/tmp"
|
66
|
+
begin
|
67
|
+
env_file = File.new("#{dir}/.env", "w")
|
68
|
+
env_data =<<-EOD
|
69
|
+
FOO=foo
|
70
|
+
BAR=bar
|
71
|
+
EOD
|
72
|
+
env_file.write(env_data)
|
73
|
+
env_file.close
|
74
|
+
env_options = process_manager.load_env(dir)
|
75
|
+
expect(env_options).to include("FOO" => "foo", "BAR" => "bar")
|
76
|
+
ensure
|
77
|
+
File.delete(env_file.path)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should default to current directory if no directory is specified" do
|
82
|
+
dir = ENV["HOME"]
|
83
|
+
ENV.stubs(:[]).with("PWD").returns(dir)
|
84
|
+
begin
|
85
|
+
env_file = File.new("#{dir}/.env", "w")
|
86
|
+
env_data =<<-EOD
|
87
|
+
FOO=bar
|
88
|
+
BAR=foo
|
89
|
+
EOD
|
90
|
+
env_file.write(env_data)
|
91
|
+
env_file.close
|
92
|
+
env_options = process_manager.load_env
|
93
|
+
expect(env_options).to include("FOO" => "bar", "BAR" => "foo")
|
94
|
+
ensure
|
95
|
+
File.delete(env_file.path)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should return empty hash if there is no .env file" do
|
100
|
+
dir = "/tmp"
|
101
|
+
expect(process_manager.load_env(dir)).to eq({})
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should load .local.env file if it exists" do
|
105
|
+
dir = "/tmp"
|
106
|
+
begin
|
107
|
+
env_file = File.new("#{dir}/.env", "w")
|
108
|
+
env_data =<<-EOD
|
109
|
+
FOO=foo
|
110
|
+
BAR=bar
|
111
|
+
EOD
|
112
|
+
env_file.write(env_data)
|
113
|
+
env_file.close
|
114
|
+
|
115
|
+
local_env_file = File.new("#{dir}/.env.local", "w")
|
116
|
+
local_env_data =<<-EOD
|
117
|
+
FOO=emacs
|
118
|
+
EOD
|
119
|
+
local_env_file.write(local_env_data)
|
120
|
+
local_env_file.close
|
121
|
+
|
122
|
+
env_options = process_manager.load_env(dir)
|
123
|
+
expect(env_options).to include("FOO" => "emacs", "BAR" => "bar")
|
124
|
+
ensure
|
125
|
+
File.delete(env_file.path)
|
126
|
+
File.delete(local_env_file.path)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require "simplecov"
|
2
|
+
require 'coveralls'
|
3
|
+
require 'fakefs/spec_helpers'
|
4
|
+
|
5
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
6
|
+
SimpleCov.start do
|
7
|
+
add_filter "/spec/"
|
8
|
+
end
|
9
|
+
|
10
|
+
require "invoker"
|
11
|
+
require "invoker/power/power"
|
12
|
+
MM = Invoker::IPC::Message
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.run_all_when_everything_filtered = true
|
16
|
+
config.filter_run :focus
|
17
|
+
config.mock_with :mocha
|
18
|
+
config.include FakeFS::SpecHelpers, fakefs: true
|
19
|
+
|
20
|
+
# Run specs in random order to surface order dependencies. If you find an
|
21
|
+
# order dependency and want to debug it, you can fix the order by providing
|
22
|
+
# the seed, which is printed after each run.
|
23
|
+
# --seed 1234
|
24
|
+
config.order = 'random'
|
25
|
+
end
|
26
|
+
|
27
|
+
ENV["INVOKER_TESTS"] = "true"
|
28
|
+
|
29
|
+
def invoker_commander
|
30
|
+
Invoker.commander ||= mock
|
31
|
+
end
|
32
|
+
|
33
|
+
def invoker_dns_cache
|
34
|
+
Invoker.dns_cache ||= mock
|
35
|
+
end
|
36
|
+
|
37
|
+
def inv_conf_dir
|
38
|
+
File.join(ENV['HOME'], '.invoker')
|
39
|
+
end
|
40
|
+
|
41
|
+
def inv_conf_file
|
42
|
+
File.join(inv_conf_dir, 'config')
|
43
|
+
end
|