meepo 1.5.2

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 (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