invoker 1.5.3 → 1.5.8

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 (42) hide show
  1. checksums.yaml +5 -5
  2. data/lib/invoker.rb +5 -6
  3. data/lib/invoker/cli.rb +47 -14
  4. data/lib/invoker/command_worker.rb +6 -2
  5. data/lib/invoker/commander.rb +8 -2
  6. data/lib/invoker/ipc/unix_client.rb +2 -2
  7. data/lib/invoker/parsers/config.rb +3 -3
  8. data/lib/invoker/power/balancer.rb +6 -4
  9. data/lib/invoker/power/setup.rb +9 -6
  10. data/lib/invoker/power/setup/distro/base.rb +36 -13
  11. data/lib/invoker/power/setup/distro/ubuntu.rb +36 -0
  12. data/lib/invoker/power/setup/linux_setup.rb +11 -19
  13. data/lib/invoker/power/setup/osx_setup.rb +4 -4
  14. data/lib/invoker/process_manager.rb +7 -4
  15. data/lib/invoker/process_printer.rb +16 -0
  16. data/lib/invoker/reactor/reader.rb +17 -6
  17. data/lib/invoker/version.rb +1 -1
  18. data/spec/invoker/config_spec.rb +4 -4
  19. data/spec/invoker/power/balancer_spec.rb +31 -0
  20. data/spec/invoker/power/setup/linux_setup_spec.rb +97 -34
  21. data/spec/invoker/power/url_rewriter_spec.rb +24 -25
  22. data/spec/spec_helper.rb +3 -3
  23. metadata +56 -69
  24. data/.coveralls.yml +0 -1
  25. data/.gitignore +0 -16
  26. data/.rspec +0 -2
  27. data/.rubocop.yml +0 -29
  28. data/.travis.yml +0 -10
  29. data/Dockerfile +0 -7
  30. data/Gemfile +0 -13
  31. data/MIT-LICENSE +0 -20
  32. data/Rakefile +0 -15
  33. data/TODO +0 -5
  34. data/contrib/completion/invoker-completion.bash +0 -70
  35. data/contrib/completion/invoker-completion.zsh +0 -62
  36. data/examples/hello_sinatra.rb +0 -26
  37. data/examples/sample.ini +0 -3
  38. data/invoker.gemspec +0 -43
  39. data/lib/invoker/power/pf_migrate.rb +0 -64
  40. data/lib/invoker/power/setup/distro/mint.rb +0 -10
  41. data/readme.md +0 -25
  42. data/spec/invoker/power/pf_migrate_spec.rb +0 -87
@@ -1,5 +1,4 @@
1
1
  require "invoker/power/setup/distro/base"
2
- require "facter"
3
2
  require 'erb'
4
3
  require 'fileutils'
5
4
 
@@ -9,8 +8,8 @@ module Invoker
9
8
  attr_accessor :distro_installer
10
9
 
11
10
  def setup_invoker
12
- if get_user_confirmation?
13
- initialize_distro_installer
11
+ initialize_distro_installer
12
+ if distro_installer.get_user_confirmation?
14
13
  find_open_ports
15
14
  distro_installer.install_required_software
16
15
  install_resolver
@@ -19,7 +18,7 @@ module Invoker
19
18
  drop_to_normal_user
20
19
  create_config_file
21
20
  else
22
- Invoker::Logger.puts("Invoker is not configured to serve from subdomains".color(:red))
21
+ Invoker::Logger.puts("Invoker is not configured to serve from subdomains".colorize(:red))
23
22
  end
24
23
  self
25
24
  end
@@ -35,6 +34,12 @@ module Invoker
35
34
  Invoker::Power::Config.delete
36
35
  end
37
36
 
37
+ def build_power_config
38
+ config = super
39
+ config[:tld] = distro_installer.tld
40
+ config
41
+ end
42
+
38
43
  def resolver_file
39
44
  distro_installer.resolver_file
40
45
  end
@@ -50,16 +55,11 @@ module Invoker
50
55
  private
