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,35 @@
1
+ module Invoker
2
+ module IPC
3
+ module Message
4
+ class ListResponse < Base
5
+ include Serialization
6
+ message_attributes :processes
7
+ def initialize(options)
8
+ self.processes = []
9
+ process_array = options[:processes] || options['processes']
10
+ process_array.each do |process_hash|
11
+ processes << Process.new(process_hash)
12
+ end
13
+ end
14
+
15
+ def self.from_workers(workers)
16
+ process_array = []
17
+ Invoker.config.processes.each do |process|
18
+ worker_attrs = {
19
+ shell_command: process.cmd,
20
+ process_name: process.label,
21
+ dir: process.dir,
22
+ port: process.port
23
+ }
24
+ if worker = workers[process.label]
25
+ worker_attrs.update(pid: worker.pid)
26
+ end
27
+ process_array << worker_attrs
28
+ end
29
+
30
+ new(processes: process_array)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,10 @@
1
+ module Invoker
2
+ module IPC
3
+ module Message
4
+ class TailResponse < Base
5
+ include Serialization
6
+ message_attributes :tail_line
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Invoker
2
+ module IPC
3
+ class PingCommand < BaseCommand
4
+ def run_command(message_object)
5
+ pong = Invoker::IPC::Message::Pong.new(status: 'pong')
6
+ send_data(pong)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module Invoker
2
+ module IPC
3
+ class ReloadCommand < BaseCommand
4
+ def run_command(message_object)
5
+ Invoker.commander.on_next_tick(message_object) do |reload_message|
6
+ restart_process(reload_message)
7
+ end
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Invoker
2
+ module IPC
3
+ class RemoveCommand < BaseCommand
4
+ def run_command(message_object)
5
+ Invoker.commander.on_next_tick(message_object) do |remove_message|
6
+ stop_process(remove_message)
7
+ end
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ require "fileutils"
2
+
3
+ module Invoker
4
+ module IPC
5
+ class Server
6
+ SOCKET_PATH = "/tmp/invoker"
7
+ def initialize
8
+ @open_clients = []
9
+ Socket.unix_server_loop(SOCKET_PATH) do |sock, client_addrinfo|
10
+ Thread.new { process_client(sock) }
11
+ end
12
+ end
13
+
14
+ def clean_old_socket
15
+ if File.exist?(SOCKET_PATH)
16
+ FileUtils.rm(SOCKET_PATH, :force => true)
17
+ end
18
+ end
19
+
20
+ def process_client(client_socket)
21
+ client = Invoker::IPC::ClientHandler.new(client_socket)
22
+ client.read_and_execute
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ module Invoker
2
+ module IPC
3
+ class TailCommand < BaseCommand
4
+ def run_command(message_object)
5
+ Invoker::Logger.puts("Adding #{message_object.process_names.inspect}")
6
+ Invoker.tail_watchers.add(message_object.process_names, client_socket)
7
+ false
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,60 @@
1
+ module Invoker
2
+ module IPC
3
+ class UnixClient
4
+ def send_command(command, message = {})
5
+ message_object = get_message_object(command, message)
6
+ open_client_socket do |socket|
7
+ send_json_message(socket, message_object)
8
+ socket.flush
9
+ if block_given?
10
+ response_object = Invoker::IPC.message_from_io(socket)
11
+ yield response_object
12
+ end
13
+ end
14
+ end
15
+
16
+ def send_and_receive(command, message = {})
17
+ response = nil
18
+ message_object = get_message_object(command, message)
19
+ open_client_socket(false) do |socket|
20
+ send_json_message(socket, message_object)
21
+ socket.flush
22
+ response = Invoker::IPC.message_from_io(socket)
23
+ end
24
+ response
25
+ end
26
+
27
+ def send_and_wait(command, message = {})
28
+ begin
29
+ socket = Socket.unix(Invoker::IPC::Server::SOCKET_PATH)
30
+ rescue
31
+ abort("Invoker does not seem to be running".color(:red))
32
+ end
33
+ message_object = get_message_object(command, message)
34
+ send_json_message(socket, message_object)
35
+ socket.flush
36
+ socket
37
+ end
38
+
39
+ def self.send_command(command, message_arguments = {}, &block)
40
+ new.send_command(command, message_arguments, &block)
41
+ end
42
+
43
+ private
44
+
45
+ def get_message_object(command, message_arguments)
46
+ Invoker::IPC::Message.const_get(Invoker::IPC.camelize(command)).new(message_arguments)
47
+ end
48
+
49
+ def open_client_socket(abort_if_not_running = true)
50
+ Socket.unix(Invoker::IPC::Server::SOCKET_PATH) { |socket| yield socket }
51
+ rescue
52
+ abort_if_not_running && abort("Invoker does not seem to be running".color(:red))
53
+ end
54
+
55
+ def send_json_message(socket, message_object)
56
+ socket.write(message_object.encoded_message)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,13 @@
1
+ module Invoker
2
+ class Logger
3
+ def self.puts(message)
4
+ return if ENV["INVOKER_TESTS"]
5
+ $stdout.puts(message)
6
+ end
7
+
8
+ def self.print(message)
9
+ return if ENV["INVOKER_TESTS"]
10
+ $stdout.print(message)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,184 @@
1
+ require 'iniparse'
2
+
3
+ module Invoker
4
+ module Parsers
5
+ class Config
6
+ PORT_REGEX = /\$PORT/
7
+
8
+ attr_accessor :processes, :power_config
9
+ attr_reader :filename
10
+
11
+ # initialize takes a port form cli and decrements it by 1 and sets the
12
+ # instance variable @port. This port value is used as the environment
13
+ # variable $PORT mentioned inside invoker.ini. When method pick_port gets
14
+ # fired it increments the value of port by 1, subsequently when pick_port
15
+ # again gets fired, for another command, it will again increment port
16
+ # value by 1, that way generating different ports for different commands.
17
+ def initialize(filename, port)
18
+ @filename = filename || autodetect_config_file
19
+ print_message_and_abort if invalid_config_file?
20
+
21
+ @port = port - 1
22
+ @processes = load_config
23
+ if Invoker.can_run_balancer?
24
+ @power_config = Invoker::Power::Config.load_config()
25
+ end
26
+ end
27
+
28
+ def http_port
29
+ power_config && power_config.http_port
30
+ end
31
+
32
+ def dns_port
33
+ power_config && power_config.dns_port
34
+ end
35
+
36
+ def https_port
37
+ power_config && power_config.https_port
38
+ end
39
+
40
+ def tld
41
+ power_config && power_config.tld
42
+ end
43
+
44
+ def autorunnable_processes
45
+ process_to_run = processes.reject(&:disable_autorun)
46
+ process_to_run.sort_by { |process| process.index }
47
+ end
48
+
49
+ def process(label)
50
+ processes.detect { |pconfig| pconfig.label == label }
51
+ end
52
+
53
+ private
54
+
55
+ def autodetect_config_file
56
+ Dir.glob("{invoker.ini,Procfile}").first
57
+ end
58
+
59
+ def invalid_config_file?
60
+ @filename.nil?
61
+ end
62
+
63
+ def load_config
64
+ @filename = to_global_file if is_global?
65
+
66
+ if is_ini?
67
+ process_ini
68
+ elsif is_procfile?
69
+ process_procfile
70
+ else
71
+ print_message_and_abort
72
+ end
73
+ end
74
+
75
+ def process_ini
76
+ ini_content = File.read(@filename)
77
+ document = IniParse.parse(ini_content)
78
+ document.map do |section|
79
+ check_directory(section["directory"])
80
+ process_command_from_section(section)
81
+ end
82
+ end
83
+
84
+ def process_procfile
85
+ procfile = Invoker::Parsers::Procfile.new(@filename)
86
+ procfile.entries.map do |name, command|
87
+ section = { "label" => name, "command" => command }
88
+ process_command_from_section(section)
89
+ end
90
+ end
91
+
92
+ def print_message_and_abort
93
+ Invoker::Logger.puts("\n Invalid config file. Invoker requires an ini or a Procfile.".color(:red))
94
+ abort
95
+ end
96
+
97
+ def process_command_from_section(section)
98
+ if supports_subdomain?(section)
99
+ port = pick_port(section)
100
+ if port
101
+ command = replace_port_in_command(section['command'], port)
102
+ section['port'], section['command'] = port, command
103
+ end
104
+ end
105
+
106
+ make_pconfig(section)
107
+ end
108
+
109
+ def pick_port(section)
110
+ if section['command'] =~ PORT_REGEX
111
+ @port += 1
112
+ elsif section['port']
113
+ section['port']
114
+ else
115
+ nil
116
+ end
117
+ end
118
+
119
+ def make_pconfig(section)
120
+ pconfig = {
121
+ label: section["label"] || section.key,
122
+ dir: expand_directory(section["directory"]),
123
+ cmd: section["command"]
124
+ }
125
+ pconfig['port'] = section['port'] if section['port']
126
+ pconfig['disable_autorun'] = section['disable_autorun'] if section['disable_autorun']
127
+ pconfig['index'] = section['index'].to_i if section['index']
128
+ section_index = pconfig['index'].to_i
129
+ if section_index
130
+ pconfig['index'] = section_index
131
+ else
132
+ pconfig['index'] = 0
133
+ end
134
+
135
+ sleep_duration = section['sleep'].to_i
136
+ if sleep_duration > 0
137
+ pconfig['sleep_duration'] = sleep_duration
138
+ else
139
+ pconfig['sleep_duration'] = 1
140
+ end
141
+
142
+ OpenStruct.new(pconfig)
143
+ end
144
+
145
+ def supports_subdomain?(section)
146
+ (section['command'] =~ PORT_REGEX) || section['port']
147
+ end
148
+
149
+ def check_directory(app_dir)
150
+ if app_dir && !app_dir.empty? && !File.directory?(expand_directory(app_dir))
151
+ raise Invoker::Errors::InvalidConfig.new("Invalid directory #{app_dir}")
152
+ end
153
+ end
154
+
155
+ def expand_directory(app_dir)
156
+ File.expand_path(app_dir) if app_dir
157
+ end
158
+
159
+ def replace_port_in_command(command, port)
160
+ if command =~ PORT_REGEX
161
+ command.gsub(PORT_REGEX, port.to_s)
162
+ else
163
+ command
164
+ end
165
+ end
166
+
167
+ def is_ini?
168
+ File.extname(@filename) == '.ini'
169
+ end
170
+
171
+ def is_procfile?
172
+ @filename =~ /Procfile/
173
+ end
174
+
175
+ def to_global_file
176
+ File.join(Invoker::Power::Config.config_dir, "#{@filename}.ini")
177
+ end
178
+
179
+ def is_global?
180
+ @filename =~ /^\w+$/ && File.exist?(to_global_file)
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,86 @@
1
+ module Invoker
2
+ module Parsers
3
+ # rip off from foreman
4
+ class Procfile
5
+ # Initialize a Procfile
6
+ #
7
+ # @param [String] filename (nil) An optional filename to read from
8
+ #
9
+ def initialize(filename=nil)
10
+ @entries = []
11
+ load(filename) if filename
12
+ end
13
+
14
+ # Yield each +Procfile+ entry in order
15
+ #
16
+ def entries
17
+ return @entries unless block_given?
18
+ @entries.each do |(name, command)|
19
+ yield name, command
20
+ end
21
+ end
22
+
23
+ # Retrieve a +Procfile+ command by name
24
+ #
25
+ # @param [String] name The name of the Procfile entry to retrieve
26
+ #
27
+ def [](name)
28
+ @entries.detect { |n,c| name == n }.last
29
+ end
30
+
31
+ # Create a +Procfile+ entry
32
+ #
33
+ # @param [String] name The name of the +Procfile+ entry to create
34
+ # @param [String] command The command of the +Procfile+ entry to create
35
+ #
36
+ def []=(name, command)
37
+ delete name
38
+ @entries << [name, command]
39
+ end
40
+
41
+ # Remove a +Procfile+ entry
42
+ #
43
+ # @param [String] name The name of the +Procfile+ entry to remove
44
+ #
45
+ def delete(name)
46
+ @entries.reject! { |n,c| name == n }
47
+ end
48
+
49
+ # Load a Procfile from a file
50
+ #
51
+ # @param [String] filename The filename of the +Procfile+ to load
52
+ #
53
+ def load(filename)
54
+ @entries.replace parse(filename)
55
+ end
56
+
57
+ # Save a Procfile to a file
58
+ #
59
+ # @param [String] filename Save the +Procfile+ to this file
60
+ #
61
+ def save(filename)
62
+ File.open(filename, 'w') do |file|
63
+ file.puts self.to_s
64
+ end
65
+ end
66
+
67
+ # Get the +Procfile+ as a +String+
68
+ #
69
+ def to_s
70
+ @entries.map do |name, command|
71
+ [ name, command ].join(": ")
72
+ end.join("\n")
73
+ end
74
+
75
+ private
76
+
77
+ def parse(filename)
78
+ File.read(filename).gsub("\r\n","\n").split("\n").map do |line|
79
+ if line =~ /^([A-Za-z0-9_-]+):\s*(.+)$/
80
+ [$1, $2]
81
+ end
82
+ end.compact
83
+ end
84
+ end
85
+ end
86
+ end