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,67 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::Event::Manager do
4
+ describe "Run scheduled events" do
5
+ before do
6
+ @event_manager = Invoker::Event::Manager.new()
7
+ end
8
+
9
+ it "should run matched events" do
10
+ @event_manager.schedule_event("foo", :exit) { 'exit foo' }
11
+ @event_manager.trigger("foo", :exit)
12
+
13
+ @event_manager.run_scheduled_events do |event|
14
+ expect(event.block.call).to eq("exit foo")
15
+ end
16
+
17
+ expect(@event_manager.scheduled_events).to be_empty
18
+ expect(@event_manager.triggered_events).to be_empty
19
+ end
20
+
21
+ it "should remove triggrered and scheduld events on run" do
22
+ @event_manager.schedule_event("foo", :exit) { 'exit foo' }
23
+ @event_manager.schedule_event("bar", :entry) { "entry bar"}
24
+ @event_manager.trigger("foo", :exit)
25
+ @event_manager.trigger("baz", :exit)
26
+
27
+ @event_manager.run_scheduled_events do |event|
28
+ expect(event.block.call).to eq("exit foo")
29
+ end
30
+
31
+ expect(@event_manager.scheduled_events).not_to be_empty
32
+ expect(@event_manager.triggered_events).not_to be_empty
33
+
34
+ baz_containing_event = @event_manager.triggered_events.map(&:command_label)
35
+ expect(baz_containing_event).to include("baz")
36
+
37
+ bar_containing_scheduled_event = @event_manager.scheduled_events.keys
38
+ expect(bar_containing_scheduled_event).to include("bar")
39
+ end
40
+
41
+ it "should handle multiple events for same command" do
42
+ @event_manager.schedule_event("foo", :exit) { 'exit foo' }
43
+ @event_manager.schedule_event("foo", :entry) { "entry bar"}
44
+ @event_manager.trigger("foo", :exit)
45
+
46
+ @event_manager.run_scheduled_events { |event| }
47
+
48
+
49
+ @event_manager.schedule_event("foo", :exit) { 'exit foo' }
50
+ @event_manager.trigger("foo", :exit)
51
+
52
+ expect(@event_manager.scheduled_events).not_to be_empty
53
+ expect(@event_manager.triggered_events).not_to be_empty
54
+ end
55
+
56
+ it "should not run unmatched events" do
57
+ @event_manager.schedule_event("bar", :entry) { "entry bar"}
58
+ @event_manager.trigger("foo", :exit)
59
+
60
+ events_ran = false
61
+ @event_manager.run_scheduled_events do |event|
62
+ events_ran = true
63
+ end
64
+ expect(events_ran).to eql false
65
+ end
66
+ end
67
+ end
@@ -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,53 @@
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 not capitalized" do
10
+ before(:all) do
11
+ @original_invoker_config = Invoker.config
12
+ end
13
+
14
+ def mock_invoker_tld_as(domain)
15
+ Invoker.config = mock
16
+ Invoker.config.stubs(:tld).returns(domain)
17
+ end
18
+
19
+ after(:all) do
20
+ Invoker.config = @original_invoker_config
21
+ end
22
+
23
+ it "should not return 400 when host is lowercase" do
24
+ headers = { 'host' => 'somehost.com' }
25
+ mock_invoker_tld_as('test')
26
+ @http_connection.expects(:send_data).with() { |value| value =~ /404 Not Found/i }
27
+ @http_connection.expects(:close_connection_after_writing)
28
+ @balancer.headers_received(headers)
29
+ end
30
+
31
+ it "should not return 400 when host is written as HoSt" do
32
+ headers = { 'HoSt' => 'somehost.com' }
33
+ mock_invoker_tld_as('test')
34
+ @http_connection.expects(:send_data).with() { |value| value =~ /404 Not Found/i }
35
+ @http_connection.expects(:close_connection_after_writing)
36
+ @balancer.headers_received(headers)
37
+ end
38
+ end
39
+
40
+ context "when Host field is missing in the request" do
41
+ it "should return 400 as response when Host is missing" do
42
+ headers = {}
43
+ @http_connection.expects(:send_data).with() { |value| value =~ /400 Bad Request/i }
44
+ @balancer.headers_received(headers)
45
+ end
46
+
47
+ it "should return 400 as response when Host is empty" do
48
+ headers = { 'Host' => '' }
49
+ @http_connection.expects(:send_data).with() { |value| value =~ /400 Bad Request/i }
50
+ @balancer.headers_received(headers)
51
+ end
52
+ end
53
+ 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,16 @@
1
+ require "spec_helper"
2
+
3
+ describe "PortFinder" do
4
+ before do
5
+ @port_finder = Invoker::Power::PortFinder.new()
6
+ @port_finder.find_ports
7
+ end
8
+
9
+ it "should find a http port" do
10
+ expect(@port_finder.http_port).not_to be_nil
11
+ end
12
+
13
+ it "should find a dns port" do
14
+ expect(@port_finder.dns_port).not_to be_nil
15
+ end
16
+ end
@@ -0,0 +1,166 @@
1
+ require "spec_helper"
2
+ require "invoker/power/setup/distro/ubuntu"
3
+ require "invoker/power/setup/distro/opensuse"
4
+
5
+ def mock_socat_scripts
6
+ FakeFS.deactivate!
7
+ socat_content = File.read(invoker_setup.forwarder_script)
8
+ socat_systemd = File.read(invoker_setup.socat_unit)
9
+ FakeFS.activate!
10
+ FileUtils.mkdir_p(File.dirname(invoker_setup.forwarder_script))
11
+ FileUtils.mkdir_p(File.dirname(invoker_setup.socat_unit))
12
+ File.open(invoker_setup.socat_unit, "w") do |fl|
13
+ fl.write(socat_systemd)
14
+ end
15
+ File.open(invoker_setup.forwarder_script, "w") do |fl|
16
+ fl.write(socat_content)
17
+ end
18
+ FileUtils.mkdir_p("/usr/bin")
19
+ FileUtils.mkdir_p("/etc/systemd/system")
20
+ end
21
+
22
+ describe Invoker::Power::LinuxSetup, fakefs: true do
23
+ before do
24
+ FileUtils.mkdir_p(inv_conf_dir)
25
+ FileUtils.mkdir_p(Invoker::Power::Distro::Base::RESOLVER_DIR)
26
+ Invoker.config = mock
27
+ end
28
+
29
+ let(:invoker_setup) { Invoker::Power::LinuxSetup.new('test') }
30
+ let(:distro_installer) { Invoker::Power::Distro::Ubuntu.new('test') }
31
+
32
+ before do
33
+ invoker_setup.distro_installer = distro_installer
34
+ end
35
+
36
+ it "should only proceed after user confirmation" do
37
+ distro_installer.expects(:get_user_confirmation?).returns(false)
38
+
39
+ invoker_setup.setup_invoker
40
+
41
+ expect { Invoker::Power::Config.load_config }.to raise_error(Errno::ENOENT)
42
+ end
43
+
44
+ it "should create config file with http(s) ports" do
45
+ invoker_setup.expects(:initialize_distro_installer).returns(true)
46
+ invoker_setup.expects(:install_resolver).returns(true)
47
+ invoker_setup.expects(:install_port_forwarder).returns(true)
48
+ invoker_setup.expects(:drop_to_normal_user).returns(true)
49
+
50
+ distro_installer.expects(:get_user_confirmation?).returns(true)
51
+ distro_installer.expects(:install_required_software)
52
+ distro_installer.expects(:restart_services)
53
+
54
+ invoker_setup.setup_invoker
55
+
56
+ config = Invoker::Power::Config.load_config
57
+ expect(config.tld).to eq('test')
58
+ expect(config.http_port).not_to be_nil
59
+ expect(config.dns_port).to be_nil
60
+ expect(config.https_port).not_to be_nil
61
+ end
62
+
63
+ describe "configuring services" do
64
+ let(:config) { Invoker::Power::Config.load_config }
65
+
66
+ before(:all) do
67
+ @original_invoker_config = Invoker.config
68
+ end
69
+
70
+ after(:all) do
71
+ Invoker.config = @original_invoker_config
72
+ end
73
+
74
+ before(:each) do
75
+ mock_socat_scripts
76
+ end
77
+
78
+ def run_setup
79
+ invoker_setup.expects(:initialize_distro_installer).returns(true)
80
+ invoker_setup.expects(:drop_to_normal_user).returns(true)
81
+
82
+ distro_installer.expects(:get_user_confirmation?).returns(true)
83
+ distro_installer.expects(:install_required_software)
84
+ distro_installer.expects(:restart_services)
85
+
86
+ invoker_setup.setup_invoker
87
+ end
88
+
89
+ def test_socat_config
90
+ socat_content = File.read(Invoker::Power::Distro::Base::SOCAT_SHELLSCRIPT)
91
+ expect(socat_content.strip).to_not be_empty
92
+ expect(socat_content.strip).to match(/#{config.https_port}/)
93
+ expect(socat_content.strip).to match(/#{config.http_port}/)
94
+
95
+ service_file = File.read(Invoker::Power::Distro::Base::SOCAT_SYSTEMD)
96
+ expect(service_file.strip).to_not be_empty
97
+ end
98
+
99
+ context 'on ubuntu with systemd-resolved' do
100
+ it "should create socat config & set tld to localhost" do
101
+ distro_installer.expects(:using_systemd_resolved?).at_least_once.returns(true)
102
+ run_setup
103
+ expect(distro_installer.resolver_file).to be_nil
104
+ test_socat_config
105
+ expect(config.tld).to eq('localhost')
106
+ end
107
+ end
108
+
109
+ context 'on non-systemd-resolved distro' do
110
+ it "should create dnsmasq & socat configs" do
111
+ run_setup
112
+ dnsmasq_content = File.read(distro_installer.resolver_file)
113
+ expect(dnsmasq_content.strip).to_not be_empty
114
+ expect(dnsmasq_content).to match(/test/)
115
+
116
+ test_socat_config
117
+ end
118
+ end
119
+ end
120
+
121
+ describe 'resolver file' do
122
+ context 'user sets up a custom top level domain' do
123
+ let(:tld) { 'local' }
124
+ let(:linux_setup) { Invoker::Power::LinuxSetup.new(tld) }
125
+
126
+ context 'on ubuntu with systemd-resolved' do
127
+ it 'should not create a resolver file' do
128
+ ubuntu_installer = Invoker::Power::Distro::Ubuntu.new(tld)
129
+ linux_setup.distro_installer = ubuntu_installer
130
+ ubuntu_installer.expects(:using_systemd_resolved?).at_least_once.returns(true)
131
+ expect(linux_setup.resolver_file).to eq(nil)
132
+ end
133
+ end
134
+
135
+ context 'on non-systemd-resolved distro' do
136
+ it 'should create the correct resolver file' do
137
+ suse_installer = Invoker::Power::Distro::Opensuse.new(tld)
138
+ linux_setup.distro_installer = suse_installer
139
+ expect(linux_setup.resolver_file).to eq("/etc/dnsmasq.d/#{tld}-tld")
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ describe Invoker::Power::Distro::Base, docker: true do
147
+ describe '.distro_installer' do
148
+ it 'correctly recognizes the current distro' do
149
+ case ENV['DISTRO']
150
+ when 'archlinux', 'manjarolinux/base'
151
+ expect(described_class.distro_installer('')).to be_a Invoker::Power::Distro::Arch
152
+ when 'debian'
153
+ expect(described_class.distro_installer('')).to be_a Invoker::Power::Distro::Debian
154
+ when 'fedora'
155
+ expect(described_class.distro_installer('')).to be_a Invoker::Power::Distro::Redhat
156
+ when 'linuxmintd/mint20-amd64', 'ubuntu'
157
+ expect(described_class.distro_installer('')).to be_a Invoker::Power::Distro::Ubuntu
158
+ when 'opensuse/leap', 'opensuse/tumbleweed'
159
+ expect(described_class.distro_installer('')).to be_a Invoker::Power::Distro::Opensuse
160
+ when nil
161
+ else
162
+ raise 'Unrecognized Linux distro. Please add the appropriate docker image to the travis build matrix, update the described method, and add a case here.'
163
+ end
164
+ end
165
+ end
166
+ end