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