meepo 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +29 -0
  6. data/.travis.yml +10 -0
  7. data/Dockerfile +7 -0
  8. data/Gemfile +13 -0
  9. data/MIT-LICENSE +20 -0
  10. data/Rakefile +15 -0
  11. data/TODO +5 -0
  12. data/bin/invoker +7 -0
  13. data/contrib/completion/invoker-completion.bash +70 -0
  14. data/contrib/completion/invoker-completion.zsh +62 -0
  15. data/examples/hello_sinatra.rb +26 -0
  16. data/examples/sample.ini +3 -0
  17. data/invoker.gemspec +43 -0
  18. data/lib/invoker.rb +152 -0
  19. data/lib/invoker/cli.rb +159 -0
  20. data/lib/invoker/cli/pinger.rb +23 -0
  21. data/lib/invoker/cli/question.rb +15 -0
  22. data/lib/invoker/cli/tail.rb +34 -0
  23. data/lib/invoker/cli/tail_watcher.rb +34 -0
  24. data/lib/invoker/command_worker.rb +60 -0
  25. data/lib/invoker/commander.rb +95 -0
  26. data/lib/invoker/daemon.rb +126 -0
  27. data/lib/invoker/dns_cache.rb +23 -0
  28. data/lib/invoker/errors.rb +17 -0
  29. data/lib/invoker/event/manager.rb +79 -0
  30. data/lib/invoker/ipc.rb +45 -0
  31. data/lib/invoker/ipc/add_command.rb +12 -0
  32. data/lib/invoker/ipc/add_http_command.rb +10 -0
  33. data/lib/invoker/ipc/base_command.rb +24 -0
  34. data/lib/invoker/ipc/client_handler.rb +26 -0
  35. data/lib/invoker/ipc/dns_check_command.rb +17 -0
  36. data/lib/invoker/ipc/list_command.rb +11 -0
  37. data/lib/invoker/ipc/message.rb +170 -0
  38. data/lib/invoker/ipc/message/list_response.rb +35 -0
  39. data/lib/invoker/ipc/message/tail_response.rb +10 -0
  40. data/lib/invoker/ipc/ping_command.rb +10 -0
  41. data/lib/invoker/ipc/reload_command.rb +12 -0
  42. data/lib/invoker/ipc/remove_command.rb +12 -0
  43. data/lib/invoker/ipc/server.rb +26 -0
  44. data/lib/invoker/ipc/tail_command.rb +11 -0
  45. data/lib/invoker/ipc/unix_client.rb +60 -0
  46. data/lib/invoker/logger.rb +13 -0
  47. data/lib/invoker/parsers/config.rb +184 -0
  48. data/lib/invoker/parsers/procfile.rb +86 -0
  49. data/lib/invoker/power/balancer.rb +131 -0
  50. data/lib/invoker/power/config.rb +77 -0
  51. data/lib/invoker/power/dns.rb +38 -0
  52. data/lib/invoker/power/http_parser.rb +68 -0
  53. data/lib/invoker/power/http_response.rb +81 -0
  54. data/lib/invoker/power/pf_migrate.rb +64 -0
  55. data/lib/invoker/power/port_finder.rb +49 -0
  56. data/lib/invoker/power/power.rb +3 -0
  57. data/lib/invoker/power/powerup.rb +29 -0
  58. data/lib/invoker/power/setup.rb +90 -0
  59. data/lib/invoker/power/setup/distro/arch.rb +15 -0
  60. data/lib/invoker/power/setup/distro/base.rb +57 -0
  61. data/lib/invoker/power/setup/distro/debian.rb +11 -0
  62. data/lib/invoker/power/setup/distro/mint.rb +10 -0
  63. data/lib/invoker/power/setup/distro/opensuse.rb +11 -0
  64. data/lib/invoker/power/setup/distro/redhat.rb +11 -0
  65. data/lib/invoker/power/setup/distro/ubuntu.rb +10 -0
  66. data/lib/invoker/power/setup/files/invoker_forwarder.sh.erb +17 -0
  67. data/lib/invoker/power/setup/files/socat_invoker.service +12 -0
  68. data/lib/invoker/power/setup/linux_setup.rb +105 -0
  69. data/lib/invoker/power/setup/osx_setup.rb +137 -0
  70. data/lib/invoker/power/templates/400.html +40 -0
  71. data/lib/invoker/power/templates/404.html +40 -0
  72. data/lib/invoker/power/templates/503.html +40 -0
  73. data/lib/invoker/power/url_rewriter.rb +40 -0
  74. data/lib/invoker/process_manager.rb +198 -0
  75. data/lib/invoker/process_printer.rb +43 -0
  76. data/lib/invoker/reactor.rb +37 -0
  77. data/lib/invoker/reactor/reader.rb +54 -0
  78. data/lib/invoker/version.rb +47 -0
  79. data/readme.md +25 -0
  80. data/spec/invoker/cli/pinger_spec.rb +22 -0
  81. data/spec/invoker/cli/tail_watcher_spec.rb +39 -0
  82. data/spec/invoker/cli_spec.rb +27 -0
  83. data/spec/invoker/command_worker_spec.rb +45 -0
  84. data/spec/invoker/commander_spec.rb +152 -0
  85. data/spec/invoker/config_spec.rb +361 -0
  86. data/spec/invoker/daemon_spec.rb +34 -0
  87. data/spec/invoker/event/manager_spec.rb +67 -0
  88. data/spec/invoker/invoker_spec.rb +71 -0
  89. data/spec/invoker/ipc/client_handler_spec.rb +54 -0
  90. data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
  91. data/spec/invoker/ipc/message/list_response_spec.rb +24 -0
  92. data/spec/invoker/ipc/message_spec.rb +49 -0
  93. data/spec/invoker/ipc/unix_client_spec.rb +29 -0
  94. data/spec/invoker/power/balancer_spec.rb +22 -0
  95. data/spec/invoker/power/config_spec.rb +18 -0
  96. data/spec/invoker/power/http_parser_spec.rb +32 -0
  97. data/spec/invoker/power/http_response_spec.rb +34 -0
  98. data/spec/invoker/power/pf_migrate_spec.rb +87 -0
  99. data/spec/invoker/power/port_finder_spec.rb +16 -0
  100. data/spec/invoker/power/setup/linux_setup_spec.rb +103 -0
  101. data/spec/invoker/power/setup/osx_setup_spec.rb +105 -0
  102. data/spec/invoker/power/setup_spec.rb +4 -0
  103. data/spec/invoker/power/url_rewriter_spec.rb +70 -0
  104. data/spec/invoker/power/web_sockets_spec.rb +61 -0
  105. data/spec/invoker/process_manager_spec.rb +130 -0
  106. data/spec/invoker/reactor_spec.rb +6 -0
  107. data/spec/spec_helper.rb +43 -0
  108. metadata +389 -0