51
56
 
52
57
  def initialize_distro_installer
53
- # Create a new facter check for systemctl (in systemd)
54
- Facter.add(:systemctl) do
55
- setcode do
56
- Facter::Util::Resolution.exec("[ -e /usr/bin/systemctl ] && echo 'true' || echo 'false'")
57
- end
58
- end
59
- @distro_installer = Invoker::Power::Distro::Base.distro_installer(tld)
58
+ @distro_installer ||= Invoker::Power::Distro::Base.distro_installer(tld)
60
59
  end
61
60
 
62
61
  def install_resolver
62
+ return if resolver_file.nil?
63
63
  File.open(resolver_file, "w") do |fl|
64
64
  fl.write(resolver_file_content)
65
65
  end
@@ -92,14 +92,6 @@ address=/#{tld}/127.0.0.1
92
92
  FileUtils.cp(socat_unit, Invoker::Power::Distro::Base::SOCAT_SYSTEMD)
93
93
  system("chmod 644 #{Invoker::Power::Distro::Base::SOCAT_SYSTEMD}")
94
94
  end
95
-
96
- def get_user_confirmation?
97
- Invoker::Logger.puts("Invoker is going to install dnsmasq and socat on this machine."\
98
- " It is also going to install a local resolver for .#{tld} domain and a socat service"\
99
- " which will forward all local requests on port 80 and 443 to another port")
100
- Invoker::Logger.puts("If you still want to proceed with installation, press y.")
101
- Invoker::CLI::Question.agree("Proceed with installation (y/n) : ")
102
- end
103
95
  end
104
96
  end
105
97
  end
@@ -17,7 +17,7 @@ module Invoker
17
17
  drop_to_normal_user
18
18
  create_config_file
19
19
  else
20
- Invoker::Logger.puts("Invoker is not configured to serve from subdomains".color(:red))
20
+ Invoker::Logger.puts("Invoker is not configured to serve from subdomains".colorize(:red))
21
21
  end
22
22
  self
23
23
  end
@@ -42,7 +42,7 @@ module Invoker
42
42
  def install_resolver(dns_port)
43
43
  open_resolver_for_write { |fl| fl.write(resolve_string(dns_port)) }
44
44
  rescue Errno::EACCES
45
- Invoker::Logger.puts("Running setup requires root access, please rerun it with sudo".color(:red))
45
+ Invoker::Logger.puts("Running setup requires root access, please rerun it with sudo".colorize(:red))
46
46
  raise
47
47
  end
48
48
 
@@ -110,7 +110,7 @@ port #{dns_port}
110
110
  return true unless File.exist?(resolver_file)
111
111
 
112
112
  Invoker::Logger.puts "Invoker has detected an existing Pow installation. We recommend "\
113
- "that you uninstall pow and rerun this setup.".color(:red)
113
+ "that you uninstall pow and rerun this setup.".colorize(:red)
114
114
  Invoker::Logger.puts "If you have already uninstalled Pow, proceed with installation"\
115
115
  " by pressing y/n."
116
116
  replace_resolver_flag = Invoker::CLI::Question.agree("Replace Pow configuration (y/n) : ")
@@ -118,7 +118,7 @@ port #{dns_port}
118
118
  if replace_resolver_flag
119
119
  Invoker::Logger.puts "Invoker has overwritten one or more files created by Pow. "\
120
120
  "If .#{tld} domains still don't resolve locally, try turning off the wi-fi"\
121
- " and turning it on. It'll force OS X to reload network configuration".color(:green)
121
+ " and turning it on. It'll force OS X to reload network configuration".colorize(:green)
122
122
  end
123
123
  replace_resolver_flag
124
124
  end
@@ -30,7 +30,7 @@ module Invoker
30
30
  # @param process_name [String] Command label of process specified in config file.
31
31
  def start_process_by_name(process_name)
32
32
  if process_running?(process_name)
