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.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/bin/invoker +7 -0
  3. data/lib/invoker/cli/pinger.rb +23 -0
  4. data/lib/invoker/cli/question.rb +15 -0
  5. data/lib/invoker/cli/tail.rb +34 -0
  6. data/lib/invoker/cli/tail_watcher.rb +34 -0
  7. data/lib/invoker/cli.rb +197 -0
  8. data/lib/invoker/command_worker.rb +64 -0
  9. data/lib/invoker/commander.rb +101 -0
  10. data/lib/invoker/daemon.rb +126 -0
  11. data/lib/invoker/dns_cache.rb +23 -0
  12. data/lib/invoker/errors.rb +17 -0
  13. data/lib/invoker/event/manager.rb +79 -0
  14. data/lib/invoker/ipc/add_command.rb +12 -0
  15. data/lib/invoker/ipc/add_http_command.rb +10 -0
  16. data/lib/invoker/ipc/base_command.rb +24 -0
  17. data/lib/invoker/ipc/client_handler.rb +26 -0
  18. data/lib/invoker/ipc/dns_check_command.rb +17 -0
  19. data/lib/invoker/ipc/list_command.rb +11 -0
  20. data/lib/invoker/ipc/message/list_response.rb +35 -0
  21. data/lib/invoker/ipc/message/tail_response.rb +10 -0
  22. data/lib/invoker/ipc/message.rb +170 -0
  23. data/lib/invoker/ipc/ping_command.rb +10 -0
  24. data/lib/invoker/ipc/reload_command.rb +12 -0
  25. data/lib/invoker/ipc/remove_command.rb +12 -0
  26. data/lib/invoker/ipc/server.rb +26 -0
  27. data/lib/invoker/ipc/tail_command.rb +11 -0
  28. data/lib/invoker/ipc/unix_client.rb +60 -0
  29. data/lib/invoker/ipc.rb +45 -0
  30. data/lib/invoker/logger.rb +13 -0
  31. data/lib/invoker/parsers/config.rb +184 -0
  32. data/lib/invoker/parsers/procfile.rb +86 -0
  33. data/lib/invoker/power/balancer.rb +133 -0
  34. data/lib/invoker/power/config.rb +77 -0
  35. data/lib/invoker/power/dns.rb +38 -0
  36. data/lib/invoker/power/http_parser.rb +68 -0
  37. data/lib/invoker/power/http_response.rb +81 -0
  38. data/lib/invoker/power/port_finder.rb +49 -0
  39. data/lib/invoker/power/power.rb +3 -0
  40. data/lib/invoker/power/powerup.rb +29 -0
  41. data/lib/invoker/power/setup/distro/arch.rb +15 -0
  42. data/lib/invoker/power/setup/distro/base.rb +80 -0
  43. data/lib/invoker/power/setup/distro/debian.rb +11 -0
  44. data/lib/invoker/power/setup/distro/opensuse.rb +11 -0
  45. data/lib/invoker/power/setup/distro/redhat.rb +11 -0
  46. data/lib/invoker/power/setup/distro/ubuntu.rb +46 -0
  47. data/lib/invoker/power/setup/files/invoker_forwarder.sh.erb +17 -0
  48. data/lib/invoker/power/setup/files/socat_invoker.service +12 -0
  49. data/lib/invoker/power/setup/linux_setup.rb +97 -0
  50. data/lib/invoker/power/setup/osx_setup.rb +137 -0
  51. data/lib/invoker/power/setup.rb +93 -0
  52. data/lib/invoker/power/templates/400.html +40 -0
  53. data/lib/invoker/power/templates/404.html +40 -0
  54. data/lib/invoker/power/templates/503.html +40 -0
  55. data/lib/invoker/power/url_rewriter.rb +40 -0
  56. data/lib/invoker/process_manager.rb +201 -0
  57. data/lib/invoker/process_printer.rb +59 -0
  58. data/lib/invoker/reactor/reader.rb +65 -0
  59. data/lib/invoker/reactor.rb +37 -0
  60. data/lib/invoker/version.rb +47 -0
  61. data/lib/invoker.rb +151 -0
  62. data/spec/invoker/cli/pinger_spec.rb +22 -0
  63. data/spec/invoker/cli/tail_watcher_spec.rb +39 -0
  64. data/spec/invoker/cli_spec.rb +27 -0
  65. data/spec/invoker/command_worker_spec.rb +45 -0
  66. data/spec/invoker/commander_spec.rb +152 -0
  67. data/spec/invoker/config_spec.rb +361 -0
  68. data/spec/invoker/daemon_spec.rb +34 -0
  69. data/spec/invoker/event/manager_spec.rb +67 -0
  70. data/spec/invoker/invoker_spec.rb +71 -0
  71. data/spec/invoker/ipc/client_handler_spec.rb +54 -0
  72. data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
  73. data/spec/invoker/ipc/message/list_response_spec.rb +24 -0
  74. data/spec/invoker/ipc/message_spec.rb +49 -0
  75. data/spec/invoker/ipc/unix_client_spec.rb +29 -0
  76. data/spec/invoker/power/balancer_spec.rb +53 -0
  77. data/spec/invoker/power/config_spec.rb +18 -0
  78. data/spec/invoker/power/http_parser_spec.rb +32 -0
  79. data/spec/invoker/power/http_response_spec.rb +34 -0
  80. data/spec/invoker/power/port_finder_spec.rb +16 -0
  81. data/spec/invoker/power/setup/linux_setup_spec.rb +166 -0
  82. data/spec/invoker/power/setup/osx_setup_spec.rb +105 -0
  83. data/spec/invoker/power/setup_spec.rb +4 -0
  84. data/spec/invoker/power/url_rewriter_spec.rb +69 -0
  85. data/spec/invoker/power/web_sockets_spec.rb +61 -0
  86. data/spec/invoker/process_manager_spec.rb +130 -0
  87. data/spec/invoker/reactor_spec.rb +6 -0
  88. data/spec/spec_helper.rb +43 -0
  89. 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,4 @@
1
+ require "spec_helper"
2
+
3
+ describe "Setup" do
4
+ 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
@@ -0,0 +1,6 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::Reactor do
4
+ describe "writing to socket" do
5
+ end
6
+ end
@@ -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