@@ -0,0 +1,71 @@
1
+ require "spec_helper"
2
+
3
+ describe "Invoker" do
4
+ describe "#darwin?" do
5
+ it "should return true on osx" do
6
+ Invoker.expects(:ruby_platform).returns("x86_64-darwin12.4.0")
7
+ expect(Invoker.darwin?).to be_truthy
8
+ end
9
+
10
+ it "should return false on linux" do
11
+ Invoker.expects(:ruby_platform).returns("i686-linux")
12
+ expect(Invoker.darwin?).to be_falsey
13
+ end
14
+ end
15
+
16
+ describe "#can_run_balancer?", fakefs: true do
17
+ before { FileUtils.mkdir_p(Invoker::Power::Config.config_dir) }
18
+ it "should return false if setup command was not run" do
19
+ expect(Invoker.can_run_balancer?).to be_falsey
20
+ end
21
+
22
+ it "should return true if setup was run properly" do
23
+ File.open(Invoker::Power::Config.config_file, "w") {|fl|
24
+ fl.write("hello")
25
+ }
26
+ expect(Invoker.can_run_balancer?).to be_truthy
27
+ end
28
+
29
+ it "should not print warning if setup is not run when flag is false" do
30
+ Invoker::Logger.expects(:puts).never()
31
+ Invoker.can_run_balancer?(false)
32
+ end
33
+ end
34
+
35
+ describe "#setup_config_location" do
36
+ before do
37
+ Dir.stubs(:home).returns('/tmp')
38
+ @config_location = File.join('/tmp', '.invoker')
39
+ FileUtils.rm_rf(@config_location)
40
+ end
41
+
42
+ context "when the old config file does not exist" do
43
+ it "creates the new config directory" do
44
+ Invoker.setup_config_location
45
+ expect(Dir.exist?(@config_location)).to be_truthy
46
+ end
47
+ end
48
+
49
+ context "when the old config file exists" do
50
+ before do
51
+ File.open(@config_location, 'w') do |file|
52
+ file.write('invoker config')
53
+ end
54
+ end
55
+
56
+ it "moves the file to the new directory" do
57
+ Invoker.setup_config_location
58
+ expect(Dir.exist?(@config_location)).to be_truthy
59
+ new_config_file = File.join(@config_location, 'config')
60
+ expect(File.exist?(new_config_file)).to be_truthy
61
+ expect(File.read(new_config_file)).to match('invoker config')
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "#home" do
67
+ it "should return home directory using etc module" do
68
+ expect(Invoker.home).to eql ENV['HOME']
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,54 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::IPC::ClientHandler do
4
+ let(:client_socket) { StringIO.new }
5
+ let(:client) { Invoker::IPC::ClientHandler.new(client_socket) }
6
+
7
+ describe "add command" do
8
+ let(:message_object) { MM::Add.new(process_name: 'foo') }
9
+ it "should run if read from socket" do
10
+ invoker_commander.expects(:on_next_tick).with("foo")
11
+ client_socket.string = message_object.encoded_message
12
+
13
+ client.read_and_execute
14
+ end
15
+ end
16
+
17
+ describe "remove command" do
18
+ it "with specific signal" do
19
+ message_object = MM::Remove.new(process_name: 'foo', signal: 'INT')
20
+ invoker_commander.expects(:on_next_tick)
21
+ client_socket.string = message_object.encoded_message
22
+
23
+ client.read_and_execute
24
+ end
25
+
26
+ it "with default signal" do
27
+ message_object = MM::Remove.new(process_name: 'foo')
28
+ invoker_commander.expects(:on_next_tick)
29
+ client_socket.string = message_object.encoded_message
30
+
31
+ client.read_and_execute
32
+ end
33
+ end
34
+
35
+ describe "add_http command" do
36
+ let(:message_object) { MM::AddHttp.new(process_name: 'foo', port: 9000)}
37
+ it "adds the process name and port to dns cache" do
38
+ invoker_dns_cache.expects(:add).with('foo', 9000, nil)
39
+ client_socket.string = message_object.encoded_message
40
+
41
+ client.read_and_execute
42
+ end
43
+ end
44
+
45
+ describe "add_http command with optional ip" do
46
+ let(:message_object) { MM::AddHttp.new(process_name: 'foo', port: 9000, ip: '192.168.0.1')}
47
+ it "adds the process name, port and host ip to dns cache" do
48
+ invoker_dns_cache.expects(:add).with('foo', 9000, '192.168.0.1')
49
+ client_socket.string = message_object.encoded_message
50
+
51
+ client.read_and_execute
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Invoker::IPC::DnsCheckCommand do
4
+ let(:client_socket) { StringIO.new }
5
+ let(:client) { Invoker::IPC::ClientHandler.new(client_socket) }
6
+
7
+ describe "dns check for valid process" do
8
+ let(:message_object) { MM::DnsCheck.new(process_name: 'lolbro') }
9
+ it "should response with dns check response" do
10
+ invoker_dns_cache.expects(:[]).returns('port' => 9000)
11
+ client_socket.string = message_object.encoded_message
12
+
13
+ client.read_and_execute
14
+
15
+ dns_check_response = client_socket.string
16
+ expect(dns_check_response).to match(/9000/)
17
+ end
18
+ end
19
+
20
+ describe "dns check for invalid process" do
21
+ let(:message_object) { MM::DnsCheck.new(process_name: 'foo') }
22
+ it "should response with dns check response" do
23
+ invoker_dns_cache.expects(:[]).returns('port' => nil)
24
+ client_socket.string = message_object.encoded_message
25
+
26
+ client.read_and_execute
27
+
28
+ dns_check_response = client_socket.string
29
+ expect(dns_check_response).to match(/null/)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ describe MM::ListResponse do
4
+ context "serializing a response" do
5
+ let(:process_array) do
6
+ [
7
+ { shell_command: 'foo', process_name: 'foo', dir: '/tmp', pid: 100,
8
+ port: 9000 },
9
+ { shell_command: 'bar', process_name: 'bar', dir: '/tmp', pid: 200,
10
+ port: 9001 }
11
+ ]
12
+ end
13
+
14
+ let(:message) { MM::ListResponse.new(processes: process_array) }
15
+
16
+ it "should prepare proper json" do
17
+ json_hash = message.as_json
18
+ expect(json_hash[:type]).to eql "list_response"
19
+ expect(json_hash[:processes].length).to eql 2
20
+ expect(json_hash[:processes][0]).to be_a(Hash)
21
+ expect(json_hash[:processes][1]).to be_a(Hash)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,49 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::IPC::Message do
4
+ describe "test equality of objects" do
5
+ context "for simple messages" do
6
+ let(:message) { MM::Add.new(process_name: 'foo') }
7
+
8
+ it "object should be reported same if same value" do
9
+ m2 = MM::Add.new(process_name: 'foo')
10
+ expect(message).to eql m2
11
+ end
12
+
13
+ it "should report objects to be not eql if differnt value" do
14
+ m2 = MM::Add.new(process_name: 'bar')
15
+ expect(message).to_not eql m2
16
+ end
17
+ end
18
+
19
+ context "for nested messages" do
20
+ let(:process_array) do
21
+ [
22
+ { shell_command: 'foo', process_name: 'foo', dir: '/tmp', pid: 100,
23
+ port: 9000 },
24
+ { shell_command: 'bar', process_name: 'bar', dir: '/tmp', pid: 200,
25
+ port: 9001 }
26
+ ]
27
+ end
28
+
29
+ let(:message) { MM::ListResponse.new(processes: process_array) }
30
+
31
+ it "should report eql for eql objects" do
32
+ m2 = MM::ListResponse.new(processes: process_array)
33
+ expect(message).to eql m2
34
+ end
35
+
36
+ it "should report not equal for different objects" do
37
+ another_process_array = [
38
+ { shell_command: 'baz', process_name: 'foo', dir: '/tmp', pid: 100,
39
+ port: 9000 },
40
+ { shell_command: 'bar', process_name: 'bar', dir: '/tmp', pid: 200,
41
+ port: 9001 }
42
+ ]
43
+
44
+ m2 = MM::ListResponse.new(processes: another_process_array)
45
+ expect(message).to_not eql m2
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::IPC::UnixClient do
4
+ let(:unix_client) { described_class.new }
5
+ let(:socket) { StringIO.new }
6
+
7
+ describe "serializing a " do
8
+ it "list request should work" do
9
+ unix_client.expects(:open_client_socket).yields(socket)
10
+ unix_client.send_command("list")
11
+
12
+ expect(socket.string).to match(/list/)
13
+ end
14
+
15
+ it "add request should work" do
16
+ unix_client.expects(:open_client_socket).yields(socket)
17
+ unix_client.send_command("add", process_name: "hello")
18
+
19
+ expect(socket.string).to match(/hello/)
20
+ end
21
+ end
22
+
23
+ describe ".send_command" do
24
+ it "calls the send_command instance method" do
25
+ Invoker::IPC::UnixClient.any_instance.expects(:send_command).once
26
+ Invoker::IPC::UnixClient.send_command("list")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Invoker::Power::Balancer do
4
+ before do
5
+ @http_connection = mock("connection")
6
+ @balancer = Invoker::Power::Balancer.new(@http_connection, "http")
7
+ end
8
+
9
+ context "when Host field is missing in the request" do
10
+ it "should return 400 as response when Host is missing" do
11
+ headers = {}
12
+ @http_connection.expects(:send_data).with() { |value| value =~ /400 Bad Request/i }
13
+ @balancer.headers_received(headers)
14
+ end
15
+
16
+ it "should return 400 as response when Host is empty" do
17
+ headers = { 'Host' => '' }
18
+ @http_connection.expects(:send_data).with() { |value| value =~ /400 Bad Request/i }
19
+ @balancer.headers_received(headers)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ require "spec_helper"
2
+
3
+ describe "Invoker Power configuration", fakefs: true do
4
+ describe "#create" do
5
+ it "should create a config file given a hash" do
6
+ FileUtils.mkdir_p(inv_conf_dir)
7
+ config = Invoker::Power::Config.create(
8
+ dns_port: 1200, http_port: 1201, ipfw_rule_number: 010
9
+ )
10
+ expect(File.exist?(Invoker::Power::Config.config_file)).to be_truthy
11
+
12
+ config = Invoker::Power::Config.load_config()
13
+ expect(config.dns_port).to eq(1200)
14
+ expect(config.http_port).to eq(1201)
15
+ expect(config.ipfw_rule_number).to eq(010)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::Power::HttpParser do
4
+ let(:parser) { Invoker::Power::HttpParser.new('https') }
5
+
6
+ describe "complete message received" do
7
+ before { parser.reset }
8
+ it "should call header received with full header" do
9
+ @header = nil
10
+ parser.on_headers_complete { |header| @header = header }
11
+ parser << "HTTP/1.1 200 OK\r\n"
12
+ parser << "Content-Type: text/plain;charset=utf-8\r\n"
13
+ parser << "Content-Length: 5\r\n"
14
+ parser << "Connection: close\r\n\r\n"
15
+ parser << "hello"
16
+
17
+ expect(@header['Content-Type']).to eql "text/plain;charset=utf-8"
18
+ end
19
+
20
+ it "should return complete message with x_forwarded added" do
21
+ complete_message = nil
22
+ parser.on_message_complete { |message| complete_message = message }
23
+ parser.on_headers_complete { |header| @header = header }
24
+ parser << "HTTP/1.1 200 OK\r\n"
25
+ parser << "Content-Type: text/plain;charset=utf-8\r\n"
26
+ parser << "Content-Length: 5\r\n"
27
+ parser << "Connection: close\r\n\r\n"
28
+ parser << "hello"
29
+ expect(complete_message).to match(/X_FORWARDED_PROTO:/i)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+ require "tempfile"
3
+
4
+ describe Invoker::Power::HttpResponse do
5
+ before do
6
+ @http_response = Invoker::Power::HttpResponse.new()
7
+ end
8
+
9
+ it "should allow user to send a file" do
10
+ begin
11
+ file = Tempfile.new("error.html")
12
+ file_content = "Error message"
13
+ file.write(file_content)
14
+ file.close
15
+
16
+ @http_response.use_file_as_body(file.path)
17
+ expect(@http_response.body).to eq(file_content)
18
+ expect(@http_response.http_string).to include(file_content)
19
+ ensure
20
+ file.unlink
21
+ end
22
+ end
23
+
24
+ it "should allow user to set headers" do
25
+ @http_response["Content-Type"] = "text/html"
26
+ expect(@http_response.header["Content-Type"]).to eq("text/html")
27
+ expect(@http_response.http_string).to include("Content-Type")
28
+ end
29
+
30
+ it "should allow user to set status" do
31
+ @http_response.status = 503
32
+ expect(@http_response.http_string).to include(Invoker::Power::HttpResponse::STATUS_MAPS[503])
33
+ end
34
+ end
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::Power::PfMigrate do
4
+ before do
5
+ FileUtils.mkdir_p("/tmp/.invoker")
6
+ @old_firewall_file = Invoker::Power::OsxSetup::FIREWALL_PLIST_FILE
7
+ Invoker::Power::OsxSetup.const_set(:FIREWALL_PLIST_FILE, "/tmp/.invoker/firewall")
8
+ end
9
+
10
+ after do
11
+ Invoker::Power::OsxSetup.const_set(:FIREWALL_PLIST_FILE, @old_firewall_file)
12
+ end
13
+
14
+ let(:pf_migrator) { Invoker::Power::PfMigrate.new }
15
+
16
+ describe "#firewall_config_requires_migration?" do
17
+ context "for nonosx systems " do
18
+ it "should return false" do
19
+ Invoker.expects(:darwin?).returns(false)
20
+ expect(pf_migrator.firewall_config_requires_migration?).to eq(false)
21
+ end
22
+ end
23
+
24
+ context "for osx systems" do
25
+ before { Invoker.expects(:darwin?).returns(true) }
26
+
27
+ context "for osx < yosemite" do
28
+ it "should return false" do
29
+ pf_migrator.expects(:osx_version).returns(Invoker::Version.new("13.4.0"))
30
+ expect(pf_migrator.firewall_config_requires_migration?).to eq(false)
31
+ end
32
+ end
33
+
34
+ context "for osx > yosemite with existing ipfw rule" do
35
+ before do
36
+ write_to_firewall_file("ipfw firewall rule")
37
+ end
38
+ it "should return true" do
39
+ pf_migrator.expects(:osx_version).returns(Invoker::Version.new("14.0.0"))
40
+ expect(pf_migrator.firewall_config_requires_migration?).to eql(true)
41
+ end
42
+ end
43
+
44
+ context "for osx >= yosemite with no ipfw rule" do
45
+ before do
46
+ write_to_firewall_file("rdr pass on")
47
+ end
48
+ it "should return false" do
49
+ pf_migrator.expects(:osx_version).returns(Invoker::Version.new("14.0.0"))
50
+ expect(pf_migrator.firewall_config_requires_migration?).to eql(false)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "#migrate" do
57
+ before do
58
+ @original_invoker_config = Invoker.config
59
+ mock_config = mock()
60
+ mock_config.stubs(:http_port).returns(80)
61
+ mock_config.stubs(:https_port).returns(443)
62
+ mock_config.stubs(:tld).returns('dev')
63
+ Invoker.config = mock_config
64
+ end
65
+
66
+ after do
67
+ Invoker.config = @original_invoker_config
68
+ end
69
+
70
+ it "should migrate firewall to new system" do
71
+ pf_migrator.expects(:firewall_config_requires_migration?).returns(true)
72
+ pf_migrator.expects(:ask_user_for_migration).returns(true)
73
+ pf_migrator.expects(:sudome)
74
+ pf_migrator.expects(:drop_to_normal_user)
75
+ pf_migrator.expects(:exit)
76
+
77
+ pf_migrator.migrate
78
+ expect(pf_migrator.check_firewall_file?).to eql(false)
79
+ end
80
+ end
81
+
82
+ def write_to_firewall_file(content)
83
+ File.open(Invoker::Power::OsxSetup::FIREWALL_PLIST_FILE, "w") do |fl|
84
+ fl.write(content)
85
+ end
86
+ end
87
+ end