33
- Invoker::Logger.puts "\nProcess '#{process_name}' is already running".color(:red)
33
+ Invoker::Logger.puts "\nProcess '#{process_name}' is already running".colorize(:red)
34
34
  return false
35
35
  end
36
36
 
@@ -49,7 +49,7 @@ module Invoker
49
49
  return false unless worker
50
50
  signal_to_use = remove_message.signal || 'INT'
51
51
 
52
- Invoker::Logger.puts("Removing #{command_label} with signal #{signal_to_use}".color(:red))
52
+ Invoker::Logger.puts("Removing #{command_label} with signal #{signal_to_use}".colorize(:red))
53
53
  kill_or_remove_process(worker.pid, signal_to_use, command_label)
54
54
  end
55
55
 
@@ -128,7 +128,7 @@ module Invoker
128
128
  thread = Thread.new do
129
129
  Process.wait(pid)
130
130
  message = "Process with command #{command_label} exited with status #{$?.exitstatus}"
131
- Invoker::Logger.puts("\n#{message}".color(:red))
131
+ Invoker::Logger.puts("\n#{message}".colorize(:red))
132
132
  Invoker.notify_user(message)
133
133
  Invoker.commander.trigger(command_label, :exit)
134
134
  end
@@ -149,7 +149,7 @@ module Invoker
149
149
  process_kill(pid, signal_to_use)
150
150
  true
151
151
  rescue Errno::ESRCH
152
- Invoker::Logger.puts("Killing process with #{pid} and name #{command_label} failed".color(:red))
152
+ Invoker::Logger.puts("Killing process with #{pid} and name #{command_label} failed".colorize(:red))
153
153
  remove_worker(command_label, false)
154
154
  false
155
155
  end
@@ -168,6 +168,9 @@ module Invoker
168
168
  if worker
169
169
  @open_pipes.delete(worker.pipe_end.fileno)
170
170
  @workers.delete(command_label)
171
+ # Move label color to front of array so it's reused first
172
+ LABEL_COLORS.delete(worker.color)
173
+ LABEL_COLORS.unshift(worker.color)
171
174
  end
172
175
  if trigger_event
173
176
  Invoker.commander.trigger(command_label, :worker_removed)
@@ -19,6 +19,22 @@ module Invoker
19
19
  Formatador.display_compact_table(hash_with_colors)
20
20
  end
21
21
 
22
+ def print_raw_text
23
+ list_response.processes.each do |process|
24
+ Formatador.display_line("[bold]Process Name : #{process.process_name}[/]")
25
+ Formatador.indent {
26
+ Formatador.display_line("Dir : #{process.dir}")
27
+ if process.pid
28
+ Formatador.display_line("PID : #{process.pid}")
29
+ else
30
+ Formatador.display_line("PID : Not Running")
31
+ end
32
+ Formatador.display_line("Port : #{process.port}")
33
+ Formatador.display_line("Command : #{process.shell_command}")
34
+ }
35
+ end
36
+ end
37
+
22
38
  private
23
39
 
24
40
  def colorize_hash(process, color)
@@ -19,18 +19,29 @@ module Invoker
19
19
 
20
20
  def process_read(ready_fd)
21
21
  command_worker = Invoker.commander.get_worker_from_fd(ready_fd)
22
- return unless command_worker
23
22
  begin
24
23
  data = read_data(ready_fd)
25
- command_worker.receive_data(data)
24
+ send_data_to_worker(data, command_worker)
26
25
  rescue Invoker::Errors::ProcessTerminated
27
- remove_from_read_monitoring(command_worker.pipe_end, command_worker)
26
+ remove_from_read_monitoring(command_worker, ready_fd)
27
+ end
28
+ end
29
+
30
+ def send_data_to_worker(data, command_worker)
31
+ if command_worker
32
+ command_worker.receive_data(data)
33
+ else
34
+ Invoker::Logger.puts("No reader found for incoming data")
28
35
  end
29
36
  end
30
37
 
