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,23 @@
1
+ module Invoker
2
+ class DNSCache
3
+ attr_accessor :dns_data
4
+
5
+ def initialize(config)
6
+ self.dns_data = {}
7
+ @dns_mutex = Mutex.new
8
+ Invoker.config.processes.each do |process|
9
+ if process.port
10
+ dns_data[process.label] = { 'port' => process.port }
11
+ end
12
+ end
13
+ end
14
+
15
+ def [](process_name)
16
+ @dns_mutex.synchronize { dns_data[process_name] }
17
+ end
18
+
19
+ def add(name, port, ip = nil)
20
+ @dns_mutex.synchronize { dns_data[name] = { 'port' => port, 'ip' => ip } }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ module Invoker
2
+ module Errors
3
+ class ToomanyOpenConnections < StandardError; end
4
+ class ProcessTerminated < StandardError
5
+ attr_accessor :message, :ready_fd
6
+ def initialize(ready_fd, message)
7
+ @ready_fd = ready_fd
8
+ @message = message
9
+ end
10
+ end
11
+
12
+ class NoValidPortFound < StandardError; end
13
+ class InvalidConfig < StandardError; end
14
+ class InvalidFile < StandardError; end
15
+ class ClientDisconnected < StandardError; end
16
+ end
17
+ end
@@ -0,0 +1,79 @@
1
+ module Invoker
2
+ module Event
3
+ class Manager
4
+ attr_accessor :scheduled_events, :triggered_events
5
+
6
+ def initialize
7
+ @scheduled_events = Hash.new {|h,k| h[k] = [] }
8
+ @triggered_events = []
9
+ @trigger_mutex = Mutex.new()
10
+ end
11
+
12
+ # Trigger an event. The event is not triggered immediately, but is just scheduled to be
13
+ # triggered.
14
+ #
15
+ # @param command_label [String] Command for which event should be triggered
16
+ # @param event_name [Symbol, nil] The optional event name
17
+ def trigger(command_label, event_name = nil)
18
+ @trigger_mutex.synchronize do
19
+ triggered_events << OpenStruct.new(
20
+ :command_label => command_label,
21
+ :event_name => event_name)
22
+ end
23
+ end
24
+
25
+ # Schedule an Event. The event will only trigger when a scheduled event matches
26
+ # a triggered event.
27
+ #
28
+ # @param command_label [String] Command for which the event should be triggered
29
+ # @param event_name [String, nil] Optional event name
30
+ # @param block The block to execute when event actually triggers
31
+ def schedule_event(command_label, event_name = nil, &block)
32
+ @trigger_mutex.synchronize do
33
+ scheduled_events[command_label] << OpenStruct.new(:event_name => event_name, :block => block)
34
+ end
35
+ end
36
+
37
+ # On next iteration of event loop, this method is called and we try to match
38
+ # scheduled events with events that were triggered.
39
+ def run_scheduled_events
40
+ filtered_events_by_name_and_command = []
41
+
42
+ triggered_events.each_with_index do |triggered_event, index|
43
+ matched_events = scheduled_events[triggered_event.command_label]
44
+ if matched_events && !matched_events.empty?
45
+ filtered_events_by_name_and_command, unmatched_events =
46
+ filter_matched_events(matched_events, triggered_event)
47
+ triggered_events[index] = nil
48
+ remove_scheduled_event(unmatched_events, triggered_event.command_label)
49
+ end
50
+ end
51
+ triggered_events.compact!
52
+
53
+ filtered_events_by_name_and_command.each {|event| yield event }
54
+ end
55
+
56
+ private
57
+ def filter_matched_events(matched_events, event)
58
+ matched_filtered_events = []
59
+
60
+ matched_events.each_with_index do |matched_event, index|
61
+ if !event.event_name || event.event_name == matched_event.event_name
62
+ matched_filtered_events << matched_event
63
+ matched_events[index] = nil
64
+ end
65
+ end
66
+ [matched_filtered_events, matched_events.compact]
67
+ end
68
+
69
+ def remove_scheduled_event(matched_events, command_label)
70
+ if !matched_events || matched_events.empty?
71
+ scheduled_events.delete(command_label)
72
+ else
73
+ scheduled_events[command_label] = matched_events
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,45 @@
1
+ require "invoker/ipc/base_command"
2
+ require 'invoker/ipc/message'
3
+ require 'invoker/ipc/add_command'
4
+ require 'invoker/ipc/add_http_command'
5
+ require 'invoker/ipc/client_handler'
6
+ require 'invoker/ipc/dns_check_command'
7
+ require 'invoker/ipc/list_command'
8
+ require 'invoker/ipc/remove_command'
9
+ require 'invoker/ipc/server'
10
+ require "invoker/ipc/reload_command"
11
+ require 'invoker/ipc/tail_command'
12
+ require 'invoker/ipc/unix_client'
13
+ require "invoker/ipc/ping_command"
14
+
15
+ module Invoker
16
+ module IPC
17
+ INITIAL_PACKET_SIZE = 9
18
+ def self.message_from_io(io)
19
+ json_size = io.read(INITIAL_PACKET_SIZE)
20
+ json_string = io.read(json_size.to_i)
21
+ ruby_object_hash = JSON.parse(json_string)
22
+ command_name = camelize(ruby_object_hash['type'])
23
+ command_klass = Invoker::IPC::Message.const_get(command_name)
24
+ command_klass.new(ruby_object_hash)
25
+ end
26
+
27
+ # Taken from Rails without inflection support
28
+ def self.camelize(term)
29
+ string = term.to_s
30
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
31
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
32
+ string.gsub!('/', '::')
33
+ string
34
+ end
35
+
36
+ def self.underscore(term)
37
+ word = term.to_s.gsub('::', '/')
38
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
39
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
40
+ word.tr!("-", "_")
41
+ word.downcase!
42
+ word
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,12 @@
1
+ module Invoker
2
+ module IPC
3
+ class AddCommand < BaseCommand
4
+ def run_command(message_object)
5
+ Invoker.commander.on_next_tick(message_object.process_name) do |process_name|
6
+ start_process_by_name(process_name)
7
+ end
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Invoker
2
+ module IPC
3
+ class AddHttpCommand < BaseCommand
4
+ def run_command(message_object)
5
+ Invoker.dns_cache.add(message_object.process_name, message_object.port, message_object.ip)
6
+ true
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ module Invoker
2
+ module IPC
3
+ class BaseCommand
4
+ attr_accessor :client_socket
5
+ def initialize(client_socket)
6
+ @client_socket = client_socket
7
+ end
8
+
9
+ def send_data(message_object)
10
+ client_socket.write(message_object.encoded_message)
11
+ end
12
+
13
+ # Invoke the command that actual processes incoming message
14
+ # returning true from this message means, command has been processed
15
+ # and client socket can be closed. returning false means, it is a
16
+ # long running command and socket should not be closed immediately
17
+ # @param [Invoker::IPC::Message] incoming message
18
+ # @return [Boolean] true or false
19
+ def run_command(message_object)
20
+ raise "Not implemented"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ module Invoker
2
+ module IPC
3
+ class ClientHandler
4
+ attr_accessor :client_socket
5
+ def initialize(client_socket)
6
+ @client_socket = client_socket
7
+ end
8
+
9
+ def read_and_execute
10
+ client_handler, message_object = read_incoming_command
11
+ client_socket.close if client_handler.run_command(message_object)
12
+ rescue StandardError => error
13
+ Invoker::Logger.puts error.message
14
+ Invoker::Logger.puts error.backtrace
15
+ client_socket.close
16
+ end
17
+
18
+ private
19
+
20
+ def read_incoming_command
21
+ message_object = Invoker::IPC.message_from_io(client_socket)
22
+ [message_object.command_handler_klass.new(client_socket), message_object]
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ module Invoker
2
+ module IPC
3
+ class DnsCheckCommand < BaseCommand
4
+ def run_command(message_object)
5
+ process_detail = Invoker.dns_cache[message_object.process_name]
6
+
7
+ dns_check_response = Invoker::IPC::Message::DnsCheckResponse.new(
8
+ process_name: message_object.process_name,
9
+ port: process_detail ? process_detail['port'] : nil,
10
+ ip: process_detail && process_detail['ip'] ? process_detail['ip'] : '0.0.0.0'
11
+ )
12
+ send_data(dns_check_response)
13
+ true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module Invoker
2
+ module IPC
3
+ class ListCommand < BaseCommand
4
+ def run_command(message_object)
5
+ list_response = Invoker.commander.process_list
6
+ send_data(list_response)
7
+ true
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,170 @@
1
+ module Invoker
2
+ module IPC
3
+ module Message
4
+ module Serialization
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ def as_json
10
+ attributes.merge(type: message_type)
11
+ end
12
+
13
+ def to_json
14
+ JSON.generate(as_json)
15
+ end
16
+
17
+ def message_attributes
18
+ self.class.message_attributes
19
+ end
20
+
21
+ def encoded_message
22
+ json_data = to_json
23
+ json_size = json_data.length.to_s
24
+ length_str = json_size.rjust(Invoker::IPC::INITIAL_PACKET_SIZE, '0')
25
+ length_str + json_data
26
+ end
27
+
28
+ def eql?(other)
29
+ other.class == self.class &&
30
+ compare_attributes(other)
31
+ end
32
+
33
+ def attributes
34
+ message_attribute_keys = message_attributes || []
35
+ message_attribute_keys.reduce({}) do |mem, obj|
36
+ value = send(obj)
37
+ if value.is_a?(Array)
38
+ mem[obj] = serialize_array(value)
39
+ elsif value.is_a?(Hash)
40
+ mem[obj] = serialize_hash(value)
41
+ else
42
+ mem[obj] = value.respond_to?(:as_json) ? value.as_json : encode_as_utf(value)
43
+ end
44
+ mem
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def compare_attributes(other)
51
+ message_attributes.all? do |attribute_name|
52
+ send(attribute_name).eql?(other.send(attribute_name))
53
+ end
54
+ end
55
+
56
+ def encode_as_utf(value)
57
+ return value unless value.is_a?(String)
58
+ value.encode("utf-8", invalid: :replace, undef: :replace, replace: '_')
59
+ end
60
+
61
+ def serialize_array(attribute_array)
62
+ attribute_array.map do |x|
63
+ x.respond_to?(:as_json) ? x.as_json : encode_as_utf(x)
64
+ end
65
+ end
66
+
67
+ def serialize_hash(attribute_hash)
68
+ attribute_hash.inject({}) do |temp_mem, (temp_key, temp_value)|
69
+ if temp_value.respond_to?(:as_json)
70
+ temp_mem[temp_key] = temp_value.as_json
71
+ else
72
+ temp_mem[temp_key] = encode_as_utf(temp_value)
73
+ end
74
+ end
75
+ end
76
+
77
+ module ClassMethods
78
+ def message_attributes(*incoming_attributes)
79
+ if incoming_attributes.empty? && defined?(@message_attributes)
80
+ @message_attributes
81
+ else
82
+ @message_attributes ||= []
83
+ new_attributes = incoming_attributes.flatten
84
+ @message_attributes += new_attributes
85
+ attr_accessor *new_attributes
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ class Base
92
+ def initialize(options)
93
+ options.each do |key, value|
94
+ if self.respond_to?("#{key}=")
95
+ send("#{key}=", value)
96
+ end
97
+ end
98
+ end
99
+
100
+ def message_type
101
+ Invoker::IPC.underscore(self.class.name).split("/").last
102
+ end
103
+
104
+ def command_handler_klass
105
+ Invoker::IPC.const_get("#{IPC.camelize(message_type)}Command")
106
+ end
107
+ end
108
+
109
+ class Add < Base
110
+ include Serialization
111
+ message_attributes :process_name
112
+ end
113
+
114
+ class Tail < Base
115
+ include Serialization
116
+ message_attributes :process_names
117
+ end
118
+
119
+ class AddHttp < Base
120
+ include Serialization
121
+ message_attributes :process_name, :port, :ip
122
+ end
123
+
124
+ class Reload < Base
125
+ include Serialization
126
+ message_attributes :process_name, :signal
127
+
128
+ def remove_message
129
+ Remove.new(process_name: process_name, signal: signal)
130
+ end
131
+ end
132
+
133
+ class List < Base
134
+ include Serialization
135
+ end
136
+
137
+ class Process < Base
138
+ include Serialization
139
+ message_attributes :process_name, :shell_command, :dir, :pid, :port
140
+ end
141
+
142
+ class Remove < Base
143
+ include Serialization
144
+ message_attributes :process_name, :signal
145
+ end
146
+
147
+ class DnsCheck < Base
148
+ include Serialization
149
+ message_attributes :process_name
150
+ end
151
+
152
+ class DnsCheckResponse < Base
153
+ include Serialization
154
+ message_attributes :process_name, :port, :ip
155
+ end
156
+
157
+ class Ping < Base
158
+ include Serialization
159
+ end
160
+
161
+ class Pong < Base
162
+ include Serialization
163
+ message_attributes :status
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ require "invoker/ipc/message/list_response"
170
+ require "invoker/ipc/message/tail_response"