31
- def remove_from_read_monitoring(fd, command_worker)
32
- read_array.delete(fd)
33
- command_worker.unbind
38
+ def remove_from_read_monitoring(command_worker, ready_fd)
39
+ if command_worker
40
+ read_array.delete(command_worker.pipe_end)
41
+ command_worker.unbind
42
+ else
43
+ read_array.delete(ready_fd)
44
+ end
34
45
  rescue StandardError => error
35
46
  Invoker::Logger.puts(error.message)
36
47
  Invoker::Logger.puts(error.backtrace)
@@ -43,5 +43,5 @@ module Invoker
43
43
  Version.new(next_splits.join('.'))
44
44
  end
45
45
  end
46
- VERSION = "1.5.3"
46
+ VERSION = "1.5.8"
47
47
  end
@@ -155,7 +155,7 @@ command = ls
155
155
 
156
156
  describe "Procfile" do
157
157
  it "should load Procfiles and create config object" do
158
- File.open("/tmp/Procfile", "w") {|fl|
158
+ File.open("/tmp/Procfile", "w") {|fl|
159
159
  fl.write <<-EOD
160
160
  web: bundle exec rails s -p $PORT
161
161
  EOD
@@ -170,7 +170,7 @@ web: bundle exec rails s -p $PORT
170
170
 
171
171
  describe "Copy of DNS information" do
172
172
  it "should allow copy of DNS information" do
173
- File.open("/tmp/Procfile", "w") {|fl|
173
+ File.open("/tmp/Procfile", "w") {|fl|
174
174
  fl.write <<-EOD
175
175
  web: bundle exec rails s -p $PORT
176
176
  EOD
@@ -251,7 +251,7 @@ index = 1
251
251
  config = Invoker::Parsers::Config.new(file.path, 9000)
252
252
  processes = config.autorunnable_processes
253
253
  expect(processes.map(&:label)).to eq(['panda-auth', 'postgres', 'memcached'])
254
- expect(processes[0].sleep_duration).to eq(1)
254
+ expect(processes[0].sleep_duration).to eq(0)
255
255
  expect(processes[1].sleep_duration).to eq(5)
256
256
  ensure
257
257
  file.unlink()
@@ -342,7 +342,7 @@ some_other_process: some_other_command
342
342
  expect(config.process("some_process").cmd).to eq("some_command")
343
343
  processes = config.autorunnable_processes
344
344
  process_1 = processes[0]
345
- expect(process_1.sleep_duration).to eq(1)
345
+ expect(process_1.sleep_duration).to eq(0)
346
346
  expect(process_1.index).to eq(0)
347
347
  ensure
348
348
  File.delete(invoker_ini)
@@ -6,6 +6,37 @@ describe Invoker::Power::Balancer do
6
6
  @balancer = Invoker::Power::Balancer.new(@http_connection, "http")
7
7
  end
8
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
+
9
40
  context "when Host field is missing in the request" do
10
41
  it "should return 400 as response when Host is missing" do
11
42
  headers = {}
@@ -16,43 +16,55 @@ def mock_socat_scripts
16
16
  fl.write(socat_content)
17
17
  end
18
18
  FileUtils.mkdir_p("/usr/bin")
19
+ FileUtils.mkdir_p("/etc/systemd/system")
19
20
  end
20
21
 
21
22
  describe Invoker::Power::LinuxSetup, fakefs: true do
22
23
  before do
23
24
  FileUtils.mkdir_p(inv_conf_dir)
24
25
  FileUtils.mkdir_p(Invoker::Power::Distro::Base::RESOLVER_DIR)
26
+ Invoker.config = mock
25
27
  end
26
28
 
27
- let(:invoker_setup) { Invoker::Power::LinuxSetup.new('dev') }
28
- let(:distro_installer) { Invoker::Power::Distro::Ubuntu.new('dev') }
29
+ let(:invoker_setup) { Invoker::Power::LinuxSetup.new('test') }
30
+ let(:distro_installer) { Invoker::Power::Distro::Ubuntu.new('test') }
29
31
 
30
- describe "should only proceed after user confirmation" do
31
- before { invoker_setup.distro_installer = distro_installer }
32
+ before do
33
+ invoker_setup.distro_installer = distro_installer
34
+ end
32
35
 
33
- it "should create config file with port" do
34
- invoker_setup.expects(:initialize_distro_installer).returns(true)
35
- invoker_setup.expects(:get_user_confirmation?).returns(true)
36
- invoker_setup.expects(:install_resolver).returns(true)
37
- invoker_setup.expects(:install_port_forwarder).returns(true)
38
- invoker_setup.expects(:drop_to_normal_user).returns(true)
36
+ it "should only proceed after user confirmation" do
37
+ distro_installer.expects(:get_user_confirmation?).returns(false)
39
38
 
40
- distro_installer.expects(:install_required_software)
41
- distro_installer.expects(:restart_services)
39
+ invoker_setup.setup_invoker
42
40
 
43
- invoker_setup.setup_invoker
41
+ expect { Invoker::Power::Config.load_config }.to raise_error(Errno::ENOENT)
42
+ end
44
43
 
45
- config = Invoker::Power::Config.load_config
46
- expect(config.http_port).not_to be_nil
47
- expect(config.dns_port).to be_nil
48
- expect(config.https_port).not_to be_nil
49
- end
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
50
61
  end
51
62
 
52
- describe "configuring dnsmasq and socat" do
63
+ describe "configuring services" do
64
+ let(:config) { Invoker::Power::Config.load_config }
65
+
53
66
  before(:all) do
54
67
  @original_invoker_config = Invoker.config
55
- Invoker.config = mock
56
68
  end
57
69
 
58
70
  after(:all) do
@@ -60,26 +72,21 @@ describe Invoker::Power::LinuxSetup, fakefs: true do
60
72
  end
61
73
 
62
74
  before(:each) do
63
- invoker_setup.distro_installer = distro_installer
64
75
  mock_socat_scripts
65
76
  end
66
77
 
67
- it "should create proper config file" do
78
+ def run_setup
68
79
  invoker_setup.expects(:initialize_distro_installer).returns(true)
69
- invoker_setup.expects(:get_user_confirmation?).returns(true)
70
80
  invoker_setup.expects(:drop_to_normal_user).returns(true)
71
81
 
82
+ distro_installer.expects(:get_user_confirmation?).returns(true)
72
83
  distro_installer.expects(:install_required_software)
73
84
  distro_installer.expects(:restart_services)
74
85
 
75
86
  invoker_setup.setup_invoker
87
+ end
76
88
 
77
- config = Invoker::Power::Config.load_config
78
-
79
- dnsmasq_content = File.read(distro_installer.resolver_file)
80
- expect(dnsmasq_content.strip).to_not be_empty
81
- expect(dnsmasq_content).to match(/dev/)
82
-
89
+ def test_socat_config
83
90
  socat_content = File.read(Invoker::Power::Distro::Base::SOCAT_SHELLSCRIPT)
84
91
  expect(socat_content.strip).to_not be_empty
85
92
  expect(socat_content.strip).to match(/#{config.https_port}/)
@@ -88,15 +95,71 @@ describe Invoker::Power::LinuxSetup, fakefs: true do
88
95
  service_file = File.read(Invoker::Power::Distro::Base::SOCAT_SYSTEMD)
89
96
  expect(service_file.strip).to_not be_empty
90
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
91
119
  end
92
120
 
93
121
  describe 'resolver file' do
94
122
  context 'user sets up a custom top level domain' do
95
- it 'should create the correct resolver file' do
96
- linux_setup = Invoker::Power::LinuxSetup.new('local')
97
- suse_installer = Invoker::Power::Distro::Opensuse.new('local')
98
- linux_setup.distro_installer = suse_installer
99
- expect(linux_setup.resolver_file).to eq('/etc/dnsmasq.d/local-tld')
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.'
100
163
  end
101
164
  end
102
165